xref: /rk3399_rockchip-uboot/board/compulab/common/omap3_display.c (revision e1e55c13c965c1c2316cd1540c3e46e1c07fc4ee)
1*e1e55c13SIgor Grinberg /*
2*e1e55c13SIgor Grinberg  * (C) Copyright 2012 - 2013 CompuLab, Ltd. <www.compulab.co.il>
3*e1e55c13SIgor Grinberg  *
4*e1e55c13SIgor Grinberg  * Authors: Nikita Kiryanov <nikita@compulab.co.il>
5*e1e55c13SIgor Grinberg  *
6*e1e55c13SIgor Grinberg  * Parsing code based on linux/drivers/video/pxafb.c
7*e1e55c13SIgor Grinberg  *
8*e1e55c13SIgor Grinberg  * SPDX-License-Identifier:	GPL-2.0+
9*e1e55c13SIgor Grinberg  */
10*e1e55c13SIgor Grinberg 
11*e1e55c13SIgor Grinberg #include <common.h>
12*e1e55c13SIgor Grinberg #include <asm/gpio.h>
13*e1e55c13SIgor Grinberg #include <asm/io.h>
14*e1e55c13SIgor Grinberg #include <stdio_dev.h>
15*e1e55c13SIgor Grinberg #include <asm/arch/dss.h>
16*e1e55c13SIgor Grinberg #include <lcd.h>
17*e1e55c13SIgor Grinberg #include <asm/arch-omap3/dss.h>
18*e1e55c13SIgor Grinberg 
19*e1e55c13SIgor Grinberg DECLARE_GLOBAL_DATA_PTR;
20*e1e55c13SIgor Grinberg 
21*e1e55c13SIgor Grinberg enum display_type {
22*e1e55c13SIgor Grinberg 	NONE,
23*e1e55c13SIgor Grinberg 	DVI,
24*e1e55c13SIgor Grinberg 	DVI_CUSTOM,
25*e1e55c13SIgor Grinberg };
26*e1e55c13SIgor Grinberg 
27*e1e55c13SIgor Grinberg #define CMAP_ADDR	0x80100000
28*e1e55c13SIgor Grinberg 
29*e1e55c13SIgor Grinberg /*
30*e1e55c13SIgor Grinberg  * The frame buffer is allocated before we have the chance to parse user input.
31*e1e55c13SIgor Grinberg  * To make sure enough memory is allocated for all resolutions, we define
32*e1e55c13SIgor Grinberg  * vl_{col | row} to the maximal resolution supported by OMAP3.
33*e1e55c13SIgor Grinberg  */
34*e1e55c13SIgor Grinberg vidinfo_t panel_info = {
35*e1e55c13SIgor Grinberg 	.vl_col  = 1400,
36*e1e55c13SIgor Grinberg 	.vl_row  = 1050,
37*e1e55c13SIgor Grinberg 	.vl_bpix = LCD_BPP,
38*e1e55c13SIgor Grinberg 	.cmap = (ushort *)CMAP_ADDR,
39*e1e55c13SIgor Grinberg };
40*e1e55c13SIgor Grinberg 
41*e1e55c13SIgor Grinberg static struct panel_config panel_cfg;
42*e1e55c13SIgor Grinberg static enum display_type lcd_def;
43*e1e55c13SIgor Grinberg 
44*e1e55c13SIgor Grinberg /*
45*e1e55c13SIgor Grinberg  * A note on DVI presets;
46*e1e55c13SIgor Grinberg  * U-Boot can convert 8 bit BMP data to 16 bit BMP data, and OMAP DSS can
47*e1e55c13SIgor Grinberg  * convert 16 bit data into 24 bit data. Thus, GFXFORMAT_RGB16 allows us to
48*e1e55c13SIgor Grinberg  * support two BMP types with one setting.
49*e1e55c13SIgor Grinberg  */
50*e1e55c13SIgor Grinberg static const struct panel_config preset_dvi_640X480 = {
51*e1e55c13SIgor Grinberg 	.lcd_size	= PANEL_LCD_SIZE(640, 480),
52*e1e55c13SIgor Grinberg 	.timing_h	= DSS_HBP(48) | DSS_HFP(16) | DSS_HSW(96),
53*e1e55c13SIgor Grinberg 	.timing_v	= DSS_VBP(33) | DSS_VFP(10) | DSS_VSW(2),
54*e1e55c13SIgor Grinberg 	.divisor	= 12 | (1 << 16),
55*e1e55c13SIgor Grinberg 	.data_lines	= LCD_INTERFACE_24_BIT,
56*e1e55c13SIgor Grinberg 	.panel_type	= ACTIVE_DISPLAY,
57*e1e55c13SIgor Grinberg 	.load_mode	= 2,
58*e1e55c13SIgor Grinberg 	.gfx_format	= GFXFORMAT_RGB16,
59*e1e55c13SIgor Grinberg };
60*e1e55c13SIgor Grinberg 
61*e1e55c13SIgor Grinberg static const struct panel_config preset_dvi_800X600 = {
62*e1e55c13SIgor Grinberg 	.lcd_size	= PANEL_LCD_SIZE(800, 600),
63*e1e55c13SIgor Grinberg 	.timing_h	= DSS_HBP(88) | DSS_HFP(40) | DSS_HSW(128),
64*e1e55c13SIgor Grinberg 	.timing_v	= DSS_VBP(23) | DSS_VFP(1) | DSS_VSW(4),
65*e1e55c13SIgor Grinberg 	.divisor	= 8 | (1 << 16),
66*e1e55c13SIgor Grinberg 	.data_lines	= LCD_INTERFACE_24_BIT,
67*e1e55c13SIgor Grinberg 	.panel_type	= ACTIVE_DISPLAY,
68*e1e55c13SIgor Grinberg 	.load_mode	= 2,
69*e1e55c13SIgor Grinberg 	.gfx_format	= GFXFORMAT_RGB16,
70*e1e55c13SIgor Grinberg };
71*e1e55c13SIgor Grinberg 
72*e1e55c13SIgor Grinberg static const struct panel_config preset_dvi_1024X768 = {
73*e1e55c13SIgor Grinberg 	.lcd_size	= PANEL_LCD_SIZE(1024, 768),
74*e1e55c13SIgor Grinberg 	.timing_h	= DSS_HBP(160) | DSS_HFP(24) | DSS_HSW(136),
75*e1e55c13SIgor Grinberg 	.timing_v	= DSS_VBP(29) | DSS_VFP(3) | DSS_VSW(6),
76*e1e55c13SIgor Grinberg 	.divisor	= 5 | (1 << 16),
77*e1e55c13SIgor Grinberg 	.data_lines	= LCD_INTERFACE_24_BIT,
78*e1e55c13SIgor Grinberg 	.panel_type	= ACTIVE_DISPLAY,
79*e1e55c13SIgor Grinberg 	.load_mode	= 2,
80*e1e55c13SIgor Grinberg 	.gfx_format	= GFXFORMAT_RGB16,
81*e1e55c13SIgor Grinberg };
82*e1e55c13SIgor Grinberg 
83*e1e55c13SIgor Grinberg static const struct panel_config preset_dvi_1152X864 = {
84*e1e55c13SIgor Grinberg 	.lcd_size	= PANEL_LCD_SIZE(1152, 864),
85*e1e55c13SIgor Grinberg 	.timing_h	= DSS_HBP(256) | DSS_HFP(64) | DSS_HSW(128),
86*e1e55c13SIgor Grinberg 	.timing_v	= DSS_VBP(32) | DSS_VFP(1) | DSS_VSW(3),
87*e1e55c13SIgor Grinberg 	.divisor	= 3 | (1 << 16),
88*e1e55c13SIgor Grinberg 	.data_lines	= LCD_INTERFACE_24_BIT,
89*e1e55c13SIgor Grinberg 	.panel_type	= ACTIVE_DISPLAY,
90*e1e55c13SIgor Grinberg 	.load_mode	= 2,
91*e1e55c13SIgor Grinberg 	.gfx_format	= GFXFORMAT_RGB16,
92*e1e55c13SIgor Grinberg };
93*e1e55c13SIgor Grinberg 
94*e1e55c13SIgor Grinberg static const struct panel_config preset_dvi_1280X960 = {
95*e1e55c13SIgor Grinberg 	.lcd_size	= PANEL_LCD_SIZE(1280, 960),
96*e1e55c13SIgor Grinberg 	.timing_h	= DSS_HBP(312) | DSS_HFP(96) | DSS_HSW(112),
97*e1e55c13SIgor Grinberg 	.timing_v	= DSS_VBP(36) | DSS_VFP(1) | DSS_VSW(3),
98*e1e55c13SIgor Grinberg 	.divisor	= 3 | (1 << 16),
99*e1e55c13SIgor Grinberg 	.data_lines	= LCD_INTERFACE_24_BIT,
100*e1e55c13SIgor Grinberg 	.panel_type	= ACTIVE_DISPLAY,
101*e1e55c13SIgor Grinberg 	.load_mode	= 2,
102*e1e55c13SIgor Grinberg 	.gfx_format	= GFXFORMAT_RGB16,
103*e1e55c13SIgor Grinberg };
104*e1e55c13SIgor Grinberg 
105*e1e55c13SIgor Grinberg static const struct panel_config preset_dvi_1280X1024 = {
106*e1e55c13SIgor Grinberg 	.lcd_size	= PANEL_LCD_SIZE(1280, 1024),
107*e1e55c13SIgor Grinberg 	.timing_h	= DSS_HBP(248) | DSS_HFP(48) | DSS_HSW(112),
108*e1e55c13SIgor Grinberg 	.timing_v	= DSS_VBP(38) | DSS_VFP(1) | DSS_VSW(3),
109*e1e55c13SIgor Grinberg 	.divisor	= 3 | (1 << 16),
110*e1e55c13SIgor Grinberg 	.data_lines	= LCD_INTERFACE_24_BIT,
111*e1e55c13SIgor Grinberg 	.panel_type	= ACTIVE_DISPLAY,
112*e1e55c13SIgor Grinberg 	.load_mode	= 2,
113*e1e55c13SIgor Grinberg 	.gfx_format	= GFXFORMAT_RGB16,
114*e1e55c13SIgor Grinberg };
115*e1e55c13SIgor Grinberg 
116*e1e55c13SIgor Grinberg /*
117*e1e55c13SIgor Grinberg  * set_resolution_params()
118*e1e55c13SIgor Grinberg  *
119*e1e55c13SIgor Grinberg  * Due to usage of multiple display related APIs resolution data is located in
120*e1e55c13SIgor Grinberg  * more than one place. This function updates them all.
121*e1e55c13SIgor Grinberg  */
122*e1e55c13SIgor Grinberg static void set_resolution_params(int x, int y)
123*e1e55c13SIgor Grinberg {
124*e1e55c13SIgor Grinberg 	panel_cfg.lcd_size = PANEL_LCD_SIZE(x, y);
125*e1e55c13SIgor Grinberg 	panel_info.vl_col = x;
126*e1e55c13SIgor Grinberg 	panel_info.vl_row = y;
127*e1e55c13SIgor Grinberg 	lcd_line_length = (panel_info.vl_col * NBITS(panel_info.vl_bpix)) / 8;
128*e1e55c13SIgor Grinberg }
129*e1e55c13SIgor Grinberg 
130*e1e55c13SIgor Grinberg static void set_preset(const struct panel_config preset, int x_res, int y_res)
131*e1e55c13SIgor Grinberg {
132*e1e55c13SIgor Grinberg 	panel_cfg = preset;
133*e1e55c13SIgor Grinberg 	set_resolution_params(x_res, y_res);
134*e1e55c13SIgor Grinberg }
135*e1e55c13SIgor Grinberg 
136*e1e55c13SIgor Grinberg static enum display_type set_dvi_preset(const struct panel_config preset,
137*e1e55c13SIgor Grinberg 					int x_res, int y_res)
138*e1e55c13SIgor Grinberg {
139*e1e55c13SIgor Grinberg 	set_preset(preset, x_res, y_res);
140*e1e55c13SIgor Grinberg 	return DVI;
141*e1e55c13SIgor Grinberg }
142*e1e55c13SIgor Grinberg 
143*e1e55c13SIgor Grinberg /*
144*e1e55c13SIgor Grinberg  * parse_mode() - parse the mode parameter of custom lcd settings
145*e1e55c13SIgor Grinberg  *
146*e1e55c13SIgor Grinberg  * @mode:	<res_x>x<res_y>
147*e1e55c13SIgor Grinberg  *
148*e1e55c13SIgor Grinberg  * Returns -1 on error, 0 on success.
149*e1e55c13SIgor Grinberg  */
150*e1e55c13SIgor Grinberg static int parse_mode(const char *mode)
151*e1e55c13SIgor Grinberg {
152*e1e55c13SIgor Grinberg 	unsigned int modelen = strlen(mode);
153*e1e55c13SIgor Grinberg 	int res_specified = 0;
154*e1e55c13SIgor Grinberg 	unsigned int xres = 0, yres = 0;
155*e1e55c13SIgor Grinberg 	int yres_specified = 0;
156*e1e55c13SIgor Grinberg 	int i;
157*e1e55c13SIgor Grinberg 
158*e1e55c13SIgor Grinberg 	for (i = modelen - 1; i >= 0; i--) {
159*e1e55c13SIgor Grinberg 		switch (mode[i]) {
160*e1e55c13SIgor Grinberg 		case 'x':
161*e1e55c13SIgor Grinberg 			if (!yres_specified) {
162*e1e55c13SIgor Grinberg 				yres = simple_strtoul(&mode[i + 1], NULL, 0);
163*e1e55c13SIgor Grinberg 				yres_specified = 1;
164*e1e55c13SIgor Grinberg 			} else {
165*e1e55c13SIgor Grinberg 				goto done_parsing;
166*e1e55c13SIgor Grinberg 			}
167*e1e55c13SIgor Grinberg 
168*e1e55c13SIgor Grinberg 			break;
169*e1e55c13SIgor Grinberg 		case '0' ... '9':
170*e1e55c13SIgor Grinberg 			break;
171*e1e55c13SIgor Grinberg 		default:
172*e1e55c13SIgor Grinberg 			goto done_parsing;
173*e1e55c13SIgor Grinberg 		}
174*e1e55c13SIgor Grinberg 	}
175*e1e55c13SIgor Grinberg 
176*e1e55c13SIgor Grinberg 	if (i < 0 && yres_specified) {
177*e1e55c13SIgor Grinberg 		xres = simple_strtoul(mode, NULL, 0);
178*e1e55c13SIgor Grinberg 		res_specified = 1;
179*e1e55c13SIgor Grinberg 	}
180*e1e55c13SIgor Grinberg 
181*e1e55c13SIgor Grinberg done_parsing:
182*e1e55c13SIgor Grinberg 	if (res_specified) {
183*e1e55c13SIgor Grinberg 		set_resolution_params(xres, yres);
184*e1e55c13SIgor Grinberg 	} else {
185*e1e55c13SIgor Grinberg 		printf("LCD: invalid mode: %s\n", mode);
186*e1e55c13SIgor Grinberg 		return -1;
187*e1e55c13SIgor Grinberg 	}
188*e1e55c13SIgor Grinberg 
189*e1e55c13SIgor Grinberg 	return 0;
190*e1e55c13SIgor Grinberg }
191*e1e55c13SIgor Grinberg 
192*e1e55c13SIgor Grinberg #define PIXEL_CLK_NUMERATOR (26 * 432 / 39)
193*e1e55c13SIgor Grinberg /*
194*e1e55c13SIgor Grinberg  * parse_pixclock() - Parse the pixclock parameter of custom lcd settings
195*e1e55c13SIgor Grinberg  *
196*e1e55c13SIgor Grinberg  * @pixclock:	the desired pixel clock
197*e1e55c13SIgor Grinberg  *
198*e1e55c13SIgor Grinberg  * Returns -1 on error, 0 on success.
199*e1e55c13SIgor Grinberg  *
200*e1e55c13SIgor Grinberg  * Handling the pixel_clock:
201*e1e55c13SIgor Grinberg  *
202*e1e55c13SIgor Grinberg  * Pixel clock is defined in the OMAP35x TRM as follows:
203*e1e55c13SIgor Grinberg  * pixel_clock =
204*e1e55c13SIgor Grinberg  * (SYS_CLK * 2 * PRCM.CM_CLKSEL2_PLL[18:8]) /
205*e1e55c13SIgor Grinberg  * (DSS.DISPC_DIVISOR[23:16] * DSS.DISPC_DIVISOR[6:0] *
206*e1e55c13SIgor Grinberg  * PRCM.CM_CLKSEL_DSS[4:0] * (PRCM.CM_CLKSEL2_PLL[6:0] + 1))
207*e1e55c13SIgor Grinberg  *
208*e1e55c13SIgor Grinberg  * In practice, this means that in order to set the
209*e1e55c13SIgor Grinberg  * divisor for the desired pixel clock one needs to
210*e1e55c13SIgor Grinberg  * solve the following equation:
211*e1e55c13SIgor Grinberg  *
212*e1e55c13SIgor Grinberg  * 26 * 432 / (39 * <pixel_clock>) = DSS.DISPC_DIVISOR[6:0]
213*e1e55c13SIgor Grinberg  *
214*e1e55c13SIgor Grinberg  * NOTE: the explicit equation above is reduced. Do not
215*e1e55c13SIgor Grinberg  * try to infer anything from these numbers.
216*e1e55c13SIgor Grinberg  */
217*e1e55c13SIgor Grinberg static int parse_pixclock(char *pixclock)
218*e1e55c13SIgor Grinberg {
219*e1e55c13SIgor Grinberg 	int divisor, pixclock_val;
220*e1e55c13SIgor Grinberg 	char *pixclk_start = pixclock;
221*e1e55c13SIgor Grinberg 
222*e1e55c13SIgor Grinberg 	pixclock_val = simple_strtoul(pixclock, &pixclock, 10);
223*e1e55c13SIgor Grinberg 	divisor = DIV_ROUND_UP(PIXEL_CLK_NUMERATOR, pixclock_val);
224*e1e55c13SIgor Grinberg 	/* 0 and 1 are illegal values for PCD */
225*e1e55c13SIgor Grinberg 	if (divisor <= 1)
226*e1e55c13SIgor Grinberg 		divisor = 2;
227*e1e55c13SIgor Grinberg 
228*e1e55c13SIgor Grinberg 	panel_cfg.divisor = divisor | (1 << 16);
229*e1e55c13SIgor Grinberg 	if (pixclock[0] != '\0') {
230*e1e55c13SIgor Grinberg 		printf("LCD: invalid value for pixclock:%s\n", pixclk_start);
231*e1e55c13SIgor Grinberg 		return -1;
232*e1e55c13SIgor Grinberg 	}
233*e1e55c13SIgor Grinberg 
234*e1e55c13SIgor Grinberg 	return 0;
235*e1e55c13SIgor Grinberg }
236*e1e55c13SIgor Grinberg 
237*e1e55c13SIgor Grinberg /*
238*e1e55c13SIgor Grinberg  * parse_setting() - parse a single setting of custom lcd parameters
239*e1e55c13SIgor Grinberg  *
240*e1e55c13SIgor Grinberg  * @setting:	The custom lcd setting <name>:<value>
241*e1e55c13SIgor Grinberg  *
242*e1e55c13SIgor Grinberg  * Returns -1 on failure, 0 on success.
243*e1e55c13SIgor Grinberg  */
244*e1e55c13SIgor Grinberg static int parse_setting(char *setting)
245*e1e55c13SIgor Grinberg {
246*e1e55c13SIgor Grinberg 	int num_val;
247*e1e55c13SIgor Grinberg 	char *setting_start = setting;
248*e1e55c13SIgor Grinberg 
249*e1e55c13SIgor Grinberg 	if (!strncmp(setting, "mode:", 5)) {
250*e1e55c13SIgor Grinberg 		return parse_mode(setting + 5);
251*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "pixclock:", 9)) {
252*e1e55c13SIgor Grinberg 		return parse_pixclock(setting + 9);
253*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "left:", 5)) {
254*e1e55c13SIgor Grinberg 		num_val = simple_strtoul(setting + 5, &setting, 0);
255*e1e55c13SIgor Grinberg 		panel_cfg.timing_h |= DSS_HBP(num_val);
256*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "right:", 6)) {
257*e1e55c13SIgor Grinberg 		num_val = simple_strtoul(setting + 6, &setting, 0);
258*e1e55c13SIgor Grinberg 		panel_cfg.timing_h |= DSS_HFP(num_val);
259*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "upper:", 6)) {
260*e1e55c13SIgor Grinberg 		num_val = simple_strtoul(setting + 6, &setting, 0);
261*e1e55c13SIgor Grinberg 		panel_cfg.timing_v |= DSS_VBP(num_val);
262*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "lower:", 6)) {
263*e1e55c13SIgor Grinberg 		num_val = simple_strtoul(setting + 6, &setting, 0);
264*e1e55c13SIgor Grinberg 		panel_cfg.timing_v |= DSS_VFP(num_val);
265*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "hsynclen:", 9)) {
266*e1e55c13SIgor Grinberg 		num_val = simple_strtoul(setting + 9, &setting, 0);
267*e1e55c13SIgor Grinberg 		panel_cfg.timing_h |= DSS_HSW(num_val);
268*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "vsynclen:", 9)) {
269*e1e55c13SIgor Grinberg 		num_val = simple_strtoul(setting + 9, &setting, 0);
270*e1e55c13SIgor Grinberg 		panel_cfg.timing_v |= DSS_VSW(num_val);
271*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "hsync:", 6)) {
272*e1e55c13SIgor Grinberg 		if (simple_strtoul(setting + 6, &setting, 0) == 0)
273*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq |= DSS_IHS;
274*e1e55c13SIgor Grinberg 		else
275*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq &= ~DSS_IHS;
276*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "vsync:", 6)) {
277*e1e55c13SIgor Grinberg 		if (simple_strtoul(setting + 6, &setting, 0) == 0)
278*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq |= DSS_IVS;
279*e1e55c13SIgor Grinberg 		else
280*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq &= ~DSS_IVS;
281*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "outputen:", 9)) {
282*e1e55c13SIgor Grinberg 		if (simple_strtoul(setting + 9, &setting, 0) == 0)
283*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq |= DSS_IEO;
284*e1e55c13SIgor Grinberg 		else
285*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq &= ~DSS_IEO;
286*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "pixclockpol:", 12)) {
287*e1e55c13SIgor Grinberg 		if (simple_strtoul(setting + 12, &setting, 0) == 0)
288*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq |= DSS_IPC;
289*e1e55c13SIgor Grinberg 		else
290*e1e55c13SIgor Grinberg 			panel_cfg.pol_freq &= ~DSS_IPC;
291*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "active", 6)) {
292*e1e55c13SIgor Grinberg 		panel_cfg.panel_type = ACTIVE_DISPLAY;
293*e1e55c13SIgor Grinberg 		return 0; /* Avoid sanity check below */
294*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "passive", 7)) {
295*e1e55c13SIgor Grinberg 		panel_cfg.panel_type = PASSIVE_DISPLAY;
296*e1e55c13SIgor Grinberg 		return 0; /* Avoid sanity check below */
297*e1e55c13SIgor Grinberg 	} else if (!strncmp(setting, "display:", 8)) {
298*e1e55c13SIgor Grinberg 		if (!strncmp(setting + 8, "dvi", 3)) {
299*e1e55c13SIgor Grinberg 			lcd_def = DVI_CUSTOM;
300*e1e55c13SIgor Grinberg 			return 0; /* Avoid sanity check below */
301*e1e55c13SIgor Grinberg 		}
302*e1e55c13SIgor Grinberg 	} else {
303*e1e55c13SIgor Grinberg 		printf("LCD: unknown option %s\n", setting_start);
304*e1e55c13SIgor Grinberg 		return -1;
305*e1e55c13SIgor Grinberg 	}
306*e1e55c13SIgor Grinberg 
307*e1e55c13SIgor Grinberg 	if (setting[0] != '\0') {
308*e1e55c13SIgor Grinberg 		printf("LCD: invalid value for %s\n", setting_start);
309*e1e55c13SIgor Grinberg 		return -1;
310*e1e55c13SIgor Grinberg 	}
311*e1e55c13SIgor Grinberg 
312*e1e55c13SIgor Grinberg 	return 0;
313*e1e55c13SIgor Grinberg }
314*e1e55c13SIgor Grinberg 
315*e1e55c13SIgor Grinberg /*
316*e1e55c13SIgor Grinberg  * env_parse_customlcd() - parse custom lcd params from an environment variable.
317*e1e55c13SIgor Grinberg  *
318*e1e55c13SIgor Grinberg  * @custom_lcd_params:	The environment variable containing the lcd params.
319*e1e55c13SIgor Grinberg  *
320*e1e55c13SIgor Grinberg  * Returns -1 on failure, 0 on success.
321*e1e55c13SIgor Grinberg  */
322*e1e55c13SIgor Grinberg static int parse_customlcd(char *custom_lcd_params)
323*e1e55c13SIgor Grinberg {
324*e1e55c13SIgor Grinberg 	char params_cpy[160];
325*e1e55c13SIgor Grinberg 	char *setting;
326*e1e55c13SIgor Grinberg 
327*e1e55c13SIgor Grinberg 	strncpy(params_cpy, custom_lcd_params, 160);
328*e1e55c13SIgor Grinberg 	setting = strtok(params_cpy, ",");
329*e1e55c13SIgor Grinberg 	while (setting) {
330*e1e55c13SIgor Grinberg 		if (parse_setting(setting) < 0)
331*e1e55c13SIgor Grinberg 			return -1;
332*e1e55c13SIgor Grinberg 
333*e1e55c13SIgor Grinberg 		setting = strtok(NULL, ",");
334*e1e55c13SIgor Grinberg 	}
335*e1e55c13SIgor Grinberg 
336*e1e55c13SIgor Grinberg 	/* Currently we don't support changing this via custom lcd params */
337*e1e55c13SIgor Grinberg 	panel_cfg.data_lines = LCD_INTERFACE_24_BIT;
338*e1e55c13SIgor Grinberg 	panel_cfg.gfx_format = GFXFORMAT_RGB16; /* See dvi predefines note */
339*e1e55c13SIgor Grinberg 
340*e1e55c13SIgor Grinberg 	return 0;
341*e1e55c13SIgor Grinberg }
342*e1e55c13SIgor Grinberg 
343*e1e55c13SIgor Grinberg /*
344*e1e55c13SIgor Grinberg  * env_parse_displaytype() - parse display type.
345*e1e55c13SIgor Grinberg  *
346*e1e55c13SIgor Grinberg  * Parses the environment variable "displaytype", which contains the
347*e1e55c13SIgor Grinberg  * name of the display type or preset, in which case it applies its
348*e1e55c13SIgor Grinberg  * configurations.
349*e1e55c13SIgor Grinberg  *
350*e1e55c13SIgor Grinberg  * Returns the type of display that was specified.
351*e1e55c13SIgor Grinberg  */
352*e1e55c13SIgor Grinberg static enum display_type env_parse_displaytype(char *displaytype)
353*e1e55c13SIgor Grinberg {
354*e1e55c13SIgor Grinberg 	if (!strncmp(displaytype, "dvi640x480", 10))
355*e1e55c13SIgor Grinberg 		return set_dvi_preset(preset_dvi_640X480, 640, 480);
356*e1e55c13SIgor Grinberg 	else if (!strncmp(displaytype, "dvi800x600", 10))
357*e1e55c13SIgor Grinberg 		return set_dvi_preset(preset_dvi_800X600, 800, 600);
358*e1e55c13SIgor Grinberg 	else if (!strncmp(displaytype, "dvi1024x768", 11))
359*e1e55c13SIgor Grinberg 		return set_dvi_preset(preset_dvi_1024X768, 1024, 768);
360*e1e55c13SIgor Grinberg 	else if (!strncmp(displaytype, "dvi1152x864", 11))
361*e1e55c13SIgor Grinberg 		return set_dvi_preset(preset_dvi_1152X864, 1152, 864);
362*e1e55c13SIgor Grinberg 	else if (!strncmp(displaytype, "dvi1280x960", 11))
363*e1e55c13SIgor Grinberg 		return set_dvi_preset(preset_dvi_1280X960, 1280, 960);
364*e1e55c13SIgor Grinberg 	else if (!strncmp(displaytype, "dvi1280x1024", 12))
365*e1e55c13SIgor Grinberg 		return set_dvi_preset(preset_dvi_1280X1024, 1280, 1024);
366*e1e55c13SIgor Grinberg 
367*e1e55c13SIgor Grinberg 	return NONE;
368*e1e55c13SIgor Grinberg }
369*e1e55c13SIgor Grinberg 
370*e1e55c13SIgor Grinberg void lcd_ctrl_init(void *lcdbase)
371*e1e55c13SIgor Grinberg {
372*e1e55c13SIgor Grinberg 	struct prcm *prcm = (struct prcm *)PRCM_BASE;
373*e1e55c13SIgor Grinberg 	char *custom_lcd;
374*e1e55c13SIgor Grinberg 	char *displaytype = getenv("displaytype");
375*e1e55c13SIgor Grinberg 
376*e1e55c13SIgor Grinberg 	if (displaytype == NULL)
377*e1e55c13SIgor Grinberg 		return;
378*e1e55c13SIgor Grinberg 
379*e1e55c13SIgor Grinberg 	lcd_def = env_parse_displaytype(displaytype);
380*e1e55c13SIgor Grinberg 	/* If we did not recognize the preset, check if it's an env variable */
381*e1e55c13SIgor Grinberg 	if (lcd_def == NONE) {
382*e1e55c13SIgor Grinberg 		custom_lcd = getenv(displaytype);
383*e1e55c13SIgor Grinberg 		if (custom_lcd == NULL || parse_customlcd(custom_lcd) < 0)
384*e1e55c13SIgor Grinberg 			return;
385*e1e55c13SIgor Grinberg 	}
386*e1e55c13SIgor Grinberg 
387*e1e55c13SIgor Grinberg 	panel_cfg.frame_buffer = lcdbase;
388*e1e55c13SIgor Grinberg 	omap3_dss_panel_config(&panel_cfg);
389*e1e55c13SIgor Grinberg 	/*
390*e1e55c13SIgor Grinberg 	 * Pixel clock is defined with many divisions and only few
391*e1e55c13SIgor Grinberg 	 * multiplications of the system clock. Since DSS FCLK divisor is set
392*e1e55c13SIgor Grinberg 	 * to 16 by default, we need to set it to a smaller value, like 3
393*e1e55c13SIgor Grinberg 	 * (chosen via trial and error).
394*e1e55c13SIgor Grinberg 	 */
395*e1e55c13SIgor Grinberg 	clrsetbits_le32(&prcm->clksel_dss, 0xF, 3);
396*e1e55c13SIgor Grinberg }
397*e1e55c13SIgor Grinberg 
398*e1e55c13SIgor Grinberg void lcd_enable(void)
399*e1e55c13SIgor Grinberg {
400*e1e55c13SIgor Grinberg 	if (lcd_def == DVI || lcd_def == DVI_CUSTOM) {
401*e1e55c13SIgor Grinberg 		gpio_direction_output(54, 0); /* Turn on DVI */
402*e1e55c13SIgor Grinberg 		omap3_dss_enable();
403*e1e55c13SIgor Grinberg 	}
404*e1e55c13SIgor Grinberg }
405*e1e55c13SIgor Grinberg 
406*e1e55c13SIgor Grinberg void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) {}
407