]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - arch/arm/cpu/armv7/tegra20/display.c
Merge branch 'master' of git://git.denx.de/u-boot into resolve
[karo-tx-uboot.git] / arch / arm / cpu / armv7 / tegra20 / display.c
1 /*
2  *  (C) Copyright 2010
3  *  NVIDIA Corporation <www.nvidia.com>
4  *
5  * See file CREDITS for list of people who contributed to this
6  * project.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
21  * MA 02111-1307 USA
22  */
23
24 #include <common.h>
25 #include <asm/io.h>
26 #include <asm/arch/clock.h>
27 #include <asm/arch/tegra.h>
28 #include <asm/arch/display.h>
29 #include <asm/arch/dc.h>
30 #include <asm/arch-tegra/clk_rst.h>
31 #include <asm/arch-tegra/timer.h>
32
33 static struct fdt_disp_config config;
34
35 static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win)
36 {
37         unsigned h_dda, v_dda;
38         unsigned long val;
39
40         val = readl(&dc->cmd.disp_win_header);
41         val |= WINDOW_A_SELECT;
42         writel(val, &dc->cmd.disp_win_header);
43
44         writel(win->fmt, &dc->win.color_depth);
45
46         clrsetbits_le32(&dc->win.byte_swap, BYTE_SWAP_MASK,
47                         BYTE_SWAP_NOSWAP << BYTE_SWAP_SHIFT);
48
49         val = win->out_x << H_POSITION_SHIFT;
50         val |= win->out_y << V_POSITION_SHIFT;
51         writel(val, &dc->win.pos);
52
53         val = win->out_w << H_SIZE_SHIFT;
54         val |= win->out_h << V_SIZE_SHIFT;
55         writel(val, &dc->win.size);
56
57         val = (win->w * win->bpp / 8) << H_PRESCALED_SIZE_SHIFT;
58         val |= win->h << V_PRESCALED_SIZE_SHIFT;
59         writel(val, &dc->win.prescaled_size);
60
61         writel(0, &dc->win.h_initial_dda);
62         writel(0, &dc->win.v_initial_dda);
63
64         h_dda = (win->w * 0x1000) / max(win->out_w - 1, 1);
65         v_dda = (win->h * 0x1000) / max(win->out_h - 1, 1);
66
67         val = h_dda << H_DDA_INC_SHIFT;
68         val |= v_dda << V_DDA_INC_SHIFT;
69         writel(val, &dc->win.dda_increment);
70
71         writel(win->stride, &dc->win.line_stride);
72         writel(0, &dc->win.buf_stride);
73
74         val = WIN_ENABLE;
75         if (win->bpp < 24)
76                 val |= COLOR_EXPAND;
77         writel(val, &dc->win.win_opt);
78
79         writel((unsigned long)win->phys_addr, &dc->winbuf.start_addr);
80         writel(win->x, &dc->winbuf.addr_h_offset);
81         writel(win->y, &dc->winbuf.addr_v_offset);
82
83         writel(0xff00, &dc->win.blend_nokey);
84         writel(0xff00, &dc->win.blend_1win);
85
86         val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
87         val |= GENERAL_UPDATE | WIN_A_UPDATE;
88         writel(val, &dc->cmd.state_ctrl);
89 }
90
91 static void write_pair(struct fdt_disp_config *config, int item, u32 *reg)
92 {
93         writel(config->horiz_timing[item] |
94                         (config->vert_timing[item] << 16), reg);
95 }
96
97 static int update_display_mode(struct dc_disp_reg *disp,
98                 struct fdt_disp_config *config)
99 {
100         unsigned long val;
101         unsigned long rate;
102         unsigned long div;
103
104         writel(0x0, &disp->disp_timing_opt);
105         write_pair(config, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync);
106         write_pair(config, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width);
107         write_pair(config, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch);
108         write_pair(config, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch);
109
110         writel(config->width | (config->height << 16), &disp->disp_active);
111
112         val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT;
113         val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT;
114         writel(val, &disp->data_enable_opt);
115
116         val = DATA_FORMAT_DF1P1C << DATA_FORMAT_SHIFT;
117         val |= DATA_ALIGNMENT_MSB << DATA_ALIGNMENT_SHIFT;
118         val |= DATA_ORDER_RED_BLUE << DATA_ORDER_SHIFT;
119         writel(val, &disp->disp_interface_ctrl);
120
121         /*
122          * The pixel clock divider is in 7.1 format (where the bottom bit
123          * represents 0.5). Here we calculate the divider needed to get from
124          * the display clock (typically 600MHz) to the pixel clock. We round
125          * up or down as requried.
126          */
127         rate = clock_get_periph_rate(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL);
128         div = ((rate * 2 + config->pixel_clock / 2) / config->pixel_clock) - 2;
129         debug("Display clock %lu, divider %lu\n", rate, div);
130
131         writel(0x00010001, &disp->shift_clk_opt);
132
133         val = PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT;
134         val |= div << SHIFT_CLK_DIVIDER_SHIFT;
135         writel(val, &disp->disp_clk_ctrl);
136
137         return 0;
138 }
139
140 /* Start up the display and turn on power to PWMs */
141 static void basic_init(struct dc_cmd_reg *cmd)
142 {
143         u32 val;
144
145         writel(0x00000100, &cmd->gen_incr_syncpt_ctrl);
146         writel(0x0000011a, &cmd->cont_syncpt_vsync);
147         writel(0x00000000, &cmd->int_type);
148         writel(0x00000000, &cmd->int_polarity);
149         writel(0x00000000, &cmd->int_mask);
150         writel(0x00000000, &cmd->int_enb);
151
152         val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE;
153         val |= PW3_ENABLE | PW4_ENABLE | PM0_ENABLE;
154         val |= PM1_ENABLE;
155         writel(val, &cmd->disp_pow_ctrl);
156
157         val = readl(&cmd->disp_cmd);
158         val |= CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT;
159         writel(val, &cmd->disp_cmd);
160 }
161
162 static void basic_init_timer(struct dc_disp_reg *disp)
163 {
164         writel(0x00000020, &disp->mem_high_pri);
165         writel(0x00000001, &disp->mem_high_pri_timer);
166 }
167
168 static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
169         0x00000000,
170         0x00000000,
171         0x00000000,
172         0x00000000,
173 };
174
175 static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
176         0x00000000,
177         0x01000000,
178         0x00000000,
179         0x00000000,
180 };
181
182 static const u32 rgb_data_tab[PIN_REG_COUNT] = {
183         0x00000000,
184         0x00000000,
185         0x00000000,
186         0x00000000,
187 };
188
189 static const u32 rgb_sel_tab[PIN_OUTPUT_SEL_COUNT] = {
190         0x00000000,
191         0x00000000,
192         0x00000000,
193         0x00000000,
194         0x00210222,
195         0x00002200,
196         0x00020000,
197 };
198
199 static void rgb_enable(struct dc_com_reg *com)
200 {
201         int i;
202
203         for (i = 0; i < PIN_REG_COUNT; i++) {
204                 writel(rgb_enb_tab[i], &com->pin_output_enb[i]);
205                 writel(rgb_polarity_tab[i], &com->pin_output_polarity[i]);
206                 writel(rgb_data_tab[i], &com->pin_output_data[i]);
207         }
208
209         for (i = 0; i < PIN_OUTPUT_SEL_COUNT; i++)
210                 writel(rgb_sel_tab[i], &com->pin_output_sel[i]);
211 }
212
213 int setup_window(struct disp_ctl_win *win, struct fdt_disp_config *config)
214 {
215         win->x = 0;
216         win->y = 0;
217         win->w = config->width;
218         win->h = config->height;
219         win->out_x = 0;
220         win->out_y = 0;
221         win->out_w = config->width;
222         win->out_h = config->height;
223         win->phys_addr = config->frame_buffer;
224         win->stride = config->width * (1 << config->log2_bpp) / 8;
225         debug("%s: depth = %d\n", __func__, config->log2_bpp);
226         switch (config->log2_bpp) {
227         case 5:
228         case 24:
229                 win->fmt = COLOR_DEPTH_R8G8B8A8;
230                 win->bpp = 32;
231                 break;
232         case 4:
233                 win->fmt = COLOR_DEPTH_B5G6R5;
234                 win->bpp = 16;
235                 break;
236
237         default:
238                 debug("Unsupported LCD bit depth");
239                 return -1;
240         }
241
242         return 0;
243 }
244
245 struct fdt_disp_config *tegra_display_get_config(void)
246 {
247         return config.valid ? &config : NULL;
248 }
249
250 static void debug_timing(const char *name, unsigned int timing[])
251 {
252 #ifdef DEBUG
253         int i;
254
255         debug("%s timing: ", name);
256         for (i = 0; i < FDT_LCD_TIMING_COUNT; i++)
257                 debug("%d ", timing[i]);
258         debug("\n");
259 #endif
260 }
261
262 /**
263  * Decode panel information from the fdt, according to a standard binding
264  *
265  * @param blob          fdt blob
266  * @param node          offset of fdt node to read from
267  * @param config        structure to store fdt config into
268  * @return 0 if ok, -ve on error
269  */
270 static int tegra_decode_panel(const void *blob, int node,
271                               struct fdt_disp_config *config)
272 {
273         int front, back, ref;
274
275         config->width = fdtdec_get_int(blob, node, "xres", -1);
276         config->height = fdtdec_get_int(blob, node, "yres", -1);
277         config->pixel_clock = fdtdec_get_int(blob, node, "clock", 0);
278         if (!config->pixel_clock || config->width == -1 ||
279                         config->height == -1) {
280                 debug("%s: Pixel parameters missing\n", __func__);
281                 return -FDT_ERR_NOTFOUND;
282         }
283
284         back = fdtdec_get_int(blob, node, "left-margin", -1);
285         front = fdtdec_get_int(blob, node, "right-margin", -1);
286         ref = fdtdec_get_int(blob, node, "hsync-len", -1);
287         if ((back | front | ref) == -1) {
288                 debug("%s: Horizontal parameters missing\n", __func__);
289                 return -FDT_ERR_NOTFOUND;
290         }
291
292         /* Use a ref-to-sync of 1 always, and take this from the front porch */
293         config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1;
294         config->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref;
295         config->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back;
296         config->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front -
297                 config->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC];
298         debug_timing("horiz", config->horiz_timing);
299
300         back = fdtdec_get_int(blob, node, "upper-margin", -1);
301         front = fdtdec_get_int(blob, node, "lower-margin", -1);
302         ref = fdtdec_get_int(blob, node, "vsync-len", -1);
303         if ((back | front | ref) == -1) {
304                 debug("%s: Vertical parameters missing\n", __func__);
305                 return -FDT_ERR_NOTFOUND;
306         }
307
308         config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1;
309         config->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref;
310         config->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back;
311         config->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front -
312                 config->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC];
313         debug_timing("vert", config->vert_timing);
314
315         return 0;
316 }
317
318 /**
319  * Decode the display controller information from the fdt.
320  *
321  * @param blob          fdt blob
322  * @param config        structure to store fdt config into
323  * @return 0 if ok, -ve on error
324  */
325 static int tegra_display_decode_config(const void *blob,
326                                        struct fdt_disp_config *config)
327 {
328         int node, rgb;
329         int bpp, bit;
330
331         /* TODO: Support multiple controllers */
332         node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA20_DC);
333         if (node < 0) {
334                 debug("%s: Cannot find display controller node in fdt\n",
335                       __func__);
336                 return node;
337         }
338         config->disp = (struct disp_ctlr *)fdtdec_get_addr(blob, node, "reg");
339         if (!config->disp) {
340                 debug("%s: No display controller address\n", __func__);
341                 return -1;
342         }
343
344         rgb = fdt_subnode_offset(blob, node, "rgb");
345
346         config->panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel");
347         if (!config->panel_node < 0) {
348                 debug("%s: Cannot find panel information\n", __func__);
349                 return -1;
350         }
351
352         if (tegra_decode_panel(blob, config->panel_node, config)) {
353                 debug("%s: Failed to decode panel information\n", __func__);
354                 return -1;
355         }
356
357         bpp = fdtdec_get_int(blob, config->panel_node, "nvidia,bits-per-pixel",
358                              -1);
359         bit = ffs(bpp) - 1;
360         if (bpp == (1 << bit))
361                 config->log2_bpp = bit;
362         else
363                 config->log2_bpp = bpp;
364         if (bpp == -1) {
365                 debug("%s: Pixel bpp parameters missing\n", __func__);
366                 return -FDT_ERR_NOTFOUND;
367         }
368         config->bpp = bpp;
369
370         config->valid = 1;      /* we have a valid configuration */
371
372         return 0;
373 }
374
375 int tegra_display_probe(const void *blob, void *default_lcd_base)
376 {
377         struct disp_ctl_win window;
378         struct dc_ctlr *dc;
379
380         if (tegra_display_decode_config(blob, &config))
381                 return -1;
382
383         config.frame_buffer = (u32)default_lcd_base;
384
385         dc = (struct dc_ctlr *)config.disp;
386
387         /*
388          * A header file for clock constants was NAKed upstream.
389          * TODO: Put this into the FDT and fdt_lcd struct when we have clock
390          * support there
391          */
392         clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH,
393                                144 * 1000000);
394         clock_start_periph_pll(PERIPH_ID_DISP1, CLOCK_ID_CGENERAL,
395                                600 * 1000000);
396         basic_init(&dc->cmd);
397         basic_init_timer(&dc->disp);
398         rgb_enable(&dc->com);
399
400         if (config.pixel_clock)
401                 update_display_mode(&dc->disp, &config);
402
403         if (setup_window(&window, &config))
404                 return -1;
405
406         update_window(dc, &window);
407
408         return 0;
409 }