1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun * Copyright 2014 Google Inc.
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * SPDX-License-Identifier: GPL-2.0+
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Extracted from Chromium coreboot commit 3f59b13d
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include <common.h>
10*4882a593Smuzhiyun #include <dm.h>
11*4882a593Smuzhiyun #include <edid.h>
12*4882a593Smuzhiyun #include <errno.h>
13*4882a593Smuzhiyun #include <display.h>
14*4882a593Smuzhiyun #include <edid.h>
15*4882a593Smuzhiyun #include <lcd.h>
16*4882a593Smuzhiyun #include <video.h>
17*4882a593Smuzhiyun #include <asm/gpio.h>
18*4882a593Smuzhiyun #include <asm/io.h>
19*4882a593Smuzhiyun #include <asm/arch/clock.h>
20*4882a593Smuzhiyun #include <asm/arch/pwm.h>
21*4882a593Smuzhiyun #include <asm/arch-tegra/dc.h>
22*4882a593Smuzhiyun #include <dm/uclass-internal.h>
23*4882a593Smuzhiyun #include "displayport.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun DECLARE_GLOBAL_DATA_PTR;
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun /* return in 1000ths of a Hertz */
tegra_dc_calc_refresh(const struct display_timing * timing)28*4882a593Smuzhiyun static int tegra_dc_calc_refresh(const struct display_timing *timing)
29*4882a593Smuzhiyun {
30*4882a593Smuzhiyun int h_total, v_total, refresh;
31*4882a593Smuzhiyun int pclk = timing->pixelclock.typ;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun h_total = timing->hactive.typ + timing->hfront_porch.typ +
34*4882a593Smuzhiyun timing->hback_porch.typ + timing->hsync_len.typ;
35*4882a593Smuzhiyun v_total = timing->vactive.typ + timing->vfront_porch.typ +
36*4882a593Smuzhiyun timing->vback_porch.typ + timing->vsync_len.typ;
37*4882a593Smuzhiyun if (!pclk || !h_total || !v_total)
38*4882a593Smuzhiyun return 0;
39*4882a593Smuzhiyun refresh = pclk / h_total;
40*4882a593Smuzhiyun refresh *= 1000;
41*4882a593Smuzhiyun refresh /= v_total;
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun return refresh;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
print_mode(const struct display_timing * timing)46*4882a593Smuzhiyun static void print_mode(const struct display_timing *timing)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun int refresh = tegra_dc_calc_refresh(timing);
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun debug("MODE:%dx%d@%d.%03uHz pclk=%d\n",
51*4882a593Smuzhiyun timing->hactive.typ, timing->vactive.typ, refresh / 1000,
52*4882a593Smuzhiyun refresh % 1000, timing->pixelclock.typ);
53*4882a593Smuzhiyun }
54*4882a593Smuzhiyun
update_display_mode(struct dc_ctlr * disp_ctrl,const struct display_timing * timing,int href_to_sync,int vref_to_sync)55*4882a593Smuzhiyun static int update_display_mode(struct dc_ctlr *disp_ctrl,
56*4882a593Smuzhiyun const struct display_timing *timing,
57*4882a593Smuzhiyun int href_to_sync, int vref_to_sync)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun print_mode(timing);
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun writel(0x1, &disp_ctrl->disp.disp_timing_opt);
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun writel(vref_to_sync << 16 | href_to_sync,
64*4882a593Smuzhiyun &disp_ctrl->disp.ref_to_sync);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun writel(timing->vsync_len.typ << 16 | timing->hsync_len.typ,
67*4882a593Smuzhiyun &disp_ctrl->disp.sync_width);
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun writel(((timing->vback_porch.typ - vref_to_sync) << 16) |
70*4882a593Smuzhiyun timing->hback_porch.typ, &disp_ctrl->disp.back_porch);
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun writel(((timing->vfront_porch.typ + vref_to_sync) << 16) |
73*4882a593Smuzhiyun timing->hfront_porch.typ, &disp_ctrl->disp.front_porch);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun writel(timing->hactive.typ | (timing->vactive.typ << 16),
76*4882a593Smuzhiyun &disp_ctrl->disp.disp_active);
77*4882a593Smuzhiyun
78*4882a593Smuzhiyun /**
79*4882a593Smuzhiyun * We want to use PLLD_out0, which is PLLD / 2:
80*4882a593Smuzhiyun * PixelClock = (PLLD / 2) / ShiftClockDiv / PixelClockDiv.
81*4882a593Smuzhiyun *
82*4882a593Smuzhiyun * Currently most panels work inside clock range 50MHz~100MHz, and PLLD
83*4882a593Smuzhiyun * has some requirements to have VCO in range 500MHz~1000MHz (see
84*4882a593Smuzhiyun * clock.c for more detail). To simplify calculation, we set
85*4882a593Smuzhiyun * PixelClockDiv to 1 and ShiftClockDiv to 1. In future these values
86*4882a593Smuzhiyun * may be calculated by clock_display, to allow wider frequency range.
87*4882a593Smuzhiyun *
88*4882a593Smuzhiyun * Note ShiftClockDiv is a 7.1 format value.
89*4882a593Smuzhiyun */
90*4882a593Smuzhiyun const u32 shift_clock_div = 1;
91*4882a593Smuzhiyun writel((PIXEL_CLK_DIVIDER_PCD1 << PIXEL_CLK_DIVIDER_SHIFT) |
92*4882a593Smuzhiyun ((shift_clock_div - 1) * 2) << SHIFT_CLK_DIVIDER_SHIFT,
93*4882a593Smuzhiyun &disp_ctrl->disp.disp_clk_ctrl);
94*4882a593Smuzhiyun debug("%s: PixelClock=%u, ShiftClockDiv=%u\n", __func__,
95*4882a593Smuzhiyun timing->pixelclock.typ, shift_clock_div);
96*4882a593Smuzhiyun return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun
tegra_dc_poll_register(void * reg,u32 mask,u32 exp_val,u32 poll_interval_us,u32 timeout_us)99*4882a593Smuzhiyun static u32 tegra_dc_poll_register(void *reg,
100*4882a593Smuzhiyun u32 mask, u32 exp_val, u32 poll_interval_us, u32 timeout_us)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun u32 temp = timeout_us;
103*4882a593Smuzhiyun u32 reg_val = 0;
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun do {
106*4882a593Smuzhiyun udelay(poll_interval_us);
107*4882a593Smuzhiyun reg_val = readl(reg);
108*4882a593Smuzhiyun if (timeout_us > poll_interval_us)
109*4882a593Smuzhiyun timeout_us -= poll_interval_us;
110*4882a593Smuzhiyun else
111*4882a593Smuzhiyun break;
112*4882a593Smuzhiyun } while ((reg_val & mask) != exp_val);
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun if ((reg_val & mask) == exp_val)
115*4882a593Smuzhiyun return 0; /* success */
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun return temp;
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun
tegra_dc_sor_general_act(struct dc_ctlr * disp_ctrl)120*4882a593Smuzhiyun int tegra_dc_sor_general_act(struct dc_ctlr *disp_ctrl)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun if (tegra_dc_poll_register(&disp_ctrl->cmd.state_ctrl,
125*4882a593Smuzhiyun GENERAL_ACT_REQ, 0, 100,
126*4882a593Smuzhiyun DC_POLL_TIMEOUT_MS * 1000)) {
127*4882a593Smuzhiyun debug("dc timeout waiting for DC to stop\n");
128*4882a593Smuzhiyun return -ETIMEDOUT;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun return 0;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun static struct display_timing min_mode = {
135*4882a593Smuzhiyun .hsync_len = { .typ = 1 },
136*4882a593Smuzhiyun .vsync_len = { .typ = 1 },
137*4882a593Smuzhiyun .hback_porch = { .typ = 20 },
138*4882a593Smuzhiyun .vback_porch = { .typ = 0 },
139*4882a593Smuzhiyun .hactive = { .typ = 16 },
140*4882a593Smuzhiyun .vactive = { .typ = 16 },
141*4882a593Smuzhiyun .hfront_porch = { .typ = 1 },
142*4882a593Smuzhiyun .vfront_porch = { .typ = 2 },
143*4882a593Smuzhiyun };
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /* Disable windows and set minimum raster timings */
tegra_dc_sor_disable_win_short_raster(struct dc_ctlr * disp_ctrl,int * dc_reg_ctx)146*4882a593Smuzhiyun void tegra_dc_sor_disable_win_short_raster(struct dc_ctlr *disp_ctrl,
147*4882a593Smuzhiyun int *dc_reg_ctx)
148*4882a593Smuzhiyun {
149*4882a593Smuzhiyun const int href_to_sync = 0, vref_to_sync = 1;
150*4882a593Smuzhiyun int selected_windows, i;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun selected_windows = readl(&disp_ctrl->cmd.disp_win_header);
153*4882a593Smuzhiyun
154*4882a593Smuzhiyun /* Store and clear window options */
155*4882a593Smuzhiyun for (i = 0; i < DC_N_WINDOWS; ++i) {
156*4882a593Smuzhiyun writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header);
157*4882a593Smuzhiyun dc_reg_ctx[i] = readl(&disp_ctrl->win.win_opt);
158*4882a593Smuzhiyun writel(0, &disp_ctrl->win.win_opt);
159*4882a593Smuzhiyun writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl);
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun writel(selected_windows, &disp_ctrl->cmd.disp_win_header);
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun /* Store current raster timings and set minimum timings */
165*4882a593Smuzhiyun dc_reg_ctx[i++] = readl(&disp_ctrl->disp.ref_to_sync);
166*4882a593Smuzhiyun writel(href_to_sync | (vref_to_sync << 16),
167*4882a593Smuzhiyun &disp_ctrl->disp.ref_to_sync);
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun dc_reg_ctx[i++] = readl(&disp_ctrl->disp.sync_width);
170*4882a593Smuzhiyun writel(min_mode.hsync_len.typ | (min_mode.vsync_len.typ << 16),
171*4882a593Smuzhiyun &disp_ctrl->disp.sync_width);
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun dc_reg_ctx[i++] = readl(&disp_ctrl->disp.back_porch);
174*4882a593Smuzhiyun writel(min_mode.hback_porch.typ | (min_mode.vback_porch.typ << 16),
175*4882a593Smuzhiyun &disp_ctrl->disp.back_porch);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun dc_reg_ctx[i++] = readl(&disp_ctrl->disp.front_porch);
178*4882a593Smuzhiyun writel(min_mode.hfront_porch.typ | (min_mode.vfront_porch.typ << 16),
179*4882a593Smuzhiyun &disp_ctrl->disp.front_porch);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun dc_reg_ctx[i++] = readl(&disp_ctrl->disp.disp_active);
182*4882a593Smuzhiyun writel(min_mode.hactive.typ | (min_mode.vactive.typ << 16),
183*4882a593Smuzhiyun &disp_ctrl->disp.disp_active);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun writel(GENERAL_ACT_REQ, &disp_ctrl->cmd.state_ctrl);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun /* Restore previous windows status and raster timings */
tegra_dc_sor_restore_win_and_raster(struct dc_ctlr * disp_ctrl,int * dc_reg_ctx)189*4882a593Smuzhiyun void tegra_dc_sor_restore_win_and_raster(struct dc_ctlr *disp_ctrl,
190*4882a593Smuzhiyun int *dc_reg_ctx)
191*4882a593Smuzhiyun {
192*4882a593Smuzhiyun int selected_windows, i;
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun selected_windows = readl(&disp_ctrl->cmd.disp_win_header);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun for (i = 0; i < DC_N_WINDOWS; ++i) {
197*4882a593Smuzhiyun writel(WINDOW_A_SELECT << i, &disp_ctrl->cmd.disp_win_header);
198*4882a593Smuzhiyun writel(dc_reg_ctx[i], &disp_ctrl->win.win_opt);
199*4882a593Smuzhiyun writel(WIN_A_ACT_REQ << i, &disp_ctrl->cmd.state_ctrl);
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun
202*4882a593Smuzhiyun writel(selected_windows, &disp_ctrl->cmd.disp_win_header);
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun writel(dc_reg_ctx[i++], &disp_ctrl->disp.ref_to_sync);
205*4882a593Smuzhiyun writel(dc_reg_ctx[i++], &disp_ctrl->disp.sync_width);
206*4882a593Smuzhiyun writel(dc_reg_ctx[i++], &disp_ctrl->disp.back_porch);
207*4882a593Smuzhiyun writel(dc_reg_ctx[i++], &disp_ctrl->disp.front_porch);
208*4882a593Smuzhiyun writel(dc_reg_ctx[i++], &disp_ctrl->disp.disp_active);
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun writel(GENERAL_UPDATE, &disp_ctrl->cmd.state_ctrl);
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun
tegra_depth_for_bpp(int bpp)213*4882a593Smuzhiyun static int tegra_depth_for_bpp(int bpp)
214*4882a593Smuzhiyun {
215*4882a593Smuzhiyun switch (bpp) {
216*4882a593Smuzhiyun case 32:
217*4882a593Smuzhiyun return COLOR_DEPTH_R8G8B8A8;
218*4882a593Smuzhiyun case 16:
219*4882a593Smuzhiyun return COLOR_DEPTH_B5G6R5;
220*4882a593Smuzhiyun default:
221*4882a593Smuzhiyun debug("Unsupported LCD bit depth");
222*4882a593Smuzhiyun return -1;
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun }
225*4882a593Smuzhiyun
update_window(struct dc_ctlr * disp_ctrl,u32 frame_buffer,int fb_bits_per_pixel,const struct display_timing * timing)226*4882a593Smuzhiyun static int update_window(struct dc_ctlr *disp_ctrl,
227*4882a593Smuzhiyun u32 frame_buffer, int fb_bits_per_pixel,
228*4882a593Smuzhiyun const struct display_timing *timing)
229*4882a593Smuzhiyun {
230*4882a593Smuzhiyun const u32 colour_white = 0xffffff;
231*4882a593Smuzhiyun int colour_depth;
232*4882a593Smuzhiyun u32 val;
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
235*4882a593Smuzhiyun
236*4882a593Smuzhiyun writel(((timing->vactive.typ << 16) | timing->hactive.typ),
237*4882a593Smuzhiyun &disp_ctrl->win.size);
238*4882a593Smuzhiyun writel(((timing->vactive.typ << 16) |
239*4882a593Smuzhiyun (timing->hactive.typ * fb_bits_per_pixel / 8)),
240*4882a593Smuzhiyun &disp_ctrl->win.prescaled_size);
241*4882a593Smuzhiyun writel(((timing->hactive.typ * fb_bits_per_pixel / 8 + 31) /
242*4882a593Smuzhiyun 32 * 32), &disp_ctrl->win.line_stride);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun colour_depth = tegra_depth_for_bpp(fb_bits_per_pixel);
245*4882a593Smuzhiyun if (colour_depth == -1)
246*4882a593Smuzhiyun return -EINVAL;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun writel(colour_depth, &disp_ctrl->win.color_depth);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun writel(frame_buffer, &disp_ctrl->winbuf.start_addr);
251*4882a593Smuzhiyun writel(0x1000 << V_DDA_INC_SHIFT | 0x1000 << H_DDA_INC_SHIFT,
252*4882a593Smuzhiyun &disp_ctrl->win.dda_increment);
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun writel(colour_white, &disp_ctrl->disp.blend_background_color);
255*4882a593Smuzhiyun writel(CTRL_MODE_C_DISPLAY << CTRL_MODE_SHIFT,
256*4882a593Smuzhiyun &disp_ctrl->cmd.disp_cmd);
257*4882a593Smuzhiyun
258*4882a593Smuzhiyun writel(WRITE_MUX_ACTIVE, &disp_ctrl->cmd.state_access);
259*4882a593Smuzhiyun
260*4882a593Smuzhiyun val = GENERAL_ACT_REQ | WIN_A_ACT_REQ;
261*4882a593Smuzhiyun val |= GENERAL_UPDATE | WIN_A_UPDATE;
262*4882a593Smuzhiyun writel(val, &disp_ctrl->cmd.state_ctrl);
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun /* Enable win_a */
265*4882a593Smuzhiyun val = readl(&disp_ctrl->win.win_opt);
266*4882a593Smuzhiyun writel(val | WIN_ENABLE, &disp_ctrl->win.win_opt);
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun return 0;
269*4882a593Smuzhiyun }
270*4882a593Smuzhiyun
tegra_dc_init(struct dc_ctlr * disp_ctrl)271*4882a593Smuzhiyun static int tegra_dc_init(struct dc_ctlr *disp_ctrl)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun /* do not accept interrupts during initialization */
274*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->cmd.int_mask);
275*4882a593Smuzhiyun writel(WRITE_MUX_ASSEMBLY | READ_MUX_ASSEMBLY,
276*4882a593Smuzhiyun &disp_ctrl->cmd.state_access);
277*4882a593Smuzhiyun writel(WINDOW_A_SELECT, &disp_ctrl->cmd.disp_win_header);
278*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.win_opt);
279*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.byte_swap);
280*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.buffer_ctrl);
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.pos);
283*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.h_initial_dda);
284*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.v_initial_dda);
285*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.dda_increment);
286*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.dv_ctrl);
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun writel(0x01000000, &disp_ctrl->win.blend_layer_ctrl);
289*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.blend_match_select);
290*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.blend_nomatch_select);
291*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->win.blend_alpha_1bit);
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->winbuf.start_addr_hi);
294*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->winbuf.addr_h_offset);
295*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->winbuf.addr_v_offset);
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->com.crc_checksum);
298*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->com.pin_output_enb[0]);
299*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->com.pin_output_enb[1]);
300*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->com.pin_output_enb[2]);
301*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->com.pin_output_enb[3]);
302*4882a593Smuzhiyun writel(0x00000000, &disp_ctrl->disp.disp_signal_opt0);
303*4882a593Smuzhiyun
304*4882a593Smuzhiyun return 0;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
dump_config(int panel_bpp,struct display_timing * timing)307*4882a593Smuzhiyun static void dump_config(int panel_bpp, struct display_timing *timing)
308*4882a593Smuzhiyun {
309*4882a593Smuzhiyun printf("timing->hactive.typ = %d\n", timing->hactive.typ);
310*4882a593Smuzhiyun printf("timing->vactive.typ = %d\n", timing->vactive.typ);
311*4882a593Smuzhiyun printf("timing->pixelclock.typ = %d\n", timing->pixelclock.typ);
312*4882a593Smuzhiyun
313*4882a593Smuzhiyun printf("timing->hfront_porch.typ = %d\n", timing->hfront_porch.typ);
314*4882a593Smuzhiyun printf("timing->hsync_len.typ = %d\n", timing->hsync_len.typ);
315*4882a593Smuzhiyun printf("timing->hback_porch.typ = %d\n", timing->hback_porch.typ);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun printf("timing->vfront_porch.typ %d\n", timing->vfront_porch.typ);
318*4882a593Smuzhiyun printf("timing->vsync_len.typ = %d\n", timing->vsync_len.typ);
319*4882a593Smuzhiyun printf("timing->vback_porch.typ = %d\n", timing->vback_porch.typ);
320*4882a593Smuzhiyun
321*4882a593Smuzhiyun printf("panel_bits_per_pixel = %d\n", panel_bpp);
322*4882a593Smuzhiyun }
323*4882a593Smuzhiyun
display_update_config_from_edid(struct udevice * dp_dev,int * panel_bppp,struct display_timing * timing)324*4882a593Smuzhiyun static int display_update_config_from_edid(struct udevice *dp_dev,
325*4882a593Smuzhiyun int *panel_bppp,
326*4882a593Smuzhiyun struct display_timing *timing)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun return display_read_timing(dp_dev, timing);
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
display_init(struct udevice * dev,void * lcdbase,int fb_bits_per_pixel,struct display_timing * timing)331*4882a593Smuzhiyun static int display_init(struct udevice *dev, void *lcdbase,
332*4882a593Smuzhiyun int fb_bits_per_pixel, struct display_timing *timing)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun struct display_plat *disp_uc_plat;
335*4882a593Smuzhiyun struct dc_ctlr *dc_ctlr;
336*4882a593Smuzhiyun struct udevice *dp_dev;
337*4882a593Smuzhiyun const int href_to_sync = 1, vref_to_sync = 1;
338*4882a593Smuzhiyun int panel_bpp = 18; /* default 18 bits per pixel */
339*4882a593Smuzhiyun u32 plld_rate;
340*4882a593Smuzhiyun int ret;
341*4882a593Smuzhiyun
342*4882a593Smuzhiyun /*
343*4882a593Smuzhiyun * Before we probe the display device (eDP), tell it that this device
344*4882a593Smuzhiyun * is the source of the display data.
345*4882a593Smuzhiyun */
346*4882a593Smuzhiyun ret = uclass_find_first_device(UCLASS_DISPLAY, &dp_dev);
347*4882a593Smuzhiyun if (ret) {
348*4882a593Smuzhiyun debug("%s: device '%s' display not found (ret=%d)\n", __func__,
349*4882a593Smuzhiyun dev->name, ret);
350*4882a593Smuzhiyun return ret;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
353*4882a593Smuzhiyun disp_uc_plat = dev_get_uclass_platdata(dp_dev);
354*4882a593Smuzhiyun debug("Found device '%s', disp_uc_priv=%p\n", dp_dev->name,
355*4882a593Smuzhiyun disp_uc_plat);
356*4882a593Smuzhiyun disp_uc_plat->src_dev = dev;
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun ret = uclass_get_device(UCLASS_DISPLAY, 0, &dp_dev);
359*4882a593Smuzhiyun if (ret) {
360*4882a593Smuzhiyun debug("%s: Failed to probe eDP, ret=%d\n", __func__, ret);
361*4882a593Smuzhiyun return ret;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun dc_ctlr = (struct dc_ctlr *)dev_read_addr(dev);
365*4882a593Smuzhiyun if (ofnode_decode_display_timing(dev_ofnode(dev), 0, timing)) {
366*4882a593Smuzhiyun debug("%s: Failed to decode display timing\n", __func__);
367*4882a593Smuzhiyun return -EINVAL;
368*4882a593Smuzhiyun }
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun ret = display_update_config_from_edid(dp_dev, &panel_bpp, timing);
371*4882a593Smuzhiyun if (ret) {
372*4882a593Smuzhiyun debug("%s: Failed to decode EDID, using defaults\n", __func__);
373*4882a593Smuzhiyun dump_config(panel_bpp, timing);
374*4882a593Smuzhiyun }
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun /*
377*4882a593Smuzhiyun * The plld is programmed with the assumption of the SHIFT_CLK_DIVIDER
378*4882a593Smuzhiyun * and PIXEL_CLK_DIVIDER are zero (divide by 1). See the
379*4882a593Smuzhiyun * update_display_mode() for detail.
380*4882a593Smuzhiyun */
381*4882a593Smuzhiyun plld_rate = clock_set_display_rate(timing->pixelclock.typ * 2);
382*4882a593Smuzhiyun if (plld_rate == 0) {
383*4882a593Smuzhiyun printf("dc: clock init failed\n");
384*4882a593Smuzhiyun return -EIO;
385*4882a593Smuzhiyun } else if (plld_rate != timing->pixelclock.typ * 2) {
386*4882a593Smuzhiyun debug("dc: plld rounded to %u\n", plld_rate);
387*4882a593Smuzhiyun timing->pixelclock.typ = plld_rate / 2;
388*4882a593Smuzhiyun }
389*4882a593Smuzhiyun
390*4882a593Smuzhiyun /* Init dc */
391*4882a593Smuzhiyun ret = tegra_dc_init(dc_ctlr);
392*4882a593Smuzhiyun if (ret) {
393*4882a593Smuzhiyun debug("dc: init failed\n");
394*4882a593Smuzhiyun return ret;
395*4882a593Smuzhiyun }
396*4882a593Smuzhiyun
397*4882a593Smuzhiyun /* Configure dc mode */
398*4882a593Smuzhiyun ret = update_display_mode(dc_ctlr, timing, href_to_sync, vref_to_sync);
399*4882a593Smuzhiyun if (ret) {
400*4882a593Smuzhiyun debug("dc: failed to configure display mode\n");
401*4882a593Smuzhiyun return ret;
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun
404*4882a593Smuzhiyun /* Enable dp */
405*4882a593Smuzhiyun ret = display_enable(dp_dev, panel_bpp, timing);
406*4882a593Smuzhiyun if (ret) {
407*4882a593Smuzhiyun debug("dc: failed to enable display: ret=%d\n", ret);
408*4882a593Smuzhiyun return ret;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun
411*4882a593Smuzhiyun ret = update_window(dc_ctlr, (ulong)lcdbase, fb_bits_per_pixel, timing);
412*4882a593Smuzhiyun if (ret) {
413*4882a593Smuzhiyun debug("dc: failed to update window\n");
414*4882a593Smuzhiyun return ret;
415*4882a593Smuzhiyun }
416*4882a593Smuzhiyun debug("%s: ready\n", __func__);
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun return 0;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun
421*4882a593Smuzhiyun enum {
422*4882a593Smuzhiyun /* Maximum LCD size we support */
423*4882a593Smuzhiyun LCD_MAX_WIDTH = 1920,
424*4882a593Smuzhiyun LCD_MAX_HEIGHT = 1200,
425*4882a593Smuzhiyun LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
426*4882a593Smuzhiyun };
427*4882a593Smuzhiyun
tegra124_lcd_init(struct udevice * dev,void * lcdbase,enum video_log2_bpp l2bpp)428*4882a593Smuzhiyun static int tegra124_lcd_init(struct udevice *dev, void *lcdbase,
429*4882a593Smuzhiyun enum video_log2_bpp l2bpp)
430*4882a593Smuzhiyun {
431*4882a593Smuzhiyun struct video_priv *uc_priv = dev_get_uclass_priv(dev);
432*4882a593Smuzhiyun struct display_timing timing;
433*4882a593Smuzhiyun int ret;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun clock_set_up_plldp();
436*4882a593Smuzhiyun clock_start_periph_pll(PERIPH_ID_HOST1X, CLOCK_ID_PERIPH, 408000000);
437*4882a593Smuzhiyun
438*4882a593Smuzhiyun clock_enable(PERIPH_ID_HOST1X);
439*4882a593Smuzhiyun clock_enable(PERIPH_ID_DISP1);
440*4882a593Smuzhiyun clock_enable(PERIPH_ID_PWM);
441*4882a593Smuzhiyun clock_enable(PERIPH_ID_DPAUX);
442*4882a593Smuzhiyun clock_enable(PERIPH_ID_SOR0);
443*4882a593Smuzhiyun udelay(2);
444*4882a593Smuzhiyun
445*4882a593Smuzhiyun reset_set_enable(PERIPH_ID_HOST1X, 0);
446*4882a593Smuzhiyun reset_set_enable(PERIPH_ID_DISP1, 0);
447*4882a593Smuzhiyun reset_set_enable(PERIPH_ID_PWM, 0);
448*4882a593Smuzhiyun reset_set_enable(PERIPH_ID_DPAUX, 0);
449*4882a593Smuzhiyun reset_set_enable(PERIPH_ID_SOR0, 0);
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun ret = display_init(dev, lcdbase, 1 << l2bpp, &timing);
452*4882a593Smuzhiyun if (ret)
453*4882a593Smuzhiyun return ret;
454*4882a593Smuzhiyun
455*4882a593Smuzhiyun uc_priv->xsize = roundup(timing.hactive.typ, 16);
456*4882a593Smuzhiyun uc_priv->ysize = timing.vactive.typ;
457*4882a593Smuzhiyun uc_priv->bpix = l2bpp;
458*4882a593Smuzhiyun
459*4882a593Smuzhiyun video_set_flush_dcache(dev, 1);
460*4882a593Smuzhiyun debug("%s: done\n", __func__);
461*4882a593Smuzhiyun
462*4882a593Smuzhiyun return 0;
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun
tegra124_lcd_probe(struct udevice * dev)465*4882a593Smuzhiyun static int tegra124_lcd_probe(struct udevice *dev)
466*4882a593Smuzhiyun {
467*4882a593Smuzhiyun struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
468*4882a593Smuzhiyun ulong start;
469*4882a593Smuzhiyun int ret;
470*4882a593Smuzhiyun
471*4882a593Smuzhiyun start = get_timer(0);
472*4882a593Smuzhiyun bootstage_start(BOOTSTAGE_ID_ACCUM_LCD, "lcd");
473*4882a593Smuzhiyun ret = tegra124_lcd_init(dev, (void *)plat->base, VIDEO_BPP16);
474*4882a593Smuzhiyun bootstage_accum(BOOTSTAGE_ID_ACCUM_LCD);
475*4882a593Smuzhiyun debug("LCD init took %lu ms\n", get_timer(start));
476*4882a593Smuzhiyun if (ret)
477*4882a593Smuzhiyun printf("%s: Error %d\n", __func__, ret);
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun return 0;
480*4882a593Smuzhiyun }
481*4882a593Smuzhiyun
tegra124_lcd_bind(struct udevice * dev)482*4882a593Smuzhiyun static int tegra124_lcd_bind(struct udevice *dev)
483*4882a593Smuzhiyun {
484*4882a593Smuzhiyun struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
485*4882a593Smuzhiyun
486*4882a593Smuzhiyun uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
487*4882a593Smuzhiyun (1 << VIDEO_BPP16) / 8;
488*4882a593Smuzhiyun debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun return 0;
491*4882a593Smuzhiyun }
492*4882a593Smuzhiyun
493*4882a593Smuzhiyun static const struct udevice_id tegra124_lcd_ids[] = {
494*4882a593Smuzhiyun { .compatible = "nvidia,tegra124-dc" },
495*4882a593Smuzhiyun { }
496*4882a593Smuzhiyun };
497*4882a593Smuzhiyun
498*4882a593Smuzhiyun U_BOOT_DRIVER(tegra124_dc) = {
499*4882a593Smuzhiyun .name = "tegra124-dc",
500*4882a593Smuzhiyun .id = UCLASS_VIDEO,
501*4882a593Smuzhiyun .of_match = tegra124_lcd_ids,
502*4882a593Smuzhiyun .bind = tegra124_lcd_bind,
503*4882a593Smuzhiyun .probe = tegra124_lcd_probe,
504*4882a593Smuzhiyun };
505