]>
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 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License as | |
9 | * published by the Free Software Foundation; either version 2 of | |
10 | * the License, or (at your option) any later version. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License | |
18 | * along with this program; if not, write to the Free Software | |
19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
20 | * MA 02111-1307 USA | |
21 | */ | |
22 | ||
23 | #include <config.h> | |
24 | #include <common.h> | |
25 | #include <asm/io.h> | |
26 | #include <lcd.h> | |
27 | #include <div64.h> | |
c23f3157 AK |
28 | #include <fdtdec.h> |
29 | #include <libfdt.h> | |
6d4339f6 DL |
30 | #include <asm/arch/clk.h> |
31 | #include <asm/arch/clock.h> | |
32 | #include <asm/arch/cpu.h> | |
33 | #include "exynos_fb.h" | |
34 | ||
c23f3157 AK |
35 | DECLARE_GLOBAL_DATA_PTR; |
36 | ||
6d4339f6 DL |
37 | static unsigned long *lcd_base_addr; |
38 | static vidinfo_t *pvid; | |
47ff6073 | 39 | static struct exynos_fb *fimd_ctrl; |
6d4339f6 DL |
40 | |
41 | void exynos_fimd_lcd_init_mem(u_long screen_base, u_long fb_size, | |
42 | u_long palette_size) | |
43 | { | |
44 | lcd_base_addr = (unsigned long *)screen_base; | |
45 | } | |
46 | ||
47 | static void exynos_fimd_set_dualrgb(unsigned int enabled) | |
48 | { | |
6d4339f6 DL |
49 | unsigned int cfg = 0; |
50 | ||
51 | if (enabled) { | |
52 | cfg = EXYNOS_DUALRGB_BYPASS_DUAL | EXYNOS_DUALRGB_LINESPLIT | | |
53 | EXYNOS_DUALRGB_VDEN_EN_ENABLE; | |
54 | ||
55 | /* in case of Line Split mode, MAIN_CNT doesn't neet to set. */ | |
56 | cfg |= EXYNOS_DUALRGB_SUB_CNT(pvid->vl_col / 2) | | |
57 | EXYNOS_DUALRGB_MAIN_CNT(0); | |
58 | } | |
59 | ||
60 | writel(cfg, &fimd_ctrl->dualrgb); | |
61 | } | |
62 | ||
a29c8322 DL |
63 | static void exynos_fimd_set_dp_clkcon(unsigned int enabled) |
64 | { | |
a29c8322 DL |
65 | unsigned int cfg = 0; |
66 | ||
67 | if (enabled) | |
68 | cfg = EXYNOS_DP_CLK_ENABLE; | |
69 | ||
70 | writel(cfg, &fimd_ctrl->dp_mie_clkcon); | |
71 | } | |
72 | ||
6d4339f6 DL |
73 | static void exynos_fimd_set_par(unsigned int win_id) |
74 | { | |
75 | unsigned int cfg = 0; | |
6d4339f6 DL |
76 | |
77 | /* set window control */ | |
78 | cfg = readl((unsigned int)&fimd_ctrl->wincon0 + | |
79 | EXYNOS_WINCON(win_id)); | |
80 | ||
81 | cfg &= ~(EXYNOS_WINCON_BITSWP_ENABLE | EXYNOS_WINCON_BYTESWP_ENABLE | | |
82 | EXYNOS_WINCON_HAWSWP_ENABLE | EXYNOS_WINCON_WSWP_ENABLE | | |
83 | EXYNOS_WINCON_BURSTLEN_MASK | EXYNOS_WINCON_BPPMODE_MASK | | |
84 | EXYNOS_WINCON_INRGB_MASK | EXYNOS_WINCON_DATAPATH_MASK); | |
85 | ||
86 | /* DATAPATH is DMA */ | |
87 | cfg |= EXYNOS_WINCON_DATAPATH_DMA; | |
88 | ||
61b59e27 AK |
89 | if (pvid->logo_on) /* To get proprietary LOGO */ |
90 | cfg |= EXYNOS_WINCON_WSWP_ENABLE; | |
91 | else /* To get output console on LCD */ | |
92 | cfg |= EXYNOS_WINCON_HAWSWP_ENABLE; | |
6d4339f6 DL |
93 | |
94 | /* dma burst is 16 */ | |
95 | cfg |= EXYNOS_WINCON_BURSTLEN_16WORD; | |
96 | ||
61b59e27 AK |
97 | if (pvid->logo_on) /* To get proprietary LOGO */ |
98 | cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888; | |
99 | else /* To get output console on LCD */ | |
100 | cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565; | |
6d4339f6 DL |
101 | |
102 | writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + | |
103 | EXYNOS_WINCON(win_id)); | |
104 | ||
105 | /* set window position to x=0, y=0*/ | |
106 | cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0); | |
107 | writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a + | |
108 | EXYNOS_VIDOSD(win_id)); | |
109 | ||
110 | cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) | | |
ee93dcfa DL |
111 | EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) | |
112 | EXYNOS_VIDOSD_RIGHT_X_E(1) | | |
113 | EXYNOS_VIDOSD_BOTTOM_Y_E(0); | |
114 | ||
6d4339f6 DL |
115 | writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b + |
116 | EXYNOS_VIDOSD(win_id)); | |
117 | ||
118 | /* set window size for window0*/ | |
119 | cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row); | |
120 | writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c + | |
121 | EXYNOS_VIDOSD(win_id)); | |
122 | } | |
123 | ||
124 | static void exynos_fimd_set_buffer_address(unsigned int win_id) | |
125 | { | |
126 | unsigned long start_addr, end_addr; | |
6d4339f6 DL |
127 | |
128 | start_addr = (unsigned long)lcd_base_addr; | |
f78095e4 | 129 | end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) * |
6d4339f6 DL |
130 | pvid->vl_row); |
131 | ||
132 | writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 + | |
133 | EXYNOS_BUFFER_OFFSET(win_id)); | |
134 | writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 + | |
135 | EXYNOS_BUFFER_OFFSET(win_id)); | |
136 | } | |
137 | ||
138 | static void exynos_fimd_set_clock(vidinfo_t *pvid) | |
139 | { | |
140 | unsigned int cfg = 0, div = 0, remainder, remainder_div; | |
141 | unsigned long pixel_clock; | |
142 | unsigned long long src_clock; | |
6d4339f6 DL |
143 | |
144 | if (pvid->dual_lcd_enabled) { | |
145 | pixel_clock = pvid->vl_freq * | |
146 | (pvid->vl_hspw + pvid->vl_hfpd + | |
147 | pvid->vl_hbpd + pvid->vl_col / 2) * | |
148 | (pvid->vl_vspw + pvid->vl_vfpd + | |
149 | pvid->vl_vbpd + pvid->vl_row); | |
150 | } else if (pvid->interface_mode == FIMD_CPU_INTERFACE) { | |
151 | pixel_clock = pvid->vl_freq * | |
152 | pvid->vl_width * pvid->vl_height * | |
153 | (pvid->cs_setup + pvid->wr_setup + | |
154 | pvid->wr_act + pvid->wr_hold + 1); | |
155 | } else { | |
156 | pixel_clock = pvid->vl_freq * | |
157 | (pvid->vl_hspw + pvid->vl_hfpd + | |
158 | pvid->vl_hbpd + pvid->vl_col) * | |
159 | (pvid->vl_vspw + pvid->vl_vfpd + | |
160 | pvid->vl_vbpd + pvid->vl_row); | |
161 | } | |
162 | ||
163 | cfg = readl(&fimd_ctrl->vidcon0); | |
164 | cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK | | |
165 | EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK | | |
166 | EXYNOS_VIDCON0_CLKDIR_MASK); | |
167 | cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS | | |
168 | EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED); | |
169 | ||
6d4339f6 DL |
170 | src_clock = (unsigned long long) get_lcd_clk(); |
171 | ||
172 | /* get quotient and remainder. */ | |
173 | remainder = do_div(src_clock, pixel_clock); | |
174 | div = src_clock; | |
175 | ||
176 | remainder *= 10; | |
177 | remainder_div = remainder / pixel_clock; | |
178 | ||
179 | /* round about one places of decimals. */ | |
180 | if (remainder_div >= 5) | |
181 | div++; | |
182 | ||
183 | /* in case of dual lcd mode. */ | |
184 | if (pvid->dual_lcd_enabled) | |
185 | div--; | |
186 | ||
187 | cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1); | |
188 | writel(cfg, &fimd_ctrl->vidcon0); | |
189 | } | |
190 | ||
191 | void exynos_set_trigger(void) | |
192 | { | |
193 | unsigned int cfg = 0; | |
6d4339f6 DL |
194 | |
195 | cfg = readl(&fimd_ctrl->trigcon); | |
196 | ||
197 | cfg |= (EXYNOS_I80SOFT_TRIG_EN | EXYNOS_I80START_TRIG); | |
198 | ||
199 | writel(cfg, &fimd_ctrl->trigcon); | |
200 | } | |
201 | ||
202 | int exynos_is_i80_frame_done(void) | |
203 | { | |
204 | unsigned int cfg = 0; | |
205 | int status; | |
6d4339f6 DL |
206 | |
207 | cfg = readl(&fimd_ctrl->trigcon); | |
208 | ||
209 | /* frame done func is valid only when TRIMODE[0] is set to 1. */ | |
210 | status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) == | |
211 | EXYNOS_I80STATUS_TRIG_DONE; | |
212 | ||
213 | return status; | |
214 | } | |
215 | ||
216 | static void exynos_fimd_lcd_on(void) | |
217 | { | |
218 | unsigned int cfg = 0; | |
6d4339f6 DL |
219 | |
220 | /* display on */ | |
221 | cfg = readl(&fimd_ctrl->vidcon0); | |
222 | cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE); | |
223 | writel(cfg, &fimd_ctrl->vidcon0); | |
224 | } | |
225 | ||
226 | static void exynos_fimd_window_on(unsigned int win_id) | |
227 | { | |
228 | unsigned int cfg = 0; | |
6d4339f6 DL |
229 | |
230 | /* enable window */ | |
231 | cfg = readl((unsigned int)&fimd_ctrl->wincon0 + | |
232 | EXYNOS_WINCON(win_id)); | |
233 | cfg |= EXYNOS_WINCON_ENWIN_ENABLE; | |
234 | writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + | |
235 | EXYNOS_WINCON(win_id)); | |
236 | ||
237 | cfg = readl(&fimd_ctrl->winshmap); | |
238 | cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id); | |
239 | writel(cfg, &fimd_ctrl->winshmap); | |
240 | } | |
241 | ||
242 | void exynos_fimd_lcd_off(void) | |
243 | { | |
244 | unsigned int cfg = 0; | |
6d4339f6 DL |
245 | |
246 | cfg = readl(&fimd_ctrl->vidcon0); | |
247 | cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE); | |
248 | writel(cfg, &fimd_ctrl->vidcon0); | |
249 | } | |
250 | ||
251 | void exynos_fimd_window_off(unsigned int win_id) | |
252 | { | |
253 | unsigned int cfg = 0; | |
6d4339f6 DL |
254 | |
255 | cfg = readl((unsigned int)&fimd_ctrl->wincon0 + | |
256 | EXYNOS_WINCON(win_id)); | |
257 | cfg &= EXYNOS_WINCON_ENWIN_DISABLE; | |
258 | writel(cfg, (unsigned int)&fimd_ctrl->wincon0 + | |
259 | EXYNOS_WINCON(win_id)); | |
260 | ||
261 | cfg = readl(&fimd_ctrl->winshmap); | |
262 | cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id); | |
263 | writel(cfg, &fimd_ctrl->winshmap); | |
264 | } | |
265 | ||
ee93dcfa | 266 | |
6d4339f6 DL |
267 | void exynos_fimd_lcd_init(vidinfo_t *vid) |
268 | { | |
269 | unsigned int cfg = 0, rgb_mode; | |
ee93dcfa | 270 | unsigned int offset; |
c23f3157 AK |
271 | #ifdef CONFIG_OF_CONTROL |
272 | unsigned int node; | |
273 | ||
274 | node = fdtdec_next_compatible(gd->fdt_blob, | |
275 | 0, COMPAT_SAMSUNG_EXYNOS_FIMD); | |
276 | if (node <= 0) | |
277 | debug("exynos_fb: Can't get device node for fimd\n"); | |
278 | ||
279 | fimd_ctrl = (struct exynos_fb *)fdtdec_get_addr(gd->fdt_blob, | |
280 | node, "reg"); | |
281 | if (fimd_ctrl == NULL) | |
282 | debug("Can't get the FIMD base address\n"); | |
283 | #endif | |
47ff6073 | 284 | fimd_ctrl = (struct exynos_fb *)samsung_get_base_fimd(); |
ee93dcfa DL |
285 | |
286 | offset = exynos_fimd_get_base_offset(); | |
6d4339f6 DL |
287 | |
288 | /* store panel info to global variable */ | |
289 | pvid = vid; | |
290 | ||
90464971 | 291 | rgb_mode = vid->rgb_mode; |
6d4339f6 DL |
292 | |
293 | if (vid->interface_mode == FIMD_RGB_INTERFACE) { | |
294 | cfg |= EXYNOS_VIDCON0_VIDOUT_RGB; | |
295 | writel(cfg, &fimd_ctrl->vidcon0); | |
296 | ||
297 | cfg = readl(&fimd_ctrl->vidcon2); | |
298 | cfg &= ~(EXYNOS_VIDCON2_WB_MASK | | |
299 | EXYNOS_VIDCON2_TVFORMATSEL_MASK | | |
300 | EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK); | |
301 | cfg |= EXYNOS_VIDCON2_WB_DISABLE; | |
302 | writel(cfg, &fimd_ctrl->vidcon2); | |
303 | ||
304 | /* set polarity */ | |
305 | cfg = 0; | |
306 | if (!pvid->vl_clkp) | |
307 | cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE; | |
308 | if (!pvid->vl_hsp) | |
309 | cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT; | |
310 | if (!pvid->vl_vsp) | |
311 | cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT; | |
312 | if (!pvid->vl_dp) | |
313 | cfg |= EXYNOS_VIDCON1_IVDEN_INVERT; | |
314 | ||
ee93dcfa | 315 | writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset); |
6d4339f6 DL |
316 | |
317 | /* set timing */ | |
318 | cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1); | |
319 | cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1); | |
320 | cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1); | |
ee93dcfa | 321 | writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset); |
6d4339f6 DL |
322 | |
323 | cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1); | |
324 | cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1); | |
325 | cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1); | |
326 | ||
ee93dcfa | 327 | writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset); |
6d4339f6 DL |
328 | |
329 | /* set lcd size */ | |
ee93dcfa DL |
330 | cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) | |
331 | EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) | | |
332 | EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) | | |
333 | EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1); | |
6d4339f6 | 334 | |
ee93dcfa | 335 | writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset); |
6d4339f6 DL |
336 | } |
337 | ||
338 | /* set display mode */ | |
339 | cfg = readl(&fimd_ctrl->vidcon0); | |
340 | cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK; | |
341 | cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT); | |
342 | writel(cfg, &fimd_ctrl->vidcon0); | |
343 | ||
344 | /* set par */ | |
345 | exynos_fimd_set_par(pvid->win_id); | |
346 | ||
347 | /* set memory address */ | |
348 | exynos_fimd_set_buffer_address(pvid->win_id); | |
349 | ||
350 | /* set buffer size */ | |
ee93dcfa DL |
351 | cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | |
352 | EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) | | |
353 | EXYNOS_VIDADDR_OFFSIZE(0) | | |
354 | EXYNOS_VIDADDR_OFFSIZE_E(0); | |
355 | ||
6d4339f6 DL |
356 | writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 + |
357 | EXYNOS_BUFFER_SIZE(pvid->win_id)); | |
358 | ||
359 | /* set clock */ | |
360 | exynos_fimd_set_clock(pvid); | |
361 | ||
362 | /* set rgb mode to dual lcd. */ | |
363 | exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled); | |
364 | ||
365 | /* display on */ | |
366 | exynos_fimd_lcd_on(); | |
367 | ||
368 | /* window on */ | |
369 | exynos_fimd_window_on(pvid->win_id); | |
a29c8322 DL |
370 | |
371 | exynos_fimd_set_dp_clkcon(pvid->dp_enabled); | |
6d4339f6 DL |
372 | } |
373 | ||
374 | unsigned long exynos_fimd_calc_fbsize(void) | |
375 | { | |
f78095e4 | 376 | return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8); |
6d4339f6 | 377 | } |