blob: 4fc312ebeca0078e3d40e2e01c8f8564e2e31098 [file] [log] [blame]
Jimmy Zhange3a938d2014-09-15 16:50:36 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; version 2 of the License.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19#include <console/console.h>
20#include <arch/io.h>
21#include <stdint.h>
22#include <lib.h>
23#include <stdlib.h>
24#include <delay.h>
25#include <soc/addressmap.h>
26#include <device/device.h>
27#include <device/i2c.h>
28#include <string.h>
29#include <cpu/cpu.h>
30#include <boot/tables.h>
31#include <cbmem.h>
Jimmy Zhang443bd002014-11-03 17:38:29 -080032#include <edid.h>
Jimmy Zhange3a938d2014-09-15 16:50:36 -070033#include <soc/clock.h>
34#include <soc/nvidia/tegra/dc.h>
35#include <soc/funitcfg.h>
36#include "chip.h"
37#include <soc/display.h>
38
39int dump = 0;
40unsigned long READL(void * p)
41{
42 unsigned long value;
43
44 /*
45 * In case of hard hung on readl(p), we can set dump > 1 to print out
46 * the address accessed.
47 */
48 if (dump > 1)
49 printk(BIOS_SPEW, "readl %p\n", p);
50
51 value = readl(p);
52 if (dump)
53 printk(BIOS_SPEW, "readl %p %08lx\n", p, value);
54 return value;
55}
56
57void WRITEL(unsigned long value, void * p)
58{
59 if (dump)
60 printk(BIOS_SPEW, "writel %p %08lx\n", p, value);
61 writel(value, p);
62}
63
64/* return in 1000ths of a Hertz */
65static int tegra_calc_refresh(const struct soc_nvidia_tegra132_config *config)
66{
67 int refresh;
68 int h_total = htotal(config);
69 int v_total = vtotal(config);
70 int pclk = config->pixel_clock;
71
72 if (!pclk || !h_total || !v_total)
73 return 0;
74 refresh = pclk / h_total;
75 refresh *= 1000;
76 refresh /= v_total;
77 return refresh;
78}
79
80static void print_mode(const struct soc_nvidia_tegra132_config *config)
81{
82 if (config) {
83 int refresh = tegra_calc_refresh(config);
84 printk(BIOS_ERR,
Jimmy Zhang93147b52014-11-13 18:17:54 -080085 "Panel Mode: %dx%d@%d.%03uHz pclk=%d\n",
Jimmy Zhange3a938d2014-09-15 16:50:36 -070086 config->xres, config->yres,
87 refresh / 1000, refresh % 1000,
88 config->pixel_clock);
89 }
90}
91
92static int update_display_mode(struct display_controller *disp_ctrl,
93 struct soc_nvidia_tegra132_config *config)
94{
95 print_mode(config);
96
97 printk(BIOS_ERR, "config: xres:yres: %d x %d\n ",
98 config->xres, config->yres);
99 printk(BIOS_ERR, " href_sync:vref_sync: %d x %d\n ",
100 config->href_to_sync, config->vref_to_sync);
101 printk(BIOS_ERR, " hsyn_width:vsyn_width: %d x %d\n ",
102 config->hsync_width, config->vsync_width);
103 printk(BIOS_ERR, " hfnt_porch:vfnt_porch: %d x %d\n ",
104 config->hfront_porch, config->vfront_porch);
105 printk(BIOS_ERR, " hbk_porch:vbk_porch: %d x %d\n ",
106 config->hback_porch, config->vback_porch);
107
108 WRITEL(0x0, &disp_ctrl->disp.disp_timing_opt);
109 WRITEL(0x0, &disp_ctrl->disp.disp_color_ctrl);
110
111 // select DSI
112 WRITEL(DSI_ENABLE, &disp_ctrl->disp.disp_win_opt);
113
114 WRITEL(config->vref_to_sync << 16 | config->href_to_sync,
115 &disp_ctrl->disp.ref_to_sync);
116
117 WRITEL(config->vsync_width << 16 | config->hsync_width,
118 &disp_ctrl->disp.sync_width);
119
120
121 WRITEL((config->vback_porch << 16) | config->hback_porch,
122 &disp_ctrl->disp.back_porch);
123
124 WRITEL((config->vfront_porch << 16) | config->hfront_porch,
125 &disp_ctrl->disp.front_porch);
126
127 WRITEL(config->xres | (config->yres << 16),
128 &disp_ctrl->disp.disp_active);
129
130 /**
131 * We want to use PLLD_out0, which is PLLD / 2:
132 * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
133 *
134 * Currently most panels work inside clock range 50MHz~100MHz, and PLLD
135 * has some requirements to have VCO in range 500MHz~1000MHz (see
136 * clock.c for more detail). To simplify calculation, we set
137 * PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values
138 * may be calculated by clock_display, to allow wider frequency range.
139 *
140 * Note ShiftClockDiv is a 7.1 format value.
141 */
142 const u32 shift_clock_div = 1;
143 WRITEL((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
144 ((shift_clock_div - 1) * 2 + 1) << SHIFT_CLK_DIVIDER_SHIFT,
145 &disp_ctrl->disp.disp_clk_ctrl);
146 printk(BIOS_DEBUG, "%s: PixelClock=%u, ShiftClockDiv=%u\n",
147 __func__, config->pixel_clock, shift_clock_div);
148 return 0;
149}
150
151/*
152 * update_window:
153 * set up window registers and activate window except two:
154 * frame buffer base address register (WINBUF_START_ADDR) and
155 * display enable register (_DISP_DISP_WIN_OPTIONS). This is
156 * becasue framebuffer is not available until payload stage.
157 */
158static void update_window(const struct soc_nvidia_tegra132_config *config)
159{
160 struct display_controller *disp_ctrl =
161 (void *)config->display_controller;
162 u32 val;
163
164 WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
165
166 WRITEL(((config->yres << 16) | config->xres), &disp_ctrl->win.size);
Jimmy Zhang93147b52014-11-13 18:17:54 -0800167
168 WRITEL(((config->display_yres << 16) |
169 (config->display_xres *
170 config->framebuffer_bits_per_pixel / 8)),
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700171 &disp_ctrl->win.prescaled_size);
172
Jimmy Zhang93147b52014-11-13 18:17:54 -0800173 val = ALIGN_UP((config->display_xres *
174 config->framebuffer_bits_per_pixel / 8), 64);
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700175 WRITEL(val, &disp_ctrl->win.line_stride);
176
177 WRITEL(config->color_depth, &disp_ctrl->win.color_depth);
178 WRITEL(COLOR_BLACK, &disp_ctrl->disp.blend_background_color);
Jimmy Zhang93147b52014-11-13 18:17:54 -0800179
180 WRITEL(((DDA_INC(config->display_yres, config->yres) << 16) |
181 DDA_INC(config->display_xres, config->xres)),
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700182 &disp_ctrl->win.dda_increment);
183
184 WRITEL(DISP_CTRL_MODE_C_DISPLAY, &disp_ctrl->cmd.disp_cmd);
185
186 WRITEL(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
187
188 WRITEL(0, &disp_ctrl->win.buffer_addr_mode);
189
190 val = PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
191 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
192 WRITEL(val, &disp_ctrl->cmd.disp_pow_ctrl);
193
194 val = GENERAL_UPDATE | WIN_A_UPDATE;
195 val |= GENERAL_ACT_REQ | WIN_A_ACT_REQ;
196 WRITEL(val, &disp_ctrl->cmd.state_ctrl);
197}
198
199static int tegra_dc_init(struct display_controller *disp_ctrl)
200{
201 /* do not accept interrupts during initialization */
202 WRITEL(0x00000000, &disp_ctrl->cmd.int_mask);
203 WRITEL(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
204 &disp_ctrl->cmd.state_access);
205 WRITEL(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
206 WRITEL(0x00000000, &disp_ctrl->win.win_opt);
207 WRITEL(0x00000000, &disp_ctrl->win.byte_swap);
208 WRITEL(0x00000000, &disp_ctrl->win.buffer_ctrl);
209
210 WRITEL(0x00000000, &disp_ctrl->win.pos);
211 WRITEL(0x00000000, &disp_ctrl->win.h_initial_dda);
212 WRITEL(0x00000000, &disp_ctrl->win.v_initial_dda);
213 WRITEL(0x00000000, &disp_ctrl->win.dda_increment);
214 WRITEL(0x00000000, &disp_ctrl->win.dv_ctrl);
215
216 WRITEL(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
217 WRITEL(0x00000000, &disp_ctrl->win.blend_match_select);
218 WRITEL(0x00000000, &disp_ctrl->win.blend_nomatch_select);
219 WRITEL(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
220
221 WRITEL(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
222 WRITEL(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
223 WRITEL(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
224
225 WRITEL(0x00000000, &disp_ctrl->com.crc_checksum);
226 WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
227 WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
228 WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
229 WRITEL(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
230 WRITEL(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
231
232 return 0;
233}
234
235void display_startup(device_t dev)
236{
237 struct soc_nvidia_tegra132_config *config = dev->chip_info;
238 struct display_controller *disp_ctrl =
239 (void *)config->display_controller;
240 u32 plld_rate;
241
242 u32 framebuffer_size_mb = config->framebuffer_size / MiB;
243 u32 framebuffer_base_mb= config->framebuffer_base / MiB;
244
245 printk(BIOS_INFO, "%s: entry: disp_ctrl: %p.\n",
246 __func__, disp_ctrl);
247
248 if (disp_ctrl == NULL) {
249 printk(BIOS_ERR, "Error: No dc is assigned by dt.\n");
250 return;
251 }
252
253 if (framebuffer_size_mb == 0){
Jimmy Zhang93147b52014-11-13 18:17:54 -0800254 framebuffer_size_mb = ALIGN_UP(config->display_xres *
255 config->display_yres *
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700256 (config->framebuffer_bits_per_pixel / 8), MiB)/MiB;
257 }
258
259 config->framebuffer_size = framebuffer_size_mb * MiB;
260 config->framebuffer_base = framebuffer_base_mb * MiB;
261
262 /*
263 * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
264 * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
265 * update_display_mode() for detail.
266 */
267 /* set default plld */
268 plld_rate = clock_display(config->pixel_clock * 2);
269 if (plld_rate == 0) {
270 printk(BIOS_ERR, "dc: clock init failed\n");
271 return;
272 } else if (plld_rate != config->pixel_clock * 2) {
273 printk(BIOS_WARNING, "dc: plld rounded to %u\n", plld_rate);
274 }
275
276 /* set disp1's clock source to PLLD_OUT0 */
277 clock_configure_source(disp1, PLLD, (plld_rate/KHz)/2);
278
279 /* Init dc */
280 if (tegra_dc_init(disp_ctrl)) {
281 printk(BIOS_ERR, "dc: init failed\n");
282 return;
283 }
284
285 /* Configure dc mode */
286 if (update_display_mode(disp_ctrl, config)) {
287 printk(BIOS_ERR, "dc: failed to configure display mode.\n");
288 return;
289 }
290
291 /* Configure and enable dsi controller and panel */
292 if (dsi_enable(config)) {
293 printk(BIOS_ERR, "%s: failed to enable dsi controllers.\n",
294 __func__);
295 return;
296 }
297
Jimmy Zhang443bd002014-11-03 17:38:29 -0800298 /* Set up window */
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700299 update_window(config);
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700300 printk(BIOS_INFO, "%s: display init done.\n", __func__);
Jimmy Zhang443bd002014-11-03 17:38:29 -0800301
302 /*
303 * Pass panel information to cb tables
304 */
305 struct edid edid;
306 /* Align bytes_per_line to 64 bytes as required by dc */
Jimmy Zhang93147b52014-11-13 18:17:54 -0800307 edid.bytes_per_line = ALIGN_UP((config->display_xres *
Jimmy Zhang443bd002014-11-03 17:38:29 -0800308 config->framebuffer_bits_per_pixel / 8), 64);
309 edid.x_resolution = edid.bytes_per_line /
310 (config->framebuffer_bits_per_pixel / 8);
Jimmy Zhang93147b52014-11-13 18:17:54 -0800311 edid.y_resolution = config->display_yres;
Jimmy Zhang443bd002014-11-03 17:38:29 -0800312 edid.framebuffer_bits_per_pixel = config->framebuffer_bits_per_pixel;
313
314 printk(BIOS_INFO, "%s: bytes_per_line: %d, bits_per_pixel: %d\n "
315 " x_res x y_res: %d x %d, size: %d\n",
316 __func__, edid.bytes_per_line,
317 edid.framebuffer_bits_per_pixel,
318 edid.x_resolution, edid.y_resolution,
319 (edid.bytes_per_line * edid.y_resolution));
320
321 set_vbe_mode_info_valid(&edid, 0);
322
323 /*
324 * After this point, it is payload's responsibility to allocate
325 * framebuffer and sets the base address to dc's
326 * WINBUF_START_ADDR register and enables window by setting dc's
327 * DISP_DISP_WIN_OPTIONS register.
328 */
Jimmy Zhange3a938d2014-09-15 16:50:36 -0700329}
330