10be8f203SSimon Glass /* 20be8f203SSimon Glass * Copyright (c) 2011 The Chromium OS Authors. 30be8f203SSimon Glass * See file CREDITS for list of people who contributed to this 40be8f203SSimon Glass * project. 50be8f203SSimon Glass * 60be8f203SSimon Glass * This program is free software; you can redistribute it and/or 70be8f203SSimon Glass * modify it under the terms of the GNU General Public License as 80be8f203SSimon Glass * published by the Free Software Foundation; either version 2 of 90be8f203SSimon Glass * the License, or (at your option) any later version. 100be8f203SSimon Glass * 110be8f203SSimon Glass * This program is distributed in the hope that it will be useful, 120be8f203SSimon Glass * but WITHOUT ANY WARRANTY; without even the implied warranty of 130be8f203SSimon Glass * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 140be8f203SSimon Glass * GNU General Public License for more details. 150be8f203SSimon Glass * 160be8f203SSimon Glass * You should have received a copy of the GNU General Public License 170be8f203SSimon Glass * along with this program; if not, write to the Free Software 180be8f203SSimon Glass * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 190be8f203SSimon Glass * MA 02111-1307 USA 200be8f203SSimon Glass */ 210be8f203SSimon Glass 220be8f203SSimon Glass #include <common.h> 230be8f203SSimon Glass #include <fdtdec.h> 240be8f203SSimon Glass #include <lcd.h> 250be8f203SSimon Glass 260be8f203SSimon Glass #include <asm/system.h> 270be8f203SSimon Glass #include <asm/gpio.h> 280be8f203SSimon Glass 290be8f203SSimon Glass #include <asm/arch/clock.h> 300be8f203SSimon Glass #include <asm/arch/funcmux.h> 310be8f203SSimon Glass #include <asm/arch/pinmux.h> 320be8f203SSimon Glass #include <asm/arch/pwm.h> 330be8f203SSimon Glass #include <asm/arch/display.h> 340be8f203SSimon Glass #include <asm/arch-tegra/timer.h> 350be8f203SSimon Glass 360be8f203SSimon Glass DECLARE_GLOBAL_DATA_PTR; 370be8f203SSimon Glass 380be8f203SSimon Glass /* These are the stages we go throuh in enabling the LCD */ 390be8f203SSimon Glass enum stage_t { 400be8f203SSimon Glass STAGE_START, 410be8f203SSimon Glass STAGE_PANEL_VDD, 420be8f203SSimon Glass STAGE_LVDS, 430be8f203SSimon Glass STAGE_BACKLIGHT_VDD, 440be8f203SSimon Glass STAGE_PWM, 450be8f203SSimon Glass STAGE_BACKLIGHT_EN, 460be8f203SSimon Glass STAGE_DONE, 470be8f203SSimon Glass }; 480be8f203SSimon Glass 490be8f203SSimon Glass static enum stage_t stage; /* Current stage we are at */ 500be8f203SSimon Glass static unsigned long timer_next; /* Time we can move onto next stage */ 510be8f203SSimon Glass 520be8f203SSimon Glass /* Our LCD config, set up in handle_stage() */ 530be8f203SSimon Glass static struct fdt_panel_config config; 540be8f203SSimon Glass struct fdt_disp_config *disp_config; /* Display controller config */ 550be8f203SSimon Glass 560be8f203SSimon Glass enum { 570be8f203SSimon Glass /* Maximum LCD size we support */ 580be8f203SSimon Glass LCD_MAX_WIDTH = 1366, 590be8f203SSimon Glass LCD_MAX_HEIGHT = 768, 600be8f203SSimon Glass LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ 610be8f203SSimon Glass }; 620be8f203SSimon Glass 630be8f203SSimon Glass vidinfo_t panel_info = { 640be8f203SSimon Glass /* Insert a value here so that we don't end up in the BSS */ 650be8f203SSimon Glass .vl_col = -1, 660be8f203SSimon Glass }; 670be8f203SSimon Glass 680be8f203SSimon Glass #ifndef CONFIG_OF_CONTROL 690be8f203SSimon Glass #error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support" 700be8f203SSimon Glass #endif 710be8f203SSimon Glass 720be8f203SSimon Glass static void update_panel_size(struct fdt_disp_config *config) 730be8f203SSimon Glass { 740be8f203SSimon Glass panel_info.vl_col = config->width; 750be8f203SSimon Glass panel_info.vl_row = config->height; 760be8f203SSimon Glass panel_info.vl_bpix = config->log2_bpp; 770be8f203SSimon Glass } 780be8f203SSimon Glass 790be8f203SSimon Glass /* 800be8f203SSimon Glass * Main init function called by lcd driver. 810be8f203SSimon Glass * Inits and then prints test pattern if required. 820be8f203SSimon Glass */ 830be8f203SSimon Glass 840be8f203SSimon Glass void lcd_ctrl_init(void *lcdbase) 850be8f203SSimon Glass { 8644706a87SSimon Glass int type = DCACHE_OFF; 870c558754SThierry Reding int size; 880be8f203SSimon Glass 890be8f203SSimon Glass assert(disp_config); 900be8f203SSimon Glass 910be8f203SSimon Glass /* Make sure that we can acommodate the selected LCD */ 920be8f203SSimon Glass assert(disp_config->width <= LCD_MAX_WIDTH); 930be8f203SSimon Glass assert(disp_config->height <= LCD_MAX_HEIGHT); 940be8f203SSimon Glass assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP); 950be8f203SSimon Glass if (disp_config->width <= LCD_MAX_WIDTH 960be8f203SSimon Glass && disp_config->height <= LCD_MAX_HEIGHT 970be8f203SSimon Glass && disp_config->log2_bpp <= LCD_MAX_LOG2_BPP) 980be8f203SSimon Glass update_panel_size(disp_config); 990c558754SThierry Reding size = lcd_get_size(&lcd_line_length); 1000be8f203SSimon Glass 10144706a87SSimon Glass /* Set up the LCD caching as requested */ 10244706a87SSimon Glass if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH) 10344706a87SSimon Glass type = DCACHE_WRITETHROUGH; 10444706a87SSimon Glass else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK) 10544706a87SSimon Glass type = DCACHE_WRITEBACK; 10644706a87SSimon Glass mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type); 10744706a87SSimon Glass 10844706a87SSimon Glass /* Enable flushing after LCD writes if requested */ 10944706a87SSimon Glass lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); 11044706a87SSimon Glass 111*00a0ca59SJeroen Hofstee debug("LCD frame buffer at %08X\n", disp_config->frame_buffer); 1120be8f203SSimon Glass } 1130be8f203SSimon Glass 1140be8f203SSimon Glass ulong calc_fbsize(void) 1150be8f203SSimon Glass { 1160be8f203SSimon Glass return (panel_info.vl_col * panel_info.vl_row * 1170be8f203SSimon Glass NBITS(panel_info.vl_bpix)) / 8; 1180be8f203SSimon Glass } 1190be8f203SSimon Glass 1200be8f203SSimon Glass void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) 1210be8f203SSimon Glass { 1220be8f203SSimon Glass } 1230be8f203SSimon Glass 1240be8f203SSimon Glass void tegra_lcd_early_init(const void *blob) 1250be8f203SSimon Glass { 1260be8f203SSimon Glass /* 1270be8f203SSimon Glass * Go with the maximum size for now. We will fix this up after 1280be8f203SSimon Glass * relocation. These values are only used for memory alocation. 1290be8f203SSimon Glass */ 1300be8f203SSimon Glass panel_info.vl_col = LCD_MAX_WIDTH; 1310be8f203SSimon Glass panel_info.vl_row = LCD_MAX_HEIGHT; 1320be8f203SSimon Glass panel_info.vl_bpix = LCD_MAX_LOG2_BPP; 1330be8f203SSimon Glass } 1340be8f203SSimon Glass 1350be8f203SSimon Glass /** 1360be8f203SSimon Glass * Decode the panel information from the fdt. 1370be8f203SSimon Glass * 1380be8f203SSimon Glass * @param blob fdt blob 1390be8f203SSimon Glass * @param config structure to store fdt config into 1400be8f203SSimon Glass * @return 0 if ok, -ve on error 1410be8f203SSimon Glass */ 1420be8f203SSimon Glass static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) 1430be8f203SSimon Glass { 1440be8f203SSimon Glass int display_node; 1450be8f203SSimon Glass 1460be8f203SSimon Glass disp_config = tegra_display_get_config(); 1470be8f203SSimon Glass if (!disp_config) { 1480be8f203SSimon Glass debug("%s: Display controller is not configured\n", __func__); 1490be8f203SSimon Glass return -1; 1500be8f203SSimon Glass } 1510be8f203SSimon Glass display_node = disp_config->panel_node; 1520be8f203SSimon Glass if (display_node < 0) { 1530be8f203SSimon Glass debug("%s: No panel configuration available\n", __func__); 1540be8f203SSimon Glass return -1; 1550be8f203SSimon Glass } 1560be8f203SSimon Glass 1570be8f203SSimon Glass config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); 1580be8f203SSimon Glass if (config->pwm_channel < 0) { 1590be8f203SSimon Glass debug("%s: Unable to request PWM channel\n", __func__); 1600be8f203SSimon Glass return -1; 1610be8f203SSimon Glass } 1620be8f203SSimon Glass 1630be8f203SSimon Glass config->cache_type = fdtdec_get_int(blob, display_node, 1640be8f203SSimon Glass "nvidia,cache-type", 1650be8f203SSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH); 1660be8f203SSimon Glass 1670be8f203SSimon Glass /* These GPIOs are all optional */ 1680be8f203SSimon Glass fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-enable-gpios", 1690be8f203SSimon Glass &config->backlight_en); 1700be8f203SSimon Glass fdtdec_decode_gpio(blob, display_node, "nvidia,lvds-shutdown-gpios", 1710be8f203SSimon Glass &config->lvds_shutdown); 1720be8f203SSimon Glass fdtdec_decode_gpio(blob, display_node, "nvidia,backlight-vdd-gpios", 1730be8f203SSimon Glass &config->backlight_vdd); 1740be8f203SSimon Glass fdtdec_decode_gpio(blob, display_node, "nvidia,panel-vdd-gpios", 1750be8f203SSimon Glass &config->panel_vdd); 1760be8f203SSimon Glass 1770be8f203SSimon Glass return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", 1780be8f203SSimon Glass config->panel_timings, FDT_LCD_TIMINGS); 1790be8f203SSimon Glass } 1800be8f203SSimon Glass 1810be8f203SSimon Glass /** 1820be8f203SSimon Glass * Handle the next stage of device init 1830be8f203SSimon Glass */ 1840be8f203SSimon Glass static int handle_stage(const void *blob) 1850be8f203SSimon Glass { 1860be8f203SSimon Glass debug("%s: stage %d\n", __func__, stage); 1870be8f203SSimon Glass 1880be8f203SSimon Glass /* do the things for this stage */ 1890be8f203SSimon Glass switch (stage) { 1900be8f203SSimon Glass case STAGE_START: 1910be8f203SSimon Glass /* Initialize the Tegra display controller */ 1920be8f203SSimon Glass if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) { 1930be8f203SSimon Glass printf("%s: Failed to probe display driver\n", 1940be8f203SSimon Glass __func__); 1950be8f203SSimon Glass return -1; 1960be8f203SSimon Glass } 1970be8f203SSimon Glass 1980be8f203SSimon Glass /* get panel details */ 1990be8f203SSimon Glass if (fdt_decode_lcd(blob, &config)) { 2000be8f203SSimon Glass printf("No valid LCD information in device tree\n"); 2010be8f203SSimon Glass return -1; 2020be8f203SSimon Glass } 2030be8f203SSimon Glass 2040be8f203SSimon Glass /* 2050be8f203SSimon Glass * It is possible that the FDT has requested that the LCD be 2060be8f203SSimon Glass * disabled. We currently don't support this. It would require 2070be8f203SSimon Glass * changes to U-Boot LCD subsystem to have LCD support 2080be8f203SSimon Glass * compiled in but not used. An easier option might be to 2090be8f203SSimon Glass * still have a frame buffer, but leave the backlight off and 2100be8f203SSimon Glass * remove all mention of lcd in the stdout environment 2110be8f203SSimon Glass * variable. 2120be8f203SSimon Glass */ 2130be8f203SSimon Glass 2140be8f203SSimon Glass funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); 2150be8f203SSimon Glass 2160be8f203SSimon Glass fdtdec_setup_gpio(&config.panel_vdd); 2170be8f203SSimon Glass fdtdec_setup_gpio(&config.lvds_shutdown); 2180be8f203SSimon Glass fdtdec_setup_gpio(&config.backlight_vdd); 2190be8f203SSimon Glass fdtdec_setup_gpio(&config.backlight_en); 2200be8f203SSimon Glass 2210be8f203SSimon Glass /* 2220be8f203SSimon Glass * TODO: If fdt includes output flag we can omit this code 2230be8f203SSimon Glass * since fdtdec_setup_gpio will do it for us. 2240be8f203SSimon Glass */ 2250be8f203SSimon Glass if (fdt_gpio_isvalid(&config.panel_vdd)) 2260be8f203SSimon Glass gpio_direction_output(config.panel_vdd.gpio, 0); 2270be8f203SSimon Glass if (fdt_gpio_isvalid(&config.lvds_shutdown)) 2280be8f203SSimon Glass gpio_direction_output(config.lvds_shutdown.gpio, 0); 2290be8f203SSimon Glass if (fdt_gpio_isvalid(&config.backlight_vdd)) 2300be8f203SSimon Glass gpio_direction_output(config.backlight_vdd.gpio, 0); 2310be8f203SSimon Glass if (fdt_gpio_isvalid(&config.backlight_en)) 2320be8f203SSimon Glass gpio_direction_output(config.backlight_en.gpio, 0); 2330be8f203SSimon Glass break; 2340be8f203SSimon Glass case STAGE_PANEL_VDD: 2350be8f203SSimon Glass if (fdt_gpio_isvalid(&config.panel_vdd)) 2360be8f203SSimon Glass gpio_direction_output(config.panel_vdd.gpio, 1); 2370be8f203SSimon Glass break; 2380be8f203SSimon Glass case STAGE_LVDS: 2390be8f203SSimon Glass if (fdt_gpio_isvalid(&config.lvds_shutdown)) 2400be8f203SSimon Glass gpio_set_value(config.lvds_shutdown.gpio, 1); 2410be8f203SSimon Glass break; 2420be8f203SSimon Glass case STAGE_BACKLIGHT_VDD: 2430be8f203SSimon Glass if (fdt_gpio_isvalid(&config.backlight_vdd)) 2440be8f203SSimon Glass gpio_set_value(config.backlight_vdd.gpio, 1); 2450be8f203SSimon Glass break; 2460be8f203SSimon Glass case STAGE_PWM: 2470be8f203SSimon Glass /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ 2480be8f203SSimon Glass pinmux_set_func(PINGRP_GPU, PMUX_FUNC_PWM); 2490be8f203SSimon Glass pinmux_tristate_disable(PINGRP_GPU); 2500be8f203SSimon Glass 2510be8f203SSimon Glass pwm_enable(config.pwm_channel, 32768, 0xdf, 1); 2520be8f203SSimon Glass break; 2530be8f203SSimon Glass case STAGE_BACKLIGHT_EN: 2540be8f203SSimon Glass if (fdt_gpio_isvalid(&config.backlight_en)) 2550be8f203SSimon Glass gpio_set_value(config.backlight_en.gpio, 1); 2560be8f203SSimon Glass break; 2570be8f203SSimon Glass case STAGE_DONE: 2580be8f203SSimon Glass break; 2590be8f203SSimon Glass } 2600be8f203SSimon Glass 2610be8f203SSimon Glass /* set up timer for next stage */ 2620be8f203SSimon Glass timer_next = timer_get_us(); 2630be8f203SSimon Glass if (stage < FDT_LCD_TIMINGS) 2640be8f203SSimon Glass timer_next += config.panel_timings[stage] * 1000; 2650be8f203SSimon Glass 2660be8f203SSimon Glass /* move to next stage */ 2670be8f203SSimon Glass stage++; 2680be8f203SSimon Glass return 0; 2690be8f203SSimon Glass } 2700be8f203SSimon Glass 2710be8f203SSimon Glass int tegra_lcd_check_next_stage(const void *blob, int wait) 2720be8f203SSimon Glass { 2730be8f203SSimon Glass if (stage == STAGE_DONE) 2740be8f203SSimon Glass return 0; 2750be8f203SSimon Glass 2760be8f203SSimon Glass do { 2770be8f203SSimon Glass /* wait if we need to */ 2780be8f203SSimon Glass debug("%s: stage %d\n", __func__, stage); 2790be8f203SSimon Glass if (stage != STAGE_START) { 2800be8f203SSimon Glass int delay = timer_next - timer_get_us(); 2810be8f203SSimon Glass 2820be8f203SSimon Glass if (delay > 0) { 2830be8f203SSimon Glass if (wait) 2840be8f203SSimon Glass udelay(delay); 2850be8f203SSimon Glass else 2860be8f203SSimon Glass return 0; 2870be8f203SSimon Glass } 2880be8f203SSimon Glass } 2890be8f203SSimon Glass 2900be8f203SSimon Glass if (handle_stage(blob)) 2910be8f203SSimon Glass return -1; 2920be8f203SSimon Glass } while (wait && stage != STAGE_DONE); 2930be8f203SSimon Glass if (stage == STAGE_DONE) 2940be8f203SSimon Glass debug("%s: LCD init complete\n", __func__); 2950be8f203SSimon Glass 2960be8f203SSimon Glass return 0; 2970be8f203SSimon Glass } 2980be8f203SSimon Glass 2990be8f203SSimon Glass void lcd_enable(void) 3000be8f203SSimon Glass { 3010be8f203SSimon Glass /* 3020be8f203SSimon Glass * Backlight and power init will be done separately in 3030be8f203SSimon Glass * tegra_lcd_check_next_stage(), which should be called in 3040be8f203SSimon Glass * board_late_init(). 3050be8f203SSimon Glass * 3060be8f203SSimon Glass * U-Boot code supports only colour depth, selected at compile time. 3070be8f203SSimon Glass * The device tree setting should match this. Otherwise the display 3080be8f203SSimon Glass * will not look right, and U-Boot may crash. 3090be8f203SSimon Glass */ 3100be8f203SSimon Glass if (disp_config->log2_bpp != LCD_BPP) { 3110be8f203SSimon Glass printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)" 3120be8f203SSimon Glass " must match setting of LCD_BPP (%d)\n", __func__, 3130be8f203SSimon Glass disp_config->log2_bpp, disp_config->bpp, LCD_BPP); 3140be8f203SSimon Glass } 3150be8f203SSimon Glass } 316