diff options
Diffstat (limited to 'drivers/video/tegra.c')
-rw-r--r-- | drivers/video/tegra.c | 333 |
1 files changed, 52 insertions, 281 deletions
diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c index c01809e..217f05f 100644 --- a/drivers/video/tegra.c +++ b/drivers/video/tegra.c @@ -6,6 +6,7 @@ #include <common.h> #include <dm.h> #include <fdtdec.h> +#include <panel.h> #include <pwm.h> #include <video.h> #include <asm/system.h> @@ -21,70 +22,16 @@ DECLARE_GLOBAL_DATA_PTR; -/* These are the stages we go throuh in enabling the LCD */ -enum stage_t { - STAGE_START, - STAGE_PANEL_VDD, - STAGE_LVDS, - STAGE_BACKLIGHT_VDD, - STAGE_PWM, - STAGE_BACKLIGHT_EN, - STAGE_DONE, -}; - -#define FDT_LCD_TIMINGS 4 - -enum { - FDT_LCD_TIMING_REF_TO_SYNC, - FDT_LCD_TIMING_SYNC_WIDTH, - FDT_LCD_TIMING_BACK_PORCH, - FDT_LCD_TIMING_FRONT_PORCH, - - FDT_LCD_TIMING_COUNT, -}; - -enum lcd_cache_t { - FDT_LCD_CACHE_OFF = 0, - FDT_LCD_CACHE_WRITE_THROUGH = 1 << 0, - FDT_LCD_CACHE_WRITE_BACK = 1 << 1, - FDT_LCD_CACHE_FLUSH = 1 << 2, - FDT_LCD_CACHE_WRITE_BACK_FLUSH = FDT_LCD_CACHE_WRITE_BACK | - FDT_LCD_CACHE_FLUSH, -}; - /* Information about the display controller */ struct tegra_lcd_priv { - enum stage_t stage; /* Current stage we are at */ - unsigned long timer_next; /* Time we can move onto next stage */ int width; /* width in pixels */ int height; /* height in pixels */ - - /* - * log2 of number of bpp, in general, unless it bpp is 24 in which - * case this field holds 24 also! This is a U-Boot thing. - */ - int log2_bpp; + enum video_log2_bpp log2_bpp; /* colour depth */ + struct display_timing timing; + struct udevice *panel; struct disp_ctlr *disp; /* Display controller to use */ fdt_addr_t frame_buffer; /* Address of frame buffer */ unsigned pixel_clock; /* Pixel clock in Hz */ - uint horiz_timing[FDT_LCD_TIMING_COUNT]; /* Horizontal timing */ - uint vert_timing[FDT_LCD_TIMING_COUNT]; /* Vertical timing */ - struct udevice *pwm; - int pwm_channel; /* PWM channel to use for backlight */ - enum lcd_cache_t cache_type; - - struct gpio_desc backlight_en; /* GPIO for backlight enable */ - struct gpio_desc lvds_shutdown; /* GPIO for lvds shutdown */ - struct gpio_desc backlight_vdd; /* GPIO for backlight vdd */ - struct gpio_desc panel_vdd; /* GPIO for panel vdd */ - /* - * Panel required timings - * Timing 1: delay between panel_vdd-rise and data-rise - * Timing 2: delay between data-rise and backlight_vdd-rise - * Timing 3: delay between backlight_vdd and pwm-rise - * Timing 4: delay between pwm-rise and backlight_en-rise - */ - uint panel_timings[FDT_LCD_TIMINGS]; }; enum { @@ -150,26 +97,23 @@ static void update_window(struct dc_ctlr *dc, struct disp_ctl_win *win) writel(val, &dc->cmd.state_ctrl); } -static void write_pair(struct tegra_lcd_priv *priv, int item, u32 *reg) -{ - writel(priv->horiz_timing[item] | - (priv->vert_timing[item] << 16), reg); -} - static int update_display_mode(struct dc_disp_reg *disp, struct tegra_lcd_priv *priv) { + struct display_timing *dt = &priv->timing; unsigned long val; unsigned long rate; unsigned long div; writel(0x0, &disp->disp_timing_opt); - write_pair(priv, FDT_LCD_TIMING_REF_TO_SYNC, &disp->ref_to_sync); - write_pair(priv, FDT_LCD_TIMING_SYNC_WIDTH, &disp->sync_width); - write_pair(priv, FDT_LCD_TIMING_BACK_PORCH, &disp->back_porch); - write_pair(priv, FDT_LCD_TIMING_FRONT_PORCH, &disp->front_porch); - writel(priv->width | (priv->height << 16), &disp->disp_active); + writel(1 | 1 << 16, &disp->ref_to_sync); + writel(dt->hsync_len.typ | dt->vsync_len.typ << 16, &disp->sync_width); + writel(dt->hback_porch.typ | dt->vback_porch.typ << 16, + &disp->back_porch); + writel((dt->hfront_porch.typ - 1) | (dt->vfront_porch.typ - 1) << 16, + &disp->front_porch); + writel(dt->hactive.typ | (dt->vactive.typ << 16), &disp->disp_active); val = DE_SELECT_ACTIVE << DE_SELECT_SHIFT; val |= DE_CONTROL_NORMAL << DE_CONTROL_SHIFT; @@ -287,12 +231,11 @@ static int setup_window(struct disp_ctl_win *win, win->stride = priv->width * (1 << priv->log2_bpp) / 8; debug("%s: depth = %d\n", __func__, priv->log2_bpp); switch (priv->log2_bpp) { - case 5: - case 24: + case VIDEO_BPP32: win->fmt = COLOR_DEPTH_R8G8B8A8; win->bpp = 32; break; - case 4: + case VIDEO_BPP16: win->fmt = COLOR_DEPTH_B5G6R5; win->bpp = 16; break; @@ -305,18 +248,6 @@ static int setup_window(struct disp_ctl_win *win, return 0; } -static void debug_timing(const char *name, unsigned int timing[]) -{ -#ifdef DEBUG - int i; - - debug("%s timing: ", name); - for (i = 0; i < FDT_LCD_TIMING_COUNT; i++) - debug("%d ", timing[i]); - debug("\n"); -#endif -} - /** * Register a new display based on device tree configuration. * @@ -363,137 +294,35 @@ static int tegra_display_probe(const void *blob, struct tegra_lcd_priv *priv, return 0; } -/** - * Handle the next stage of device init - */ -static int handle_stage(const void *blob, struct tegra_lcd_priv *priv) -{ - debug("%s: stage %d\n", __func__, priv->stage); - - /* do the things for this stage */ - switch (priv->stage) { - case STAGE_START: - /* - * It is possible that the FDT has requested that the LCD be - * disabled. We currently don't support this. It would require - * changes to U-Boot LCD subsystem to have LCD support - * compiled in but not used. An easier option might be to - * still have a frame buffer, but leave the backlight off and - * remove all mention of lcd in the stdout environment - * variable. - */ - - funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); - break; - case STAGE_PANEL_VDD: - if (dm_gpio_is_valid(&priv->panel_vdd)) - dm_gpio_set_value(&priv->panel_vdd, 1); - break; - case STAGE_LVDS: - if (dm_gpio_is_valid(&priv->lvds_shutdown)) - dm_gpio_set_value(&priv->lvds_shutdown, 1); - break; - case STAGE_BACKLIGHT_VDD: - if (dm_gpio_is_valid(&priv->backlight_vdd)) - dm_gpio_set_value(&priv->backlight_vdd, 1); - break; - case STAGE_PWM: - /* Enable PWM at 15/16 high, 32768 Hz with divider 1 */ - pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); - pinmux_tristate_disable(PMUX_PINGRP_GPU); - - pwm_set_config(priv->pwm, priv->pwm_channel, 0xdf, 0xff); - pwm_set_enable(priv->pwm, priv->pwm_channel, true); - break; - case STAGE_BACKLIGHT_EN: - if (dm_gpio_is_valid(&priv->backlight_en)) - dm_gpio_set_value(&priv->backlight_en, 1); - break; - case STAGE_DONE: - break; - } - - /* set up timer for next stage */ - priv->timer_next = timer_get_us(); - if (priv->stage < FDT_LCD_TIMINGS) - priv->timer_next += priv->panel_timings[priv->stage] * 1000; - - /* move to next stage */ - priv->stage++; - return 0; -} - -/** - * Perform the next stage of the LCD init if it is time to do so. - * - * LCD init can be time-consuming because of the number of delays we need - * while waiting for the backlight power supply, etc. This function can - * be called at various times during U-Boot operation to advance the - * initialization of the LCD to the next stage if sufficient time has - * passed since the last stage. It keeps track of what stage it is up to - * and the time that it is permitted to move to the next stage. - * - * The final call should have wait=1 to complete the init. - * - * @param blob fdt blob containing LCD information - * @param wait 1 to wait until all init is complete, and then return - * 0 to return immediately, potentially doing nothing if it is - * not yet time for the next init. - */ -static int tegra_lcd_check_next_stage(const void *blob, - struct tegra_lcd_priv *priv, int wait) -{ - if (priv->stage == STAGE_DONE) - return 0; - - do { - /* wait if we need to */ - debug("%s: stage %d\n", __func__, priv->stage); - if (priv->stage != STAGE_START) { - int delay = priv->timer_next - timer_get_us(); - - if (delay > 0) { - if (wait) - udelay(delay); - else - return 0; - } - } - - if (handle_stage(blob, priv)) - return -1; - } while (wait && priv->stage != STAGE_DONE); - if (priv->stage == STAGE_DONE) - debug("%s: LCD init complete\n", __func__); - - return 0; -} - static int tegra_lcd_probe(struct udevice *dev) { struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); struct video_priv *uc_priv = dev_get_uclass_priv(dev); struct tegra_lcd_priv *priv = dev_get_priv(dev); const void *blob = gd->fdt_blob; - int type = DCACHE_OFF; + int ret; /* Initialize the Tegra display controller */ + funcmux_select(PERIPH_ID_DISP1, FUNCMUX_DEFAULT); if (tegra_display_probe(blob, priv, (void *)plat->base)) { printf("%s: Failed to probe display driver\n", __func__); return -1; } - tegra_lcd_check_next_stage(blob, priv, 1); + pinmux_set_func(PMUX_PINGRP_GPU, PMUX_FUNC_PWM); + pinmux_tristate_disable(PMUX_PINGRP_GPU); + + ret = panel_enable_backlight(priv->panel); + if (ret) { + debug("%s: Cannot enable backlight, ret=%d\n", __func__, ret); + return ret; + } - /* Set up the LCD caching as requested */ - if (priv->cache_type & FDT_LCD_CACHE_WRITE_THROUGH) - type = DCACHE_WRITETHROUGH; - else if (priv->cache_type & FDT_LCD_CACHE_WRITE_BACK) - type = DCACHE_WRITEBACK; - mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, type); + mmu_set_region_dcache_behaviour(priv->frame_buffer, plat->size, + DCACHE_WRITETHROUGH); /* Enable flushing after LCD writes if requested */ - video_set_flush_dcache(dev, priv->cache_type & FDT_LCD_CACHE_FLUSH); + video_set_flush_dcache(dev, true); uc_priv->xsize = priv->width; uc_priv->ysize = priv->height; @@ -507,13 +336,11 @@ static int tegra_lcd_probe(struct udevice *dev) static int tegra_lcd_ofdata_to_platdata(struct udevice *dev) { struct tegra_lcd_priv *priv = dev_get_priv(dev); - struct fdtdec_phandle_args args; const void *blob = gd->fdt_blob; + struct display_timing *timing; int node = dev->of_offset; - int front, back, ref; int panel_node; int rgb; - int bpp, bit; int ret; priv->disp = (struct disp_ctlr *)dev_get_addr(dev); @@ -523,96 +350,40 @@ static int tegra_lcd_ofdata_to_platdata(struct udevice *dev) } rgb = fdt_subnode_offset(blob, node, "rgb"); - - panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); - if (panel_node < 0) { - debug("%s: Cannot find panel information\n", __func__); + if (rgb < 0) { + debug("%s: Cannot find rgb subnode for '%s' (ret=%d)\n", + __func__, dev->name, rgb); return -EINVAL; } - priv->width = fdtdec_get_int(blob, panel_node, "xres", -1); - priv->height = fdtdec_get_int(blob, panel_node, "yres", -1); - priv->pixel_clock = fdtdec_get_int(blob, panel_node, "clock", 0); - if (!priv->pixel_clock || priv->width == -1 || priv->height == -1) { - debug("%s: Pixel parameters missing\n", __func__); - return -EINVAL; - } - - back = fdtdec_get_int(blob, panel_node, "left-margin", -1); - front = fdtdec_get_int(blob, panel_node, "right-margin", -1); - ref = fdtdec_get_int(blob, panel_node, "hsync-len", -1); - if ((back | front | ref) == -1) { - debug("%s: Horizontal parameters missing\n", __func__); - return -EINVAL; - } - - /* Use a ref-to-sync of 1 always, and take this from the front porch */ - priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; - priv->horiz_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; - priv->horiz_timing[FDT_LCD_TIMING_BACK_PORCH] = back; - priv->horiz_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - - priv->horiz_timing[FDT_LCD_TIMING_REF_TO_SYNC]; - debug_timing("horiz", priv->horiz_timing); - - back = fdtdec_get_int(blob, panel_node, "upper-margin", -1); - front = fdtdec_get_int(blob, panel_node, "lower-margin", -1); - ref = fdtdec_get_int(blob, panel_node, "vsync-len", -1); - if ((back | front | ref) == -1) { - debug("%s: Vertical parameters missing\n", __func__); - return -EINVAL; - } - - priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC] = 1; - priv->vert_timing[FDT_LCD_TIMING_SYNC_WIDTH] = ref; - priv->vert_timing[FDT_LCD_TIMING_BACK_PORCH] = back; - priv->vert_timing[FDT_LCD_TIMING_FRONT_PORCH] = front - - priv->vert_timing[FDT_LCD_TIMING_REF_TO_SYNC]; - debug_timing("vert", priv->vert_timing); - - bpp = fdtdec_get_int(blob, panel_node, "nvidia,bits-per-pixel", -1); - bit = ffs(bpp) - 1; - if (bpp == (1 << bit)) - priv->log2_bpp = bit; - else - priv->log2_bpp = bpp; - if (bpp == -1) { - debug("%s: Pixel bpp parameters missing\n", __func__); + ret = fdtdec_decode_display_timing(blob, rgb, 0, &priv->timing); + if (ret) { + debug("%s: Cannot read display timing for '%s' (ret=%d)\n", + __func__, dev->name, ret); return -EINVAL; } + timing = &priv->timing; + priv->width = timing->hactive.typ; + priv->height = timing->vactive.typ; + priv->pixel_clock = timing->pixelclock.typ; + priv->log2_bpp = VIDEO_BPP16; - if (fdtdec_parse_phandle_with_args(blob, panel_node, "nvidia,pwm", - "#pwm-cells", 0, 0, &args)) { - debug("%s: Unable to decode PWM\n", __func__); + /* + * Sadly the panel phandle is in an rgb subnode so we cannot use + * uclass_get_device_by_phandle(). + */ + panel_node = fdtdec_lookup_phandle(blob, rgb, "nvidia,panel"); + if (panel_node < 0) { + debug("%s: Cannot find panel information\n", __func__); return -EINVAL; } - - ret = uclass_get_device_by_of_offset(UCLASS_PWM, args.node, &priv->pwm); + ret = uclass_get_device_by_of_offset(UCLASS_PANEL, panel_node, + &priv->panel); if (ret) { - debug("%s: Unable to find PWM\n", __func__); - return -EINVAL; + debug("%s: Cannot find panel for '%s' (ret=%d)\n", __func__, + dev->name, ret); + return ret; } - priv->pwm_channel = args.args[0]; - - priv->cache_type = fdtdec_get_int(blob, panel_node, "nvidia,cache-type", - FDT_LCD_CACHE_WRITE_BACK_FLUSH); - - /* These GPIOs are all optional */ - gpio_request_by_name_nodev(blob, panel_node, - "nvidia,backlight-enable-gpios", 0, - &priv->backlight_en, GPIOD_IS_OUT); - gpio_request_by_name_nodev(blob, panel_node, - "nvidia,lvds-shutdown-gpios", 0, - &priv->lvds_shutdown, GPIOD_IS_OUT); - gpio_request_by_name_nodev(blob, panel_node, - "nvidia,backlight-vdd-gpios", 0, - &priv->backlight_vdd, GPIOD_IS_OUT); - gpio_request_by_name_nodev(blob, panel_node, - "nvidia,panel-vdd-gpios", 0, - &priv->panel_vdd, GPIOD_IS_OUT); - - if (fdtdec_get_int_array(blob, panel_node, "nvidia,panel-timings", - priv->panel_timings, FDT_LCD_TIMINGS)) - return -EINVAL; return 0; } |