]> git.ipfire.org Git - people/ms/u-boot.git/blame - arch/arm/cpu/armv7/tegra20/display.c
Licenses: introduce SPDX Unique Lincense Identifiers
[people/ms/u-boot.git] / arch / arm / cpu / armv7 / tegra20 / display.c
CommitLineData
87540de3
WN
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
33static struct fdt_disp_config config;
34
35static 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
91static 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
97static 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 */
141static 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
162static 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
168static const u32 rgb_enb_tab[PIN_REG_COUNT] = {
169 0x00000000,
170 0x00000000,
171 0x00000000,
172 0x00000000,
173};
174
175static const u32 rgb_polarity_tab[PIN_REG_COUNT] = {
176 0x00000000,
177 0x01000000,
178 0x00000000,
179 0x00000000,
180};
181
182static const u32 rgb_data_tab[PIN_REG_COUNT] = {
183 0x00000000,
184 0x00000000,
185 0x00000000,
186 0x00000000,
187};
188
189static 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
199static 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
213int 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
245struct fdt_disp_config *tegra_display_get_config(void)
246{
247 return config.valid ? &config : NULL;
248}
249
250static 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 */
270static 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 */
325static 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
375int 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}