]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/video/exynos_fimd.c
doc: SPI: Add qspi test details on AM43xx
[karo-tx-uboot.git] / drivers / video / exynos_fimd.c
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  * SPDX-License-Identifier:     GPL-2.0+
8  */
9
10 #include <config.h>
11 #include <common.h>
12 #include <asm/io.h>
13 #include <lcd.h>
14 #include <div64.h>
15 #include <fdtdec.h>
16 #include <libfdt.h>
17 #include <asm/arch/clk.h>
18 #include <asm/arch/clock.h>
19 #include <asm/arch/cpu.h>
20 #include "exynos_fb.h"
21
22 DECLARE_GLOBAL_DATA_PTR;
23
24 static unsigned long *lcd_base_addr;
25 static vidinfo_t *pvid;
26 static struct exynos_fb *fimd_ctrl;
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 {
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
50 static void exynos_fimd_set_dp_clkcon(unsigned int enabled)
51 {
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
60 static void exynos_fimd_set_par(unsigned int win_id)
61 {
62         unsigned int cfg = 0;
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
76         if (pvid->logo_on) /* To get proprietary LOGO */
77                 cfg |= EXYNOS_WINCON_WSWP_ENABLE;
78         else /* To get output console on LCD */
79                 cfg |= EXYNOS_WINCON_HAWSWP_ENABLE;
80
81         /* dma burst is 16 */
82         cfg |= EXYNOS_WINCON_BURSTLEN_16WORD;
83
84         if (pvid->logo_on) /* To get proprietary LOGO */
85                 cfg |= EXYNOS_WINCON_BPPMODE_24BPP_888;
86         else /* To get output console on LCD */
87                 cfg |= EXYNOS_WINCON_BPPMODE_16BPP_565;
88
89         writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
90                         EXYNOS_WINCON(win_id));
91
92         /* set window position to x=0, y=0*/
93         cfg = EXYNOS_VIDOSD_LEFT_X(0) | EXYNOS_VIDOSD_TOP_Y(0);
94         writel(cfg, (unsigned int)&fimd_ctrl->vidosd0a +
95                         EXYNOS_VIDOSD(win_id));
96
97         cfg = EXYNOS_VIDOSD_RIGHT_X(pvid->vl_col - 1) |
98                 EXYNOS_VIDOSD_BOTTOM_Y(pvid->vl_row - 1) |
99                 EXYNOS_VIDOSD_RIGHT_X_E(1) |
100                 EXYNOS_VIDOSD_BOTTOM_Y_E(0);
101
102         writel(cfg, (unsigned int)&fimd_ctrl->vidosd0b +
103                         EXYNOS_VIDOSD(win_id));
104
105         /* set window size for window0*/
106         cfg = EXYNOS_VIDOSD_SIZE(pvid->vl_col * pvid->vl_row);
107         writel(cfg, (unsigned int)&fimd_ctrl->vidosd0c +
108                         EXYNOS_VIDOSD(win_id));
109 }
110
111 static void exynos_fimd_set_buffer_address(unsigned int win_id)
112 {
113         unsigned long start_addr, end_addr;
114
115         start_addr = (unsigned long)lcd_base_addr;
116         end_addr = start_addr + ((pvid->vl_col * (NBITS(pvid->vl_bpix) / 8)) *
117                                 pvid->vl_row);
118
119         writel(start_addr, (unsigned int)&fimd_ctrl->vidw00add0b0 +
120                         EXYNOS_BUFFER_OFFSET(win_id));
121         writel(end_addr, (unsigned int)&fimd_ctrl->vidw00add1b0 +
122                         EXYNOS_BUFFER_OFFSET(win_id));
123 }
124
125 static void exynos_fimd_set_clock(vidinfo_t *pvid)
126 {
127         unsigned int cfg = 0, div = 0, remainder, remainder_div;
128         unsigned long pixel_clock;
129         unsigned long long src_clock;
130
131         if (pvid->dual_lcd_enabled) {
132                 pixel_clock = pvid->vl_freq *
133                                 (pvid->vl_hspw + pvid->vl_hfpd +
134                                  pvid->vl_hbpd + pvid->vl_col / 2) *
135                                 (pvid->vl_vspw + pvid->vl_vfpd +
136                                  pvid->vl_vbpd + pvid->vl_row);
137         } else if (pvid->interface_mode == FIMD_CPU_INTERFACE) {
138                 pixel_clock = pvid->vl_freq *
139                                 pvid->vl_width * pvid->vl_height *
140                                 (pvid->cs_setup + pvid->wr_setup +
141                                  pvid->wr_act + pvid->wr_hold + 1);
142         } else {
143                 pixel_clock = pvid->vl_freq *
144                                 (pvid->vl_hspw + pvid->vl_hfpd +
145                                  pvid->vl_hbpd + pvid->vl_col) *
146                                 (pvid->vl_vspw + pvid->vl_vfpd +
147                                  pvid->vl_vbpd + pvid->vl_row);
148         }
149
150         cfg = readl(&fimd_ctrl->vidcon0);
151         cfg &= ~(EXYNOS_VIDCON0_CLKSEL_MASK | EXYNOS_VIDCON0_CLKVALUP_MASK |
152                 EXYNOS_VIDCON0_CLKVAL_F(0xFF) | EXYNOS_VIDCON0_VCLKEN_MASK |
153                 EXYNOS_VIDCON0_CLKDIR_MASK);
154         cfg |= (EXYNOS_VIDCON0_CLKSEL_SCLK | EXYNOS_VIDCON0_CLKVALUP_ALWAYS |
155                 EXYNOS_VIDCON0_VCLKEN_NORMAL | EXYNOS_VIDCON0_CLKDIR_DIVIDED);
156
157         src_clock = (unsigned long long) get_lcd_clk();
158
159         /* get quotient and remainder. */
160         remainder = do_div(src_clock, pixel_clock);
161         div = src_clock;
162
163         remainder *= 10;
164         remainder_div = remainder / pixel_clock;
165
166         /* round about one places of decimals. */
167         if (remainder_div >= 5)
168                 div++;
169
170         /* in case of dual lcd mode. */
171         if (pvid->dual_lcd_enabled)
172                 div--;
173
174         cfg |= EXYNOS_VIDCON0_CLKVAL_F(div - 1);
175         writel(cfg, &fimd_ctrl->vidcon0);
176 }
177
178 void exynos_set_trigger(void)
179 {
180         unsigned int cfg = 0;
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
189 int exynos_is_i80_frame_done(void)
190 {
191         unsigned int cfg = 0;
192         int status;
193
194         cfg = readl(&fimd_ctrl->trigcon);
195
196         /* frame done func is valid only when TRIMODE[0] is set to 1. */
197         status = (cfg & EXYNOS_I80STATUS_TRIG_DONE) ==
198                         EXYNOS_I80STATUS_TRIG_DONE;
199
200         return status;
201 }
202
203 static void exynos_fimd_lcd_on(void)
204 {
205         unsigned int cfg = 0;
206
207         /* display on */
208         cfg = readl(&fimd_ctrl->vidcon0);
209         cfg |= (EXYNOS_VIDCON0_ENVID_ENABLE | EXYNOS_VIDCON0_ENVID_F_ENABLE);
210         writel(cfg, &fimd_ctrl->vidcon0);
211 }
212
213 static void exynos_fimd_window_on(unsigned int win_id)
214 {
215         unsigned int cfg = 0;
216
217         /* enable window */
218         cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
219                         EXYNOS_WINCON(win_id));
220         cfg |= EXYNOS_WINCON_ENWIN_ENABLE;
221         writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
222                         EXYNOS_WINCON(win_id));
223
224         cfg = readl(&fimd_ctrl->winshmap);
225         cfg |= EXYNOS_WINSHMAP_CH_ENABLE(win_id);
226         writel(cfg, &fimd_ctrl->winshmap);
227 }
228
229 void exynos_fimd_lcd_off(void)
230 {
231         unsigned int cfg = 0;
232
233         cfg = readl(&fimd_ctrl->vidcon0);
234         cfg &= (EXYNOS_VIDCON0_ENVID_DISABLE | EXYNOS_VIDCON0_ENVID_F_DISABLE);
235         writel(cfg, &fimd_ctrl->vidcon0);
236 }
237
238 void exynos_fimd_window_off(unsigned int win_id)
239 {
240         unsigned int cfg = 0;
241
242         cfg = readl((unsigned int)&fimd_ctrl->wincon0 +
243                         EXYNOS_WINCON(win_id));
244         cfg &= EXYNOS_WINCON_ENWIN_DISABLE;
245         writel(cfg, (unsigned int)&fimd_ctrl->wincon0 +
246                         EXYNOS_WINCON(win_id));
247
248         cfg = readl(&fimd_ctrl->winshmap);
249         cfg &= ~EXYNOS_WINSHMAP_CH_DISABLE(win_id);
250         writel(cfg, &fimd_ctrl->winshmap);
251 }
252
253
254 void exynos_fimd_lcd_init(vidinfo_t *vid)
255 {
256         unsigned int cfg = 0, rgb_mode;
257         unsigned int offset;
258 #ifdef CONFIG_OF_CONTROL
259         unsigned int node;
260
261         node = fdtdec_next_compatible(gd->fdt_blob,
262                                         0, COMPAT_SAMSUNG_EXYNOS_FIMD);
263         if (node <= 0)
264                 debug("exynos_fb: Can't get device node for fimd\n");
265
266         fimd_ctrl = (struct exynos_fb *)fdtdec_get_addr(gd->fdt_blob,
267                                                                 node, "reg");
268         if (fimd_ctrl == NULL)
269                 debug("Can't get the FIMD base address\n");
270 #else
271         fimd_ctrl = (struct exynos_fb *)samsung_get_base_fimd();
272 #endif
273
274         offset = exynos_fimd_get_base_offset();
275
276         /* store panel info to global variable */
277         pvid = vid;
278
279         rgb_mode = vid->rgb_mode;
280
281         if (vid->interface_mode == FIMD_RGB_INTERFACE) {
282                 cfg |= EXYNOS_VIDCON0_VIDOUT_RGB;
283                 writel(cfg, &fimd_ctrl->vidcon0);
284
285                 cfg = readl(&fimd_ctrl->vidcon2);
286                 cfg &= ~(EXYNOS_VIDCON2_WB_MASK |
287                         EXYNOS_VIDCON2_TVFORMATSEL_MASK |
288                         EXYNOS_VIDCON2_TVFORMATSEL_YUV_MASK);
289                 cfg |= EXYNOS_VIDCON2_WB_DISABLE;
290                 writel(cfg, &fimd_ctrl->vidcon2);
291
292                 /* set polarity */
293                 cfg = 0;
294                 if (!pvid->vl_clkp)
295                         cfg |= EXYNOS_VIDCON1_IVCLK_RISING_EDGE;
296                 if (!pvid->vl_hsp)
297                         cfg |= EXYNOS_VIDCON1_IHSYNC_INVERT;
298                 if (!pvid->vl_vsp)
299                         cfg |= EXYNOS_VIDCON1_IVSYNC_INVERT;
300                 if (!pvid->vl_dp)
301                         cfg |= EXYNOS_VIDCON1_IVDEN_INVERT;
302
303                 writel(cfg, (unsigned int)&fimd_ctrl->vidcon1 + offset);
304
305                 /* set timing */
306                 cfg = EXYNOS_VIDTCON0_VFPD(pvid->vl_vfpd - 1);
307                 cfg |= EXYNOS_VIDTCON0_VBPD(pvid->vl_vbpd - 1);
308                 cfg |= EXYNOS_VIDTCON0_VSPW(pvid->vl_vspw - 1);
309                 writel(cfg, (unsigned int)&fimd_ctrl->vidtcon0 + offset);
310
311                 cfg = EXYNOS_VIDTCON1_HFPD(pvid->vl_hfpd - 1);
312                 cfg |= EXYNOS_VIDTCON1_HBPD(pvid->vl_hbpd - 1);
313                 cfg |= EXYNOS_VIDTCON1_HSPW(pvid->vl_hspw - 1);
314
315                 writel(cfg, (unsigned int)&fimd_ctrl->vidtcon1 + offset);
316
317                 /* set lcd size */
318                 cfg = EXYNOS_VIDTCON2_HOZVAL(pvid->vl_col - 1) |
319                         EXYNOS_VIDTCON2_LINEVAL(pvid->vl_row - 1) |
320                         EXYNOS_VIDTCON2_HOZVAL_E(pvid->vl_col - 1) |
321                         EXYNOS_VIDTCON2_LINEVAL_E(pvid->vl_row - 1);
322
323                 writel(cfg, (unsigned int)&fimd_ctrl->vidtcon2 + offset);
324         }
325
326         /* set display mode */
327         cfg = readl(&fimd_ctrl->vidcon0);
328         cfg &= ~EXYNOS_VIDCON0_PNRMODE_MASK;
329         cfg |= (rgb_mode << EXYNOS_VIDCON0_PNRMODE_SHIFT);
330         writel(cfg, &fimd_ctrl->vidcon0);
331
332         /* set par */
333         exynos_fimd_set_par(pvid->win_id);
334
335         /* set memory address */
336         exynos_fimd_set_buffer_address(pvid->win_id);
337
338         /* set buffer size */
339         cfg = EXYNOS_VIDADDR_PAGEWIDTH(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) |
340                 EXYNOS_VIDADDR_PAGEWIDTH_E(pvid->vl_col * NBITS(pvid->vl_bpix) / 8) |
341                 EXYNOS_VIDADDR_OFFSIZE(0) |
342                 EXYNOS_VIDADDR_OFFSIZE_E(0);
343
344         writel(cfg, (unsigned int)&fimd_ctrl->vidw00add2 +
345                                         EXYNOS_BUFFER_SIZE(pvid->win_id));
346
347         /* set clock */
348         exynos_fimd_set_clock(pvid);
349
350         /* set rgb mode to dual lcd. */
351         exynos_fimd_set_dualrgb(pvid->dual_lcd_enabled);
352
353         /* display on */
354         exynos_fimd_lcd_on();
355
356         /* window on */
357         exynos_fimd_window_on(pvid->win_id);
358
359         exynos_fimd_set_dp_clkcon(pvid->dp_enabled);
360 }
361
362 unsigned long exynos_fimd_calc_fbsize(void)
363 {
364         return pvid->vl_col * pvid->vl_row * (NBITS(pvid->vl_bpix) / 8);
365 }