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