10be8f203SSimon Glass /* 20be8f203SSimon Glass * Copyright (c) 2011 The Chromium OS Authors. 31a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 40be8f203SSimon Glass */ 50be8f203SSimon Glass 60be8f203SSimon Glass #include <common.h> 70be8f203SSimon Glass #include <fdtdec.h> 80be8f203SSimon Glass #include <lcd.h> 90be8f203SSimon Glass 100be8f203SSimon Glass #include <asm/system.h> 110be8f203SSimon Glass #include <asm/gpio.h> 120be8f203SSimon Glass 130be8f203SSimon Glass #include <asm/arch/clock.h> 140be8f203SSimon Glass #include <asm/arch/funcmux.h> 150be8f203SSimon Glass #include <asm/arch/pinmux.h> 160be8f203SSimon Glass #include <asm/arch/pwm.h> 170be8f203SSimon Glass #include <asm/arch/display.h> 180be8f203SSimon Glass #include <asm/arch-tegra/timer.h> 190be8f203SSimon Glass 200be8f203SSimon Glass DECLARE_GLOBAL_DATA_PTR; 210be8f203SSimon Glass 220be8f203SSimon Glass /* These are the stages we go throuh in enabling the LCD */ 230be8f203SSimon Glass enum stage_t { 240be8f203SSimon Glass STAGE_START, 250be8f203SSimon Glass STAGE_PANEL_VDD, 260be8f203SSimon Glass STAGE_LVDS, 270be8f203SSimon Glass STAGE_BACKLIGHT_VDD, 280be8f203SSimon Glass STAGE_PWM, 290be8f203SSimon Glass STAGE_BACKLIGHT_EN, 300be8f203SSimon Glass STAGE_DONE, 310be8f203SSimon Glass }; 320be8f203SSimon Glass 330be8f203SSimon Glass static enum stage_t stage; /* Current stage we are at */ 340be8f203SSimon Glass static unsigned long timer_next; /* Time we can move onto next stage */ 350be8f203SSimon Glass 360be8f203SSimon Glass /* Our LCD config, set up in handle_stage() */ 370be8f203SSimon Glass static struct fdt_panel_config config; 380be8f203SSimon Glass struct fdt_disp_config *disp_config; /* Display controller config */ 390be8f203SSimon Glass 400be8f203SSimon Glass enum { 410be8f203SSimon Glass /* Maximum LCD size we support */ 420be8f203SSimon Glass LCD_MAX_WIDTH = 1366, 430be8f203SSimon Glass LCD_MAX_HEIGHT = 768, 440be8f203SSimon Glass LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */ 450be8f203SSimon Glass }; 460be8f203SSimon Glass 470be8f203SSimon Glass vidinfo_t panel_info = { 480be8f203SSimon Glass /* Insert a value here so that we don't end up in the BSS */ 490be8f203SSimon Glass .vl_col = -1, 500be8f203SSimon Glass }; 510be8f203SSimon Glass 520be8f203SSimon Glass #ifndef CONFIG_OF_CONTROL 530be8f203SSimon Glass #error "You must enable CONFIG_OF_CONTROL to get Tegra LCD support" 540be8f203SSimon Glass #endif 550be8f203SSimon Glass 560be8f203SSimon Glass static void update_panel_size(struct fdt_disp_config *config) 570be8f203SSimon Glass { 580be8f203SSimon Glass panel_info.vl_col = config->width; 590be8f203SSimon Glass panel_info.vl_row = config->height; 600be8f203SSimon Glass panel_info.vl_bpix = config->log2_bpp; 610be8f203SSimon Glass } 620be8f203SSimon Glass 630be8f203SSimon Glass /* 640be8f203SSimon Glass * Main init function called by lcd driver. 650be8f203SSimon Glass * Inits and then prints test pattern if required. 660be8f203SSimon Glass */ 670be8f203SSimon Glass 680be8f203SSimon Glass void lcd_ctrl_init(void *lcdbase) 690be8f203SSimon Glass { 7044706a87SSimon Glass int type = DCACHE_OFF; 710c558754SThierry Reding int size; 720be8f203SSimon Glass 730be8f203SSimon Glass assert(disp_config); 740be8f203SSimon Glass 750be8f203SSimon Glass /* Make sure that we can acommodate the selected LCD */ 760be8f203SSimon Glass assert(disp_config->width <= LCD_MAX_WIDTH); 770be8f203SSimon Glass assert(disp_config->height <= LCD_MAX_HEIGHT); 780be8f203SSimon Glass assert(disp_config->log2_bpp <= LCD_MAX_LOG2_BPP); 790be8f203SSimon Glass if (disp_config->width <= LCD_MAX_WIDTH 800be8f203SSimon Glass && disp_config->height <= LCD_MAX_HEIGHT 810be8f203SSimon Glass && disp_config->log2_bpp <= LCD_MAX_LOG2_BPP) 820be8f203SSimon Glass update_panel_size(disp_config); 830c558754SThierry Reding size = lcd_get_size(&lcd_line_length); 840be8f203SSimon Glass 8544706a87SSimon Glass /* Set up the LCD caching as requested */ 8644706a87SSimon Glass if (config.cache_type & FDT_LCD_CACHE_WRITE_THROUGH) 8744706a87SSimon Glass type = DCACHE_WRITETHROUGH; 8844706a87SSimon Glass else if (config.cache_type & FDT_LCD_CACHE_WRITE_BACK) 8944706a87SSimon Glass type = DCACHE_WRITEBACK; 9044706a87SSimon Glass mmu_set_region_dcache_behaviour(disp_config->frame_buffer, size, type); 9144706a87SSimon Glass 9244706a87SSimon Glass /* Enable flushing after LCD writes if requested */ 9344706a87SSimon Glass lcd_set_flush_dcache(config.cache_type & FDT_LCD_CACHE_FLUSH); 9444706a87SSimon Glass 9500a0ca59SJeroen Hofstee debug("LCD frame buffer at %08X\n", disp_config->frame_buffer); 960be8f203SSimon Glass } 970be8f203SSimon Glass 980be8f203SSimon Glass ulong calc_fbsize(void) 990be8f203SSimon Glass { 1000be8f203SSimon Glass return (panel_info.vl_col * panel_info.vl_row * 1010be8f203SSimon Glass NBITS(panel_info.vl_bpix)) / 8; 1020be8f203SSimon Glass } 1030be8f203SSimon Glass 1040be8f203SSimon Glass void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue) 1050be8f203SSimon Glass { 1060be8f203SSimon Glass } 1070be8f203SSimon Glass 1080be8f203SSimon Glass void tegra_lcd_early_init(const void *blob) 1090be8f203SSimon Glass { 1100be8f203SSimon Glass /* 1110be8f203SSimon Glass * Go with the maximum size for now. We will fix this up after 1120be8f203SSimon Glass * relocation. These values are only used for memory alocation. 1130be8f203SSimon Glass */ 1140be8f203SSimon Glass panel_info.vl_col = LCD_MAX_WIDTH; 1150be8f203SSimon Glass panel_info.vl_row = LCD_MAX_HEIGHT; 1160be8f203SSimon Glass panel_info.vl_bpix = LCD_MAX_LOG2_BPP; 1170be8f203SSimon Glass } 1180be8f203SSimon Glass 1190be8f203SSimon Glass /** 1200be8f203SSimon Glass * Decode the panel information from the fdt. 1210be8f203SSimon Glass * 1220be8f203SSimon Glass * @param blob fdt blob 1230be8f203SSimon Glass * @param config structure to store fdt config into 1240be8f203SSimon Glass * @return 0 if ok, -ve on error 1250be8f203SSimon Glass */ 1260be8f203SSimon Glass static int fdt_decode_lcd(const void *blob, struct fdt_panel_config *config) 1270be8f203SSimon Glass { 1280be8f203SSimon Glass int display_node; 1290be8f203SSimon Glass 1300be8f203SSimon Glass disp_config = tegra_display_get_config(); 1310be8f203SSimon Glass if (!disp_config) { 1320be8f203SSimon Glass debug("%s: Display controller is not configured\n", __func__); 1330be8f203SSimon Glass return -1; 1340be8f203SSimon Glass } 1350be8f203SSimon Glass display_node = disp_config->panel_node; 1360be8f203SSimon Glass if (display_node < 0) { 1370be8f203SSimon Glass debug("%s: No panel configuration available\n", __func__); 1380be8f203SSimon Glass return -1; 1390be8f203SSimon Glass } 1400be8f203SSimon Glass 1410be8f203SSimon Glass config->pwm_channel = pwm_request(blob, display_node, "nvidia,pwm"); 1420be8f203SSimon Glass if (config->pwm_channel < 0) { 1430be8f203SSimon Glass debug("%s: Unable to request PWM channel\n", __func__); 1440be8f203SSimon Glass return -1; 1450be8f203SSimon Glass } 1460be8f203SSimon Glass 1470be8f203SSimon Glass config->cache_type = fdtdec_get_int(blob, display_node, 1480be8f203SSimon Glass "nvidia,cache-type", 1490be8f203SSimon Glass FDT_LCD_CACHE_WRITE_BACK_FLUSH); 1500be8f203SSimon Glass 1510be8f203SSimon Glass /* These GPIOs are all optional */ 152*04072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 153*04072cbaSSimon Glass "nvidia,backlight-enable-gpios", 0, 154*04072cbaSSimon Glass &config->backlight_en, GPIOD_IS_OUT); 155*04072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 156*04072cbaSSimon Glass "nvidia,lvds-shutdown-gpios", 0, 157*04072cbaSSimon Glass &config->lvds_shutdown, GPIOD_IS_OUT); 158*04072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 159*04072cbaSSimon Glass "nvidia,backlight-vdd-gpios", 0, 160*04072cbaSSimon Glass &config->backlight_vdd, GPIOD_IS_OUT); 161*04072cbaSSimon Glass gpio_request_by_name_nodev(blob, display_node, 162*04072cbaSSimon Glass "nvidia,panel-vdd-gpios", 0, 163*04072cbaSSimon Glass &config->panel_vdd, GPIOD_IS_OUT); 1640be8f203SSimon Glass 1650be8f203SSimon Glass return fdtdec_get_int_array(blob, display_node, "nvidia,panel-timings", 1660be8f203SSimon Glass config->panel_timings, FDT_LCD_TIMINGS); 1670be8f203SSimon Glass } 1680be8f203SSimon Glass 1690be8f203SSimon Glass /** 1700be8f203SSimon Glass * Handle the next stage of device init 1710be8f203SSimon Glass */ 1720be8f203SSimon Glass static int handle_stage(const void *blob) 1730be8f203SSimon Glass { 1740be8f203SSimon Glass debug("%s: stage %d\n", __func__, stage); 1750be8f203SSimon Glass 1760be8f203SSimon Glass /* do the things for this stage */ 1770be8f203SSimon Glass switch (stage) { 1780be8f203SSimon Glass case STAGE_START: 1790be8f203SSimon Glass /* Initialize the Tegra display controller */ 1800be8f203SSimon Glass if (tegra_display_probe(gd->fdt_blob, (void *)gd->fb_base)) { 1810be8f203SSimon Glass printf("%s: Failed to probe display driver\n", 1820be8f203SSimon Glass __func__); 1830be8f203SSimon Glass return -1; 1840be8f203SSimon Glass } 1850be8f203SSimon Glass 1860be8f203SSimon Glass /* get panel details */ 1870be8f203SSimon Glass if (fdt_decode_lcd(blob, &config)) { 1880be8f203SSimon Glass printf("No valid LCD information in device tree\n"); 1890be8f203SSimon Glass return -1; 1900be8f203SSimon Glass } 1910be8f203SSimon Glass 1920be8f203SSimon Glass /* 1930be8f203SSimon Glass * It is possible that the FDT has requested that the LCD be 1940be8f203SSimon Glass * disabled. We currently don't support this. It would require 1950be8f203SSimon Glass * changes to U-Boot LCD subsystem to have LCD support 1960be8f203SSimon Glass * compiled in but not used. An easier option might be to 1970be8f203SSimon Glass * still have a frame buffer, but leave the backlight off and 1980be8f203SSimon Glass * remove all mention of lcd in the stdout environment 1990be8f203SSimon Glass * variable. 2000be8f203SSimon Glass */ 2010be8f203SSimon Glass 2020be8f203SSimon Glass funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); 2030be8f203SSimon Glass break; 2040be8f203SSimon Glass case STAGE_PANEL_VDD: 205*04072cbaSSimon Glass if (dm_gpio_is_valid(&config.panel_vdd)) 206*04072cbaSSimon Glass dm_gpio_set_value(&config.panel_vdd, 1); 2070be8f203SSimon Glass break; 2080be8f203SSimon Glass case STAGE_LVDS: 209*04072cbaSSimon Glass if (dm_gpio_is_valid(&config.lvds_shutdown)) 210*04072cbaSSimon Glass dm_gpio_set_value(&config.lvds_shutdown, 1); 2110be8f203SSimon Glass break; 2120be8f203SSimon Glass case STAGE_BACKLIGHT_VDD: 213*04072cbaSSimon Glass if (dm_gpio_is_valid(&config.backlight_vdd)) 214*04072cbaSSimon Glass dm_gpio_set_value(&config.backlight_vdd, 1); 2150be8f203SSimon Glass break; 2160be8f203SSimon Glass case STAGE_PWM: 2170be8f203SSimon Glass /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ 21870ad375eSStephen Warren pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); 21970ad375eSStephen Warren pinmux_tristate_disable(PMUX_PINGRP_GPU); 2200be8f203SSimon Glass 2210be8f203SSimon Glass pwm_enable(config.pwm_channel, 32768, 0xdf, 1); 2220be8f203SSimon Glass break; 2230be8f203SSimon Glass case STAGE_BACKLIGHT_EN: 224*04072cbaSSimon Glass if (dm_gpio_is_valid(&config.backlight_en)) 225*04072cbaSSimon Glass dm_gpio_set_value(&config.backlight_en, 1); 2260be8f203SSimon Glass break; 2270be8f203SSimon Glass case STAGE_DONE: 2280be8f203SSimon Glass break; 2290be8f203SSimon Glass } 2300be8f203SSimon Glass 2310be8f203SSimon Glass /* set up timer for next stage */ 2320be8f203SSimon Glass timer_next = timer_get_us(); 2330be8f203SSimon Glass if (stage < FDT_LCD_TIMINGS) 2340be8f203SSimon Glass timer_next += config.panel_timings[stage] * 1000; 2350be8f203SSimon Glass 2360be8f203SSimon Glass /* move to next stage */ 2370be8f203SSimon Glass stage++; 2380be8f203SSimon Glass return 0; 2390be8f203SSimon Glass } 2400be8f203SSimon Glass 2410be8f203SSimon Glass int tegra_lcd_check_next_stage(const void *blob, int wait) 2420be8f203SSimon Glass { 2430be8f203SSimon Glass if (stage == STAGE_DONE) 2440be8f203SSimon Glass return 0; 2450be8f203SSimon Glass 2460be8f203SSimon Glass do { 2470be8f203SSimon Glass /* wait if we need to */ 2480be8f203SSimon Glass debug("%s: stage %d\n", __func__, stage); 2490be8f203SSimon Glass if (stage != STAGE_START) { 2500be8f203SSimon Glass int delay = timer_next - timer_get_us(); 2510be8f203SSimon Glass 2520be8f203SSimon Glass if (delay > 0) { 2530be8f203SSimon Glass if (wait) 2540be8f203SSimon Glass udelay(delay); 2550be8f203SSimon Glass else 2560be8f203SSimon Glass return 0; 2570be8f203SSimon Glass } 2580be8f203SSimon Glass } 2590be8f203SSimon Glass 2600be8f203SSimon Glass if (handle_stage(blob)) 2610be8f203SSimon Glass return -1; 2620be8f203SSimon Glass } while (wait && stage != STAGE_DONE); 2630be8f203SSimon Glass if (stage == STAGE_DONE) 2640be8f203SSimon Glass debug("%s: LCD init complete\n", __func__); 2650be8f203SSimon Glass 2660be8f203SSimon Glass return 0; 2670be8f203SSimon Glass } 2680be8f203SSimon Glass 2690be8f203SSimon Glass void lcd_enable(void) 2700be8f203SSimon Glass { 2710be8f203SSimon Glass /* 2720be8f203SSimon Glass * Backlight and power init will be done separately in 2730be8f203SSimon Glass * tegra_lcd_check_next_stage(), which should be called in 2740be8f203SSimon Glass * board_late_init(). 2750be8f203SSimon Glass * 2760be8f203SSimon Glass * U-Boot code supports only colour depth, selected at compile time. 2770be8f203SSimon Glass * The device tree setting should match this. Otherwise the display 2780be8f203SSimon Glass * will not look right, and U-Boot may crash. 2790be8f203SSimon Glass */ 2800be8f203SSimon Glass if (disp_config->log2_bpp != LCD_BPP) { 2810be8f203SSimon Glass printf("%s: Error: LCD depth configured in FDT (%d = %dbpp)" 2820be8f203SSimon Glass " must match setting of LCD_BPP (%d)\n", __func__, 2830be8f203SSimon Glass disp_config->log2_bpp, disp_config->bpp, LCD_BPP); 2840be8f203SSimon Glass } 2850be8f203SSimon Glass } 286