]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6d4339f6 DL |
2 | /* |
3 | * Copyright (C) 2012 Samsung Electronics | |
4 | * | |
5 | * Author: InKi Dae <inki.dae@samsung.com> | |
6 | * Author: Donghwa Lee <dh09.lee@samsung.com> | |
6d4339f6 DL |
7 | */ |
8 | ||
9 | #include <config.h> | |
d678a59d | 10 | #include <common.h> |
bb5930d5 | 11 | #include <display.h> |
0c84358c | 12 | #include <div64.h> |
bb5930d5 | 13 | #include <dm.h> |
c23f3157 | 14 | #include <fdtdec.h> |
f7ae49fc | 15 | #include <log.h> |
401d1c4f | 16 | #include <asm/global_data.h> |
b08c8c48 | 17 | #include <linux/libfdt.h> |
bb5930d5 SG |
18 | #include <panel.h> |
19 | #include <video.h> | |
20 | #include <video_bridge.h> | |
6d4339f6 DL |
21 | #include <asm/io.h> |
22 | #include <asm/arch/cpu.h> | |
23 | #include <asm/arch/clock.h> | |
24 | #include <asm/arch/clk.h> | |
25 | #include <asm/arch/mipi_dsim.h> | |
a29c8322 | 26 | #include <asm/arch/dp_info.h> |
bb5930d5 SG |
27 | #include <asm/arch/fb.h> |
28 | #include <asm/arch/pinmux.h> | |
6d4339f6 | 29 | #include <asm/arch/system.h> |
9018efa7 | 30 | #include <asm/gpio.h> |
5d97dff0 | 31 | #include <linux/errno.h> |
6d4339f6 | 32 | |
c23f3157 AK |
33 | DECLARE_GLOBAL_DATA_PTR; |
34 | ||
bb5930d5 SG |
35 | enum { |
36 | FIMD_RGB_INTERFACE = 1, | |
37 | FIMD_CPU_INTERFACE = 2, | |
38 | }; | |
39 | ||
40 | enum exynos_fb_rgb_mode_t { | |
41 | MODE_RGB_P = 0, | |
42 | MODE_BGR_P = 1, | |
43 | MODE_RGB_S = 2, | |
44 | MODE_BGR_S = 3, | |
c23f3157 | 45 | }; |
c23f3157 | 46 | |
bb5930d5 SG |
47 | struct exynos_fb_priv { |
48 | ushort vl_col; /* Number of columns (i.e. 640) */ | |
49 | ushort vl_row; /* Number of rows (i.e. 480) */ | |
50 | ushort vl_rot; /* Rotation of Display (0, 1, 2, 3) */ | |
51 | ushort vl_width; /* Width of display area in millimeters */ | |
52 | ushort vl_height; /* Height of display area in millimeters */ | |
53 | ||
54 | /* LCD configuration register */ | |
55 | u_char vl_freq; /* Frequency */ | |
56 | u_char vl_clkp; /* Clock polarity */ | |
57 | u_char vl_oep; /* Output Enable polarity */ | |
58 | u_char vl_hsp; /* Horizontal Sync polarity */ | |
59 | u_char vl_vsp; /* Vertical Sync polarity */ | |
60 | u_char vl_dp; /* Data polarity */ | |
61 | u_char vl_bpix; /* Bits per pixel */ | |
62 | ||
63 | /* Horizontal control register. Timing from data sheet */ | |
64 | u_char vl_hspw; /* Horz sync pulse width */ | |
65 | u_char vl_hfpd; /* Wait before of line */ | |
66 | u_char vl_hbpd; /* Wait end of line */ | |
67 | ||
68 | /* Vertical control register. */ | |
69 | u_char vl_vspw; /* Vertical sync pulse width */ | |
70 | u_char vl_vfpd; /* Wait before of frame */ | |
71 | u_char vl_vbpd; /* Wait end of frame */ | |
72 | u_char vl_cmd_allow_len; /* Wait end of frame */ | |
73 | ||
74 | unsigned int win_id; | |
75 | unsigned int init_delay; | |
76 | unsigned int power_on_delay; | |
77 | unsigned int reset_delay; | |
78 | unsigned int interface_mode; | |
79 | unsigned int mipi_enabled; | |
80 | unsigned int dp_enabled; | |
81 | unsigned int cs_setup; | |
82 | unsigned int wr_setup; | |
83 | unsigned int wr_act; | |
84 | unsigned int wr_hold; | |
85 | unsigned int logo_on; | |
86 | unsigned int logo_width; | |
87 | unsigned int logo_height; | |
88 | int logo_x_offset; | |
89 | int logo_y_offset; | |
90 | unsigned long logo_addr; | |
91 | unsigned int rgb_mode; | |
92 | unsigned int resolution; | |
93 | ||
94 | /* parent clock name(MPLL, EPLL or VPLL) */ | |
95 | unsigned int pclk_name; | |
96 | /* ratio value for source clock from parent clock. */ | |
97 | unsigned int sclk_div; | |
98 | ||
99 | unsigned int dual_lcd_enabled; | |
100 | struct exynos_fb *reg; | |
101 | struct exynos_platform_mipi_dsim *dsim_platform_data_dt; | |
102 | }; | |
103 | ||
104 | static void exynos_fimd_set_dualrgb(struct exynos_fb_priv *priv, bool enabled) | |
0c84358c | 105 | { |
8b449a66 | 106 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
107 | unsigned int cfg = 0; |
108 | ||
109 | if (enabled) { | |
110 | cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT | | |
111 | EXYNOS_DUALRGB_VDEN_EN_ENABLE; | |
112 | ||
113 | /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */ | |
8b449a66 | 114 | cfg |= EXYNOS_DUALRGB_SUB_CNT(priv->vl_col / 2) | |
0c84358c SG |
115 | EXYNOS_DUALRGB_MAIN_CNT(0); |
116 | } | |
117 | ||
8b449a66 | 118 | writel(cfg, ®->dualrgb); |
0c84358c SG |
119 | } |
120 | ||
bb5930d5 | 121 | static void exynos_fimd_set_dp_clkcon(struct exynos_fb_priv *priv, |
0c84358c SG |
122 | unsigned int enabled) |
123 | { | |
8b449a66 | 124 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
125 | unsigned int cfg = 0; |
126 | ||
127 | if (enabled) | |
128 | cfg = EXYNOS_DP_CLK_ENABLE; | |
129 | ||
8b449a66 | 130 | writel(cfg, ®->dp_mie_clkcon); |
0c84358c SG |
131 | } |
132 | ||
bb5930d5 SG |
133 | static void exynos_fimd_set_par(struct exynos_fb_priv *priv, |
134 | unsigned int win_id) | |
0c84358c | 135 | { |
8b449a66 | 136 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
137 | unsigned int cfg = 0; |
138 | ||
139 | /* set window control */ | |
8b449a66 | 140 | cfg = readl((unsigned int)®->wincon0 + |
0c84358c SG |
141 | EXYNOS_WINCON(win_id)); |
142 | ||
143 | cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE | | |
144 | EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE | | |
145 | EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK | | |
146 | EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK); | |
147 | ||
148 | /* DATAPATH is DMA */ | |
149 | cfg |= EXYNOS_WINCON_DATAPATH_DMA; | |
150 | ||
151 | cfg |= EXYNOS_WINCON_HAWSWP_ENABLE; | |
152 | ||
153 | /* dma burst is 16 */ | |
154 | cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; | |
155 | ||
8b449a66 | 156 | switch (priv->vl_bpix) { |
0c84358c SG |
157 | case 4: |
158 | cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565; | |
159 | break; | |
160 | default: | |
161 | cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888; | |
162 | break; | |
163 | } | |
164 | ||
8b449a66 | 165 | writel(cfg, (unsigned int)®->wincon0 + |
0c84358c SG |
166 | EXYNOS_WINCON(win_id)); |
167 | ||
168 | /* set window position to x=0, y=0*/ | |
169 | cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0); | |
8b449a66 | 170 | writel(cfg, (unsigned int)®->vidosd0a + |
0c84358c SG |
171 | EXYNOS_VIDOSD(win_id)); |
172 | ||
8b449a66 SG |
173 | cfg = EXYNOS_VIDOSD_RIGHT_X(priv->vl_col - 1) | |
174 | EXYNOS_VIDOSD_BOTTOM_Y(priv->vl_row - 1) | | |
0c84358c SG |
175 | EXYNOS_VIDOSD_RIGHT_X_E(1) | |
176 | EXYNOS_VIDOSD_BOTTOM_Y_E(0); | |
177 | ||
8b449a66 | 178 | writel(cfg, (unsigned int)®->vidosd0b + |
0c84358c SG |
179 | EXYNOS_VIDOSD(win_id)); |
180 | ||
181 | /* set window size for window0*/ | |
8b449a66 SG |
182 | cfg = EXYNOS_VIDOSD_SIZE(priv->vl_col * priv->vl_row); |
183 | writel(cfg, (unsigned int)®->vidosd0c + | |
0c84358c SG |
184 | EXYNOS_VIDOSD(win_id)); |
185 | } | |
186 | ||
bb5930d5 | 187 | static void exynos_fimd_set_buffer_address(struct exynos_fb_priv *priv, |
0c84358c SG |
188 | unsigned int win_id, |
189 | ulong lcd_base_addr) | |
190 | { | |
8b449a66 | 191 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
192 | unsigned long start_addr, end_addr; |
193 | ||
194 | start_addr = lcd_base_addr; | |
bb5930d5 | 195 | end_addr = start_addr + ((priv->vl_col * (VNBITS(priv->vl_bpix) / 8)) * |
8b449a66 | 196 | priv->vl_row); |
0c84358c | 197 | |
8b449a66 | 198 | writel(start_addr, (unsigned int)®->vidw00add0b0 + |
0c84358c | 199 | EXYNOS_BUFFER_OFFSET(win_id)); |
8b449a66 | 200 | writel(end_addr, (unsigned int)®->vidw00add1b0 + |
0c84358c SG |
201 | EXYNOS_BUFFER_OFFSET(win_id)); |
202 | } | |
203 | ||
bb5930d5 | 204 | static void exynos_fimd_set_clock(struct exynos_fb_priv *priv) |
0c84358c | 205 | { |
8b449a66 | 206 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
207 | unsigned int cfg = 0, div = 0, remainder, remainder_div; |
208 | unsigned long pixel_clock; | |
209 | unsigned long long src_clock; | |
210 | ||
8b449a66 SG |
211 | if (priv->dual_lcd_enabled) { |
212 | pixel_clock = priv->vl_freq * | |
213 | (priv->vl_hspw + priv->vl_hfpd + | |
214 | priv->vl_hbpd + priv->vl_col / 2) * | |
215 | (priv->vl_vspw + priv->vl_vfpd + | |
216 | priv->vl_vbpd + priv->vl_row); | |
217 | } else if (priv->interface_mode == FIMD_CPU_INTERFACE) { | |
218 | pixel_clock = priv->vl_freq * | |
219 | priv->vl_width * priv->vl_height * | |
220 | (priv->cs_setup + priv->wr_setup + | |
221 | priv->wr_act + priv->wr_hold + 1); | |
0c84358c | 222 | } else { |
8b449a66 SG |
223 | pixel_clock = priv->vl_freq * |
224 | (priv->vl_hspw + priv->vl_hfpd + | |
225 | priv->vl_hbpd + priv->vl_col) * | |
226 | (priv->vl_vspw + priv->vl_vfpd + | |
227 | priv->vl_vbpd + priv->vl_row); | |
0c84358c SG |
228 | } |
229 | ||
8b449a66 | 230 | cfg = readl(®->vidcon0); |
0c84358c SG |
231 | cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK | |
232 | EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK | | |
233 | EXYNOS_VIDCON0_CLKDIR_MASK); | |
234 | cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS | | |
235 | EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); | |
236 | ||
237 | src_clock = (unsigned long long) get_lcd_clk(); | |
238 | ||
239 | /* get quotient and remainder. */ | |
240 | remainder = do_div(src_clock, pixel_clock); | |
241 | div = src_clock; | |
242 | ||
243 | remainder *= 10; | |
244 | remainder_div = remainder / pixel_clock; | |
245 | ||
246 | /* round about one places of decimals. */ | |
247 | if (remainder_div >= 5) | |
248 | div++; | |
249 | ||
250 | /* in case of dual lcd mode. */ | |
8b449a66 | 251 | if (priv->dual_lcd_enabled) |
0c84358c SG |
252 | div--; |
253 | ||
254 | cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); | |
8b449a66 | 255 | writel(cfg, ®->vidcon0); |
0c84358c SG |
256 | } |
257 | ||
bb5930d5 | 258 | void exynos_set_trigger(struct exynos_fb_priv *priv) |
0c84358c | 259 | { |
8b449a66 | 260 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
261 | unsigned int cfg = 0; |
262 | ||
8b449a66 | 263 | cfg = readl(®->trigcon); |
0c84358c SG |
264 | |
265 | cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); | |
266 | ||
8b449a66 | 267 | writel(cfg, ®->trigcon); |
0c84358c SG |
268 | } |
269 | ||
bb5930d5 | 270 | int exynos_is_i80_frame_done(struct exynos_fb_priv *priv) |
0c84358c | 271 | { |
8b449a66 | 272 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
273 | unsigned int cfg = 0; |
274 | int status; | |
275 | ||
8b449a66 | 276 | cfg = readl(®->trigcon); |
0c84358c SG |
277 | |
278 | /* frame done func is valid only when TRIMODE[0] is set to 1. */ | |
279 | status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) == | |
280 | EXYNOS_I80STATUS_TRIG_DONE; | |
281 | ||
282 | return status; | |
283 | } | |
284 | ||
bb5930d5 | 285 | static void exynos_fimd_lcd_on(struct exynos_fb_priv *priv) |
0c84358c | 286 | { |
8b449a66 | 287 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
288 | unsigned int cfg = 0; |
289 | ||
290 | /* display on */ | |
8b449a66 | 291 | cfg = readl(®->vidcon0); |
0c84358c | 292 | cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); |
8b449a66 | 293 | writel(cfg, ®->vidcon0); |
0c84358c SG |
294 | } |
295 | ||
bb5930d5 SG |
296 | static void exynos_fimd_window_on(struct exynos_fb_priv *priv, |
297 | unsigned int win_id) | |
0c84358c | 298 | { |
8b449a66 | 299 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
300 | unsigned int cfg = 0; |
301 | ||
302 | /* enable window */ | |
8b449a66 | 303 | cfg = readl((unsigned int)®->wincon0 + |
0c84358c SG |
304 | EXYNOS_WINCON(win_id)); |
305 | cfg |= EXYNOS_WINCON_ENWIN_ENABLE; | |
8b449a66 | 306 | writel(cfg, (unsigned int)®->wincon0 + |
0c84358c SG |
307 | EXYNOS_WINCON(win_id)); |
308 | ||
8b449a66 | 309 | cfg = readl(®->winshmap); |
0c84358c | 310 | cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); |
8b449a66 | 311 | writel(cfg, ®->winshmap); |
0c84358c SG |
312 | } |
313 | ||
bb5930d5 | 314 | void exynos_fimd_lcd_off(struct exynos_fb_priv *priv) |
0c84358c | 315 | { |
8b449a66 | 316 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
317 | unsigned int cfg = 0; |
318 | ||
8b449a66 | 319 | cfg = readl(®->vidcon0); |
0c84358c | 320 | cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); |
8b449a66 | 321 | writel(cfg, ®->vidcon0); |
0c84358c SG |
322 | } |
323 | ||
bb5930d5 | 324 | void exynos_fimd_window_off(struct exynos_fb_priv *priv, unsigned int win_id) |
0c84358c | 325 | { |
8b449a66 | 326 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
327 | unsigned int cfg = 0; |
328 | ||
8b449a66 | 329 | cfg = readl((unsigned int)®->wincon0 + |
0c84358c SG |
330 | EXYNOS_WINCON(win_id)); |
331 | cfg &= EXYNOS_WINCON_ENWIN_DISABLE; | |
8b449a66 | 332 | writel(cfg, (unsigned int)®->wincon0 + |
0c84358c SG |
333 | EXYNOS_WINCON(win_id)); |
334 | ||
8b449a66 | 335 | cfg = readl(®->winshmap); |
0c84358c | 336 | cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); |
8b449a66 | 337 | writel(cfg, ®->winshmap); |
0c84358c SG |
338 | } |
339 | ||
340 | /* | |
341 | * The reset value for FIMD SYSMMU register MMU_CTRL is 3 | |
342 | * on Exynos5420 and newer versions. | |
343 | * This means FIMD SYSMMU is on by default on Exynos5420 | |
344 | * and newer versions. | |
345 | * Since in u-boot we don't use SYSMMU, we should disable | |
346 | * those FIMD SYSMMU. | |
347 | * Note that there are 2 SYSMMU for FIMD: m0 and m1. | |
348 | * m0 handles windows 0 and 4, and m1 handles windows 1, 2 and 3. | |
349 | * We disable both of them here. | |
350 | */ | |
351 | void exynos_fimd_disable_sysmmu(void) | |
352 | { | |
353 | u32 *sysmmufimd; | |
354 | unsigned int node; | |
355 | int node_list[2]; | |
356 | int count; | |
357 | int i; | |
358 | ||
359 | count = fdtdec_find_aliases_for_id(gd->fdt_blob, "fimd", | |
360 | COMPAT_SAMSUNG_EXYNOS_SYSMMU, node_list, 2); | |
361 | for (i = 0; i < count; i++) { | |
362 | node = node_list[i]; | |
363 | if (node <= 0) { | |
364 | debug("Can't get device node for fimd sysmmu\n"); | |
365 | return; | |
366 | } | |
367 | ||
368 | sysmmufimd = (u32 *)fdtdec_get_addr(gd->fdt_blob, node, "reg"); | |
369 | if (!sysmmufimd) { | |
370 | debug("Can't get base address for sysmmu fimdm0"); | |
371 | return; | |
372 | } | |
373 | ||
374 | writel(0x0, sysmmufimd); | |
375 | } | |
376 | } | |
377 | ||
bb5930d5 | 378 | void exynos_fimd_lcd_init(struct udevice *dev) |
0c84358c | 379 | { |
bb5930d5 | 380 | struct exynos_fb_priv *priv = dev_get_priv(dev); |
8a8d24bd | 381 | struct video_uc_plat *plat = dev_get_uclass_plat(dev); |
bb5930d5 | 382 | struct exynos_fb *reg = priv->reg; |
0c84358c SG |
383 | unsigned int cfg = 0, rgb_mode; |
384 | unsigned int offset; | |
385 | unsigned int node; | |
386 | ||
e160f7d4 | 387 | node = dev_of_offset(dev); |
0c84358c SG |
388 | if (fdtdec_get_bool(gd->fdt_blob, node, "samsung,disable-sysmmu")) |
389 | exynos_fimd_disable_sysmmu(); | |
390 | ||
391 | offset = exynos_fimd_get_base_offset(); | |
392 | ||
8b449a66 | 393 | rgb_mode = priv->rgb_mode; |
0c84358c | 394 | |
8b449a66 | 395 | if (priv->interface_mode == FIMD_RGB_INTERFACE) { |
0c84358c | 396 | cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; |
8b449a66 | 397 | writel(cfg, ®->vidcon0); |
0c84358c | 398 | |
8b449a66 | 399 | cfg = readl(®->vidcon2); |
0c84358c SG |
400 | cfg &= ~(EXYNOS_VIDCON2_WB_MASK | |
401 | EXYNOS_VIDCON2_TVFORMATSEL_MASK | | |
402 | EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); | |
403 | cfg |= EXYNOS_VIDCON2_WB_DISABLE; | |
8b449a66 | 404 | writel(cfg, ®->vidcon2); |
0c84358c SG |
405 | |
406 | /* set polarity */ | |
407 | cfg = 0; | |
8b449a66 | 408 | if (!priv->vl_clkp) |
0c84358c | 409 | cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; |
8b449a66 | 410 | if (!priv->vl_hsp) |
0c84358c | 411 | cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; |
8b449a66 | 412 | if (!priv->vl_vsp) |
0c84358c | 413 | cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; |
8b449a66 | 414 | if (!priv->vl_dp) |
0c84358c SG |
415 | cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; |
416 | ||
8b449a66 | 417 | writel(cfg, (unsigned int)®->vidcon1 + offset); |
0c84358c SG |
418 | |
419 | /* set timing */ | |
8b449a66 SG |
420 | cfg = EXYNOS_VIDTCON0_VFPD(priv->vl_vfpd - 1); |
421 | cfg |= EXYNOS_VIDTCON0_VBPD(priv->vl_vbpd - 1); | |
422 | cfg |= EXYNOS_VIDTCON0_VSPW(priv->vl_vspw - 1); | |
423 | writel(cfg, (unsigned int)®->vidtcon0 + offset); | |
0c84358c | 424 | |
8b449a66 SG |
425 | cfg = EXYNOS_VIDTCON1_HFPD(priv->vl_hfpd - 1); |
426 | cfg |= EXYNOS_VIDTCON1_HBPD(priv->vl_hbpd - 1); | |
427 | cfg |= EXYNOS_VIDTCON1_HSPW(priv->vl_hspw - 1); | |
0c84358c | 428 | |
8b449a66 | 429 | writel(cfg, (unsigned int)®->vidtcon1 + offset); |
0c84358c SG |
430 | |
431 | /* set lcd size */ | |
8b449a66 SG |
432 | cfg = EXYNOS_VIDTCON2_HOZVAL(priv->vl_col - 1) | |
433 | EXYNOS_VIDTCON2_LINEVAL(priv->vl_row - 1) | | |
434 | EXYNOS_VIDTCON2_HOZVAL_E(priv->vl_col - 1) | | |
435 | EXYNOS_VIDTCON2_LINEVAL_E(priv->vl_row - 1); | |
0c84358c | 436 | |
8b449a66 | 437 | writel(cfg, (unsigned int)®->vidtcon2 + offset); |
0c84358c SG |
438 | } |
439 | ||
440 | /* set display mode */ | |
8b449a66 | 441 | cfg = readl(®->vidcon0); |
0c84358c SG |
442 | cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; |
443 | cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); | |
8b449a66 | 444 | writel(cfg, ®->vidcon0); |
0c84358c SG |
445 | |
446 | /* set par */ | |
8b449a66 | 447 | exynos_fimd_set_par(priv, priv->win_id); |
0c84358c SG |
448 | |
449 | /* set memory address */ | |
bb5930d5 | 450 | exynos_fimd_set_buffer_address(priv, priv->win_id, plat->base); |
0c84358c SG |
451 | |
452 | /* set buffer size */ | |
8b449a66 | 453 | cfg = EXYNOS_VIDADDR_PAGEWIDTH(priv->vl_col * |
bb5930d5 | 454 | VNBITS(priv->vl_bpix) / 8) | |
8b449a66 | 455 | EXYNOS_VIDADDR_PAGEWIDTH_E(priv->vl_col * |
bb5930d5 | 456 | VNBITS(priv->vl_bpix) / 8) | |
0c84358c SG |
457 | EXYNOS_VIDADDR_OFFSIZE(0) | |
458 | EXYNOS_VIDADDR_OFFSIZE_E(0); | |
459 | ||
8b449a66 SG |
460 | writel(cfg, (unsigned int)®->vidw00add2 + |
461 | EXYNOS_BUFFER_SIZE(priv->win_id)); | |
0c84358c SG |
462 | |
463 | /* set clock */ | |
8b449a66 | 464 | exynos_fimd_set_clock(priv); |
0c84358c SG |
465 | |
466 | /* set rgb mode to dual lcd. */ | |
8b449a66 | 467 | exynos_fimd_set_dualrgb(priv, priv->dual_lcd_enabled); |
0c84358c SG |
468 | |
469 | /* display on */ | |
8b449a66 | 470 | exynos_fimd_lcd_on(priv); |
0c84358c SG |
471 | |
472 | /* window on */ | |
8b449a66 | 473 | exynos_fimd_window_on(priv, priv->win_id); |
0c84358c | 474 | |
8b449a66 | 475 | exynos_fimd_set_dp_clkcon(priv, priv->dp_enabled); |
0c84358c SG |
476 | } |
477 | ||
bb5930d5 | 478 | unsigned long exynos_fimd_calc_fbsize(struct exynos_fb_priv *priv) |
6d4339f6 | 479 | { |
bb5930d5 | 480 | return priv->vl_col * priv->vl_row * (VNBITS(priv->vl_bpix) / 8); |
6d4339f6 DL |
481 | } |
482 | ||
d1998a9f | 483 | int exynos_fb_of_to_plat(struct udevice *dev) |
6d4339f6 | 484 | { |
bb5930d5 | 485 | struct exynos_fb_priv *priv = dev_get_priv(dev); |
e160f7d4 | 486 | unsigned int node = dev_of_offset(dev); |
bb5930d5 SG |
487 | const void *blob = gd->fdt_blob; |
488 | fdt_addr_t addr; | |
6d4339f6 | 489 | |
2548493a | 490 | addr = dev_read_addr(dev); |
bb5930d5 SG |
491 | if (addr == FDT_ADDR_T_NONE) { |
492 | debug("Can't get the FIMD base address\n"); | |
493 | return -EINVAL; | |
c23f3157 | 494 | } |
bb5930d5 | 495 | priv->reg = (struct exynos_fb *)addr; |
c23f3157 | 496 | |
bb5930d5 SG |
497 | priv->vl_col = fdtdec_get_int(blob, node, "samsung,vl-col", 0); |
498 | if (priv->vl_col == 0) { | |
c23f3157 AK |
499 | debug("Can't get XRES\n"); |
500 | return -ENXIO; | |
501 | } | |
502 | ||
bb5930d5 SG |
503 | priv->vl_row = fdtdec_get_int(blob, node, "samsung,vl-row", 0); |
504 | if (priv->vl_row == 0) { | |
c23f3157 AK |
505 | debug("Can't get YRES\n"); |
506 | return -ENXIO; | |
507 | } | |
508 | ||
bb5930d5 | 509 | priv->vl_width = fdtdec_get_int(blob, node, |
c23f3157 AK |
510 | "samsung,vl-width", 0); |
511 | ||
bb5930d5 | 512 | priv->vl_height = fdtdec_get_int(blob, node, |
c23f3157 AK |
513 | "samsung,vl-height", 0); |
514 | ||
bb5930d5 SG |
515 | priv->vl_freq = fdtdec_get_int(blob, node, "samsung,vl-freq", 0); |
516 | if (priv->vl_freq == 0) { | |
c23f3157 AK |
517 | debug("Can't get refresh rate\n"); |
518 | return -ENXIO; | |
519 | } | |
520 | ||
521 | if (fdtdec_get_bool(blob, node, "samsung,vl-clkp")) | |
bb5930d5 | 522 | priv->vl_clkp = VIDEO_ACTIVE_LOW; |
c23f3157 AK |
523 | |
524 | if (fdtdec_get_bool(blob, node, "samsung,vl-oep")) | |
bb5930d5 | 525 | priv->vl_oep = VIDEO_ACTIVE_LOW; |
c23f3157 AK |
526 | |
527 | if (fdtdec_get_bool(blob, node, "samsung,vl-hsp")) | |
bb5930d5 | 528 | priv->vl_hsp = VIDEO_ACTIVE_LOW; |
c23f3157 AK |
529 | |
530 | if (fdtdec_get_bool(blob, node, "samsung,vl-vsp")) | |
bb5930d5 | 531 | priv->vl_vsp = VIDEO_ACTIVE_LOW; |
c23f3157 AK |
532 | |
533 | if (fdtdec_get_bool(blob, node, "samsung,vl-dp")) | |
bb5930d5 | 534 | priv->vl_dp = VIDEO_ACTIVE_LOW; |
c23f3157 | 535 | |
bb5930d5 SG |
536 | priv->vl_bpix = fdtdec_get_int(blob, node, "samsung,vl-bpix", 0); |
537 | if (priv->vl_bpix == 0) { | |
c23f3157 AK |
538 | debug("Can't get bits per pixel\n"); |
539 | return -ENXIO; | |
540 | } | |
541 | ||
bb5930d5 SG |
542 | priv->vl_hspw = fdtdec_get_int(blob, node, "samsung,vl-hspw", 0); |
543 | if (priv->vl_hspw == 0) { | |
c23f3157 AK |
544 | debug("Can't get hsync width\n"); |
545 | return -ENXIO; | |
546 | } | |
547 | ||
bb5930d5 SG |
548 | priv->vl_hfpd = fdtdec_get_int(blob, node, "samsung,vl-hfpd", 0); |
549 | if (priv->vl_hfpd == 0) { | |
c23f3157 AK |
550 | debug("Can't get right margin\n"); |
551 | return -ENXIO; | |
552 | } | |
553 | ||
bb5930d5 | 554 | priv->vl_hbpd = (u_char)fdtdec_get_int(blob, node, |
c23f3157 | 555 | "samsung,vl-hbpd", 0); |
bb5930d5 | 556 | if (priv->vl_hbpd == 0) { |
c23f3157 AK |
557 | debug("Can't get left margin\n"); |
558 | return -ENXIO; | |
559 | } | |
560 | ||
bb5930d5 | 561 | priv->vl_vspw = (u_char)fdtdec_get_int(blob, node, |
c23f3157 | 562 | "samsung,vl-vspw", 0); |
bb5930d5 | 563 | if (priv->vl_vspw == 0) { |
c23f3157 AK |
564 | debug("Can't get vsync width\n"); |
565 | return -ENXIO; | |
566 | } | |
567 | ||
bb5930d5 | 568 | priv->vl_vfpd = fdtdec_get_int(blob, node, |
c23f3157 | 569 | "samsung,vl-vfpd", 0); |
bb5930d5 | 570 | if (priv->vl_vfpd == 0) { |
c23f3157 AK |
571 | debug("Can't get lower margin\n"); |
572 | return -ENXIO; | |
573 | } | |
574 | ||
bb5930d5 SG |
575 | priv->vl_vbpd = fdtdec_get_int(blob, node, "samsung,vl-vbpd", 0); |
576 | if (priv->vl_vbpd == 0) { | |
c23f3157 AK |
577 | debug("Can't get upper margin\n"); |
578 | return -ENXIO; | |
579 | } | |
580 | ||
bb5930d5 | 581 | priv->vl_cmd_allow_len = fdtdec_get_int(blob, node, |
c23f3157 AK |
582 | "samsung,vl-cmd-allow-len", 0); |
583 | ||
bb5930d5 SG |
584 | priv->win_id = fdtdec_get_int(blob, node, "samsung,winid", 0); |
585 | priv->init_delay = fdtdec_get_int(blob, node, | |
c23f3157 | 586 | "samsung,init-delay", 0); |
bb5930d5 | 587 | priv->power_on_delay = fdtdec_get_int(blob, node, |
c23f3157 | 588 | "samsung,power-on-delay", 0); |
bb5930d5 | 589 | priv->reset_delay = fdtdec_get_int(blob, node, |
c23f3157 | 590 | "samsung,reset-delay", 0); |
bb5930d5 | 591 | priv->interface_mode = fdtdec_get_int(blob, node, |
c23f3157 | 592 | "samsung,interface-mode", 0); |
bb5930d5 | 593 | priv->mipi_enabled = fdtdec_get_int(blob, node, |
c23f3157 | 594 | "samsung,mipi-enabled", 0); |
bb5930d5 | 595 | priv->dp_enabled = fdtdec_get_int(blob, node, |
c23f3157 | 596 | "samsung,dp-enabled", 0); |
bb5930d5 | 597 | priv->cs_setup = fdtdec_get_int(blob, node, |
c23f3157 | 598 | "samsung,cs-setup", 0); |
bb5930d5 | 599 | priv->wr_setup = fdtdec_get_int(blob, node, |
c23f3157 | 600 | "samsung,wr-setup", 0); |
bb5930d5 SG |
601 | priv->wr_act = fdtdec_get_int(blob, node, "samsung,wr-act", 0); |
602 | priv->wr_hold = fdtdec_get_int(blob, node, "samsung,wr-hold", 0); | |
c23f3157 | 603 | |
bb5930d5 SG |
604 | priv->logo_on = fdtdec_get_int(blob, node, "samsung,logo-on", 0); |
605 | if (priv->logo_on) { | |
606 | priv->logo_width = fdtdec_get_int(blob, node, | |
c23f3157 | 607 | "samsung,logo-width", 0); |
bb5930d5 | 608 | priv->logo_height = fdtdec_get_int(blob, node, |
c23f3157 | 609 | "samsung,logo-height", 0); |
bb5930d5 | 610 | priv->logo_addr = fdtdec_get_int(blob, node, |
c23f3157 AK |
611 | "samsung,logo-addr", 0); |
612 | } | |
613 | ||
bb5930d5 | 614 | priv->rgb_mode = fdtdec_get_int(blob, node, |
c23f3157 | 615 | "samsung,rgb-mode", 0); |
bb5930d5 | 616 | priv->pclk_name = fdtdec_get_int(blob, node, |
c23f3157 | 617 | "samsung,pclk-name", 0); |
bb5930d5 | 618 | priv->sclk_div = fdtdec_get_int(blob, node, |
c23f3157 | 619 | "samsung,sclk-div", 0); |
bb5930d5 | 620 | priv->dual_lcd_enabled = fdtdec_get_int(blob, node, |
c23f3157 AK |
621 | "samsung,dual-lcd-enabled", 0); |
622 | ||
623 | return 0; | |
624 | } | |
c23f3157 | 625 | |
bb5930d5 | 626 | static int exynos_fb_probe(struct udevice *dev) |
6d4339f6 | 627 | { |
bb5930d5 SG |
628 | struct video_priv *uc_priv = dev_get_uclass_priv(dev); |
629 | struct exynos_fb_priv *priv = dev_get_priv(dev); | |
630 | struct udevice *panel, *bridge; | |
631 | struct udevice *dp; | |
632 | int ret; | |
633 | ||
634 | debug("%s: start\n", __func__); | |
6d4339f6 DL |
635 | set_system_display_ctrl(); |
636 | set_lcd_clk(); | |
637 | ||
1591ee73 PW |
638 | #ifdef CONFIG_EXYNOS_MIPI_DSIM |
639 | exynos_init_dsim_platform_data(&panel_info); | |
640 | #endif | |
bb5930d5 | 641 | exynos_fimd_lcd_init(dev); |
1591ee73 | 642 | |
c726fc01 | 643 | ret = uclass_first_device_err(UCLASS_PANEL, &panel); |
bb5930d5 | 644 | if (ret) { |
c726fc01 | 645 | printf("%s: LCD panel failed to probe %d\n", __func__, ret); |
bb5930d5 SG |
646 | return ret; |
647 | } | |
6d4339f6 | 648 | |
c726fc01 | 649 | ret = uclass_first_device_err(UCLASS_DISPLAY, &dp); |
bb5930d5 SG |
650 | if (ret) { |
651 | debug("%s: Display device error %d\n", __func__, ret); | |
652 | return ret; | |
653 | } | |
bb5930d5 SG |
654 | ret = display_enable(dp, 18, NULL); |
655 | if (ret) { | |
656 | debug("%s: Display enable error %d\n", __func__, ret); | |
657 | return ret; | |
90464971 DL |
658 | } |
659 | ||
bb5930d5 SG |
660 | /* backlight / pwm */ |
661 | ret = panel_enable_backlight(panel); | |
662 | if (ret) { | |
663 | debug("%s: backlight error: %d\n", __func__, ret); | |
664 | return ret; | |
665 | } | |
666 | ||
667 | ret = uclass_get_device(UCLASS_VIDEO_BRIDGE, 0, &bridge); | |
668 | if (!ret) | |
669 | ret = video_bridge_set_backlight(bridge, 80); | |
670 | if (ret) { | |
671 | debug("%s: No video bridge, or no backlight on bridge\n", | |
672 | __func__); | |
673 | exynos_pinmux_config(PERIPH_ID_PWM0, 0); | |
674 | } | |
675 | ||
676 | uc_priv->xsize = priv->vl_col; | |
677 | uc_priv->ysize = priv->vl_row; | |
678 | uc_priv->bpix = priv->vl_bpix; | |
679 | ||
680 | /* Enable flushing after LCD writes if requested */ | |
681 | video_set_flush_dcache(dev, true); | |
682 | ||
683 | return 0; | |
6d4339f6 DL |
684 | } |
685 | ||
bb5930d5 | 686 | static int exynos_fb_bind(struct udevice *dev) |
6d4339f6 | 687 | { |
8a8d24bd | 688 | struct video_uc_plat *plat = dev_get_uclass_plat(dev); |
bb5930d5 SG |
689 | |
690 | /* This is the maximum panel size we expect to see */ | |
691 | plat->size = 1920 * 1080 * 2; | |
692 | ||
693 | return 0; | |
6d4339f6 | 694 | } |
bb5930d5 SG |
695 | |
696 | static const struct video_ops exynos_fb_ops = { | |
697 | }; | |
698 | ||
699 | static const struct udevice_id exynos_fb_ids[] = { | |
700 | { .compatible = "samsung,exynos-fimd" }, | |
701 | { } | |
702 | }; | |
703 | ||
704 | U_BOOT_DRIVER(exynos_fb) = { | |
705 | .name = "exynos_fb", | |
706 | .id = UCLASS_VIDEO, | |
707 | .of_match = exynos_fb_ids, | |
708 | .ops = &exynos_fb_ops, | |
709 | .bind = exynos_fb_bind, | |
710 | .probe = exynos_fb_probe, | |
d1998a9f | 711 | .of_to_plat = exynos_fb_of_to_plat, |
41575d8e | 712 | .priv_auto = sizeof(struct exynos_fb_priv), |
bb5930d5 | 713 | }; |