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