1 /* 2 * Copyright (c) 2011 The Chromium OS Authors. 3 * See file CREDITS for list of people who contributed to this 4 * project. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 19 * MA 02111-1307 USA 20 */ 21 22 #include <common.h> 23 #include <fdtdec.h> 24 #include <lcd.h> 25 26 #include <asm/system.h> 27 #include <asm/gpio.h> 28 29 #include <asm/arch/clock.h> 30 #include <asm/arch/funcmux.h> 31 #include <asm/arch/pinmux.h> 32 #include <asm/arch/pwm.h> 33 #include <asm/arch/display.h> 34 #include <asm/arch-tegra/timer.h> 35 36 DECLARE_GLOBAL_DATA_PTR; 37 38 /* These are the stages we go throuh in enabling the LCD */ 39 enum stage_t { 40 STAGE_START, 41 STAGE_PANEL_VDD, 42 STAGE_LVDS, 43 STAGE_BACKLIGHT_VDD, 44 STAGE_PWM, 45 STAGE_BACKLIGHT_EN, 46 STAGE_DONE, 47 }; 48 49 static enum stage_t stage; /* Current stage we are at */ 50 static unsigned long timer_next; /* Time we can move onto next stage */ 51 52 /* Our LCD config, set up in handle_stage() */ 53 static struct fdt_panel_config config; 54 struct fdt_disp_config *disp_config; /* Display controller config */ 55 56 enum { 57 /* Maximum LCD size we support */ 58 LCD_MAX_WIDTH = 1366, 59 LCD_MAX_HEIGHT = 768, 60 LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ 61 }; 62 63 vidinfo_t panel_info = { 64 /* Insert a value here so that we don't end up in the BSS */ 65 .vl_col = -1, 66 }; 67 68 #ifndef CONFIG_OF_CONTROL 69 #error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support" 70 #endif 71 72 static void update_panel_size(struct fdt_disp_config *config) 73 { 74 panel_info.vl_col = config->width; 75 panel_info.vl_row = config->height; 76 panel_info.vl_bpix = config->log2_bpp; 77 } 78 79 /* 80 * Main init function called by lcd driver. 81 * Inits and then prints test pattern if required. 82 */ 83 84 void lcd_ctrl_init(void *lcdbase) 85 { 86 int type = DCACHE_OFF; 87 int size; 88 89 assert(disp_config); 90 91 /* Make sure that we can acommodate the selected LCD */ 92 assert(disp_config->width <= LCD_MAX_WIDTH); 93 assert(disp_config->height <= LCD_MAX_HEIGHT); 94 assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP); 95 if (disp_config->width <= LCD_MAX_WIDTH 96 && disp_config->height <= LCD_MAX_HEIGHT 97 && disp_config->log2_bpp <= LCD_MAX_LOG2_BPP) 98 update_panel_size(disp_config); 99 size = lcd_get_size(&lcd_line_length); 100 101 /* Set up the LCD caching as requested */ 102 if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH) 103 type = DCACHE_WRITETHROUGH; 104 else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK) 105 type = DCACHE_WRITEBACK; 106 mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type); 107 108 /* Enable flushing after LCD writes if requested */ 109 lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); 110 111 debug("LCD frame buffer at %08X\n", disp_config->frame_buffer); 112 } 113 114 ulong calc_fbsize(void) 115 { 116 return (panel_info.vl_col * panel_info.vl_row * 117 NBITS(panel_info.vl_bpix)) / 8; 118 } 119 120 void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) 121 { 122 } 123 124 void tegra_lcd_early_init(const void *blob) 125 { 126 /* 127 * Go with the maximum size for now. We will fix this up after 128 * relocation. These values are only used for memory alocation. 129 */ 130 panel_info.vl_col = LCD_MAX_WIDTH; 131 panel_info.vl_row = LCD_MAX_HEIGHT; 132 panel_info.vl_bpix = LCD_MAX_LOG2_BPP; 133 } 134 135 /** 136 * Decode the panel information from the fdt. 137 * 138 * @param blob fdt blob 139 * @param config structure to store fdt config into 140 * @return 0 if ok, -ve on error 141 */ 142 static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) 143 { 144 int display_node; 145 146 disp_config = tegra_display_get_config(); 147 if (!disp_config) { 148 debug("%s: Display controller is not configured\n", __func__); 149 return -1; 150 } 151 display_node = disp_config->panel_node; 152 if (display_node < 0) { 153 debug("%s: No panel configuration available\n", __func__); 154 return -1; 155 } 156 157 config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); 158 if (config->pwm_channel < 0) { 159 debug("%s: Unable to request PWM channel\n", __func__); 160 return -1; 161 } 162 163 config->cache_type = fdtdec_get_int(blob, display_node, 164 "nvidia,cache-type", 165 FDT_LCD_CACHE_WRITE_BACK_FLUSH); 166 167 /* These GPIOs are all optional */ 168 fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios", 169 &config->backlight_en); 170 fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios", 171 &config->lvds_shutdown); 172 fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios", 173 &config->backlight_vdd); 174 fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios", 175 &config->panel_vdd); 176 177 return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", 178 config->panel_timings, FDT_LCD_TIMINGS); 179 } 180 181 /** 182 * Handle the next stage of device init 183 */ 184 static int handle_stage(const void *blob) 185 { 186 debug("%s: stage %d\n", __func__, stage); 187 188 /* do the things for this stage */ 189 switch (stage) { 190 case STAGE_START: 191 /* Initialize the Tegra display controller */ 192 if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) { 193 printf("%s: Failed to probe display driver\n", 194 __func__); 195 return -1; 196 } 197 198 /* get panel details */ 199 if (fdt_decode_lcd(blob, &config)) { 200 printf("No valid LCD information in device tree\n"); 201 return -1; 202 } 203 204 /* 205 * It is possible that the FDT has requested that the LCD be 206 * disabled. We currently don't support this. It would require 207 * changes to U-Boot LCD subsystem to have LCD support 208 * compiled in but not used. An easier option might be to 209 * still have a frame buffer, but leave the backlight off and 210 * remove all mention of lcd in the stdout environment 211 * variable. 212 */ 213 214 funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); 215 216 fdtdec_setup_gpio(&config.panel_vdd); 217 fdtdec_setup_gpio(&config.lvds_shutdown); 218 fdtdec_setup_gpio(&config.backlight_vdd); 219 fdtdec_setup_gpio(&config.backlight_en); 220 221 /* 222 * TODO: If fdt includes output flag we can omit this code 223 * since fdtdec_setup_gpio will do it for us. 224 */ 225 if (fdt_gpio_isvalid(&config.panel_vdd)) 226 gpio_direction_output(config.panel_vdd.gpio, 0); 227 if (fdt_gpio_isvalid(&config.lvds_shutdown)) 228 gpio_direction_output(config.lvds_shutdown.gpio, 0); 229 if (fdt_gpio_isvalid(&config.backlight_vdd)) 230 gpio_direction_output(config.backlight_vdd.gpio, 0); 231 if (fdt_gpio_isvalid(&config.backlight_en)) 232 gpio_direction_output(config.backlight_en.gpio, 0); 233 break; 234 case STAGE_PANEL_VDD: 235 if (fdt_gpio_isvalid(&config.panel_vdd)) 236 gpio_direction_output(config.panel_vdd.gpio, 1); 237 break; 238 case STAGE_LVDS: 239 if (fdt_gpio_isvalid(&config.lvds_shutdown)) 240 gpio_set_value(config.lvds_shutdown.gpio, 1); 241 break; 242 case STAGE_BACKLIGHT_VDD: 243 if (fdt_gpio_isvalid(&config.backlight_vdd)) 244 gpio_set_value(config.backlight_vdd.gpio, 1); 245 break; 246 case STAGE_PWM: 247 /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ 248 pinmux_set_func(PINGRP_GPU, PMUX_FUNC_PWM); 249 pinmux_tristate_disable(PINGRP_GPU); 250 251 pwm_enable(config.pwm_channel, 32768, 0xdf, 1); 252 break; 253 case STAGE_BACKLIGHT_EN: 254 if (fdt_gpio_isvalid(&config.backlight_en)) 255 gpio_set_value(config.backlight_en.gpio, 1); 256 break; 257 case STAGE_DONE: 258 break; 259 } 260 261 /* set up timer for next stage */ 262 timer_next = timer_get_us(); 263 if (stage < FDT_LCD_TIMINGS) 264 timer_next += config.panel_timings[stage] * 1000; 265 266 /* move to next stage */ 267 stage++; 268 return 0; 269 } 270 271 int tegra_lcd_check_next_stage(const void *blob, int wait) 272 { 273 if (stage == STAGE_DONE) 274 return 0; 275 276 do { 277 /* wait if we need to */ 278 debug("%s: stage %d\n", __func__, stage); 279 if (stage != STAGE_START) { 280 int delay = timer_next - timer_get_us(); 281 282 if (delay > 0) { 283 if (wait) 284 udelay(delay); 285 else 286 return 0; 287 } 288 } 289 290 if (handle_stage(blob)) 291 return -1; 292 } while (wait && stage != STAGE_DONE); 293 if (stage == STAGE_DONE) 294 debug("%s: LCD init complete\n", __func__); 295 296 return 0; 297 } 298 299 void lcd_enable(void) 300 { 301 /* 302 * Backlight and power init will be done separately in 303 * tegra_lcd_check_next_stage(), which should be called in 304 * board_late_init(). 305 * 306 * U-Boot code supports only colour depth, selected at compile time. 307 * The device tree setting should match this. Otherwise the display 308 * will not look right, and U-Boot may crash. 309 */ 310 if (disp_config->log2_bpp != LCD_BPP) { 311 printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)" 312 " must match setting of LCD_BPP (%d)\n", __func__, 313 disp_config->log2_bpp, disp_config->bpp, LCD_BPP); 314 } 315 } 316