X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;ds=sidebyside;f=drivers%2Fvideo%2Fsunxi_display.c;h=6cba1b95a110528dbda9d29d9370a7613f60d6d6;hb=9909aebc9d0820aa4f35308f3d8a8d7821e9f8e5;hp=18681850587c61e9bddb638c8b38db520a2383c7;hpb=e2c1c5bae619d2e87505de99f907a26237640bc9;p=u-boot diff --git a/drivers/video/sunxi_display.c b/drivers/video/sunxi_display.c index 1868185058..6cba1b95a1 100644 --- a/drivers/video/sunxi_display.c +++ b/drivers/video/sunxi_display.c @@ -12,15 +12,19 @@ #include #include #include +#include #include #include #include +#include #include #include #include #include +#include #include #include "videomodes.h" +#include "anx9804.h" #include "hitachi_tx18d42vm_lcd.h" #include "ssd2828.h" @@ -51,6 +55,7 @@ struct sunxi_display { GraphicDevice graphic_device; enum sunxi_monitor monitor; unsigned int depth; + unsigned int fb_addr; unsigned int fb_size; } sunxi_display; @@ -483,7 +488,9 @@ static void sunxi_composer_mode_set(const struct ctfb_res_modes *mode, setbits_le32(&de_be->mode, SUNXI_DE_BE_MODE_LAYER0_ENABLE); if (mode->vmode == FB_VMODE_INTERLACED) setbits_le32(&de_be->mode, +#ifndef CONFIG_MACH_SUN5I SUNXI_DE_BE_MODE_DEFLICKER_ENABLE | +#endif SUNXI_DE_BE_MODE_INTERLACE_ENABLE); if (sunxi_is_composite()) { @@ -517,6 +524,7 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, int value, n, m, min_m, max_m, diff; int best_n = 0, best_m = 0, best_diff = 0x0FFFFFFF; int best_double = 0; + bool use_mipi_pll = false; if (tcon == 0) { #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL @@ -567,16 +575,42 @@ static void sunxi_lcdc_pll_set(int tcon, int dotclock, } } - debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", - dotclock, (best_double + 1) * 3000 * best_n / best_m, - best_double + 1, best_n, best_m); +#ifdef CONFIG_MACH_SUN6I + /* + * Use the MIPI pll if we've been unable to find any matching setting + * for PLL3, this happens with high dotclocks because of min_m = 6. + */ + if (tcon == 0 && best_n == 0) { + use_mipi_pll = true; + best_m = 6; /* Minimum m for tcon0 */ + } - clock_set_pll3(best_n * 3000000); + if (use_mipi_pll) { + clock_set_pll3(297000000); /* Fix the video pll at 297 MHz */ + clock_set_mipi_pll(best_m * dotclock * 1000); + debug("dotclock: %dkHz = %dkHz via mipi pll\n", + dotclock, clock_get_mipi_pll() / best_m / 1000); + } else +#endif + { + clock_set_pll3(best_n * 3000000); + debug("dotclock: %dkHz = %dkHz: (%d * 3MHz * %d) / %d\n", + dotclock, + (best_double + 1) * clock_get_pll3() / best_m / 1000, + best_double + 1, best_n, best_m); + } if (tcon == 0) { - writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | - (best_double ? CCM_LCD_CH0_CTRL_PLL3_2X : - CCM_LCD_CH0_CTRL_PLL3), + u32 pll; + + if (use_mipi_pll) + pll = CCM_LCD_CH0_CTRL_MIPI_PLL; + else if (best_double) + pll = CCM_LCD_CH0_CTRL_PLL3_2X; + else + pll = CCM_LCD_CH0_CTRL_PLL3; + + writel(CCM_LCD_CH0_CTRL_GATE | CCM_LCD_CH0_CTRL_RST | pll, &ccm->lcd0_ch0_clk_cfg); } else { writel(CCM_LCD_CH1_CTRL_GATE | @@ -710,6 +744,16 @@ static void sunxi_lcdc_backlight_enable(void) gpio_direction_output(pin, 1); pin = sunxi_name_to_gpio(CONFIG_VIDEO_LCD_BL_PWM); +#ifdef SUNXI_PWM_PIN0 + if (pin == SUNXI_PWM_PIN0) { + writel(SUNXI_PWM_CTRL_POLARITY0(PWM_ON) | + SUNXI_PWM_CTRL_ENABLE0 | + SUNXI_PWM_CTRL_PRESCALE0(0xf), SUNXI_PWM_CTRL_REG); + writel(SUNXI_PWM_PERIOD_80PCT, SUNXI_PWM_CH0_PERIOD); + sunxi_gpio_set_cfgpin(pin, SUNXI_PWM_MUX); + return; + } +#endif if (pin >= 0) gpio_direction_output(pin, PWM_ON); } @@ -734,13 +778,21 @@ static void sunxi_lcdc_tcon0_mode_set(const struct ctfb_res_modes *mode, (struct sunxi_lcdc_reg *)SUNXI_LCD0_BASE; int bp, clk_delay, clk_div, clk_double, pin, total, val; - for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) +#if defined CONFIG_MACH_SUN8I && defined CONFIG_VIDEO_LCD_IF_LVDS + for (pin = SUNXI_GPD(18); pin <= SUNXI_GPD(27); pin++) { +#else + for (pin = SUNXI_GPD(0); pin <= SUNXI_GPD(27); pin++) { +#endif #ifdef CONFIG_VIDEO_LCD_IF_PARALLEL sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LCD0); #endif #ifdef CONFIG_VIDEO_LCD_IF_LVDS sunxi_gpio_set_cfgpin(pin, SUNXI_GPD_LVDS0); #endif +#ifdef CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804 + sunxi_gpio_set_drv(pin, 3); +#endif + } sunxi_lcdc_pll_set(0, mode->pixclock_khz, &clk_div, &clk_double); @@ -872,6 +924,13 @@ static void sunxi_lcdc_tcon1_mode_set(const struct ctfb_res_modes *mode, SUNXI_LCDC_TCON_VSYNC_MASK | SUNXI_LCDC_TCON_HSYNC_MASK); } + +#ifdef CONFIG_MACH_SUN5I + if (sunxi_is_composite()) + clrsetbits_le32(&lcdc->mux_ctrl, SUNXI_LCDC_MUX_CTRL_SRC0_MASK, + SUNXI_LCDC_MUX_CTRL_SRC0(1)); +#endif + sunxi_lcdc_pll_set(1, mode->pixclock_khz, clk_div, clk_double); } #endif /* CONFIG_VIDEO_HDMI || defined CONFIG_VIDEO_VGA || CONFIG_VIDEO_COMPOSITE */ @@ -997,6 +1056,8 @@ static void sunxi_tvencoder_mode_set(void) struct sunxi_tve_reg * const tve = (struct sunxi_tve_reg *)SUNXI_TVE0_BASE; + /* Reset off */ + setbits_le32(&ccm->lcd0_ch0_clk_cfg, CCM_LCD_CH0_CTRL_TVE_RST); /* Clock on */ setbits_le32(&ccm->ahb_gate1, 1 << AHB_GATE_OFFSET_TVE0); @@ -1168,6 +1229,17 @@ static void sunxi_mode_set(const struct ctfb_res_modes *mode, break; case sunxi_monitor_lcd: sunxi_lcdc_panel_enable(); + if (IS_ENABLED(CONFIG_VIDEO_LCD_PANEL_EDP_4_LANE_1620M_VIA_ANX9804)) { + /* + * The anx9804 needs 1.8V from eldo3, we do this here + * and not via CONFIG_AXP_ELDO3_VOLT from board_init() + * to avoid turning this on when using hdmi output. + */ + axp_set_eldo(3, 1800); + anx9804_init(CONFIG_VIDEO_LCD_I2C_BUS, 4, + ANX9804_DATA_RATE_1620M, + sunxi_display.depth); + } if (IS_ENABLED(CONFIG_VIDEO_LCD_HITACHI_TX18D42VM)) { mdelay(50); /* Wait for lcd controller power on */ hitachi_tx18d42vm_init(); @@ -1297,9 +1369,10 @@ void *video_hw_init(void) #ifdef CONFIG_VIDEO_HDMI int ret, hpd, hpd_delay, edid; #endif + int i, overscan_offset, overscan_x, overscan_y; + unsigned int fb_dma_addr; char mon[16]; char *lcd_mode = CONFIG_VIDEO_LCD_MODE; - int i; memset(&sunxi_display, 0, sizeof(struct sunxi_display)); @@ -1310,6 +1383,8 @@ void *video_hw_init(void) hpd_delay = video_get_option_int(options, "hpd_delay", 500); edid = video_get_option_int(options, "edid", 1); #endif + overscan_x = video_get_option_int(options, "overscan_x", -1); + overscan_y = video_get_option_int(options, "overscan_y", -1); sunxi_display.monitor = sunxi_get_default_mon(true); video_get_option_string(options, "monitor", mon, sizeof(mon), sunxi_get_mon_desc(sunxi_display.monitor)); @@ -1386,8 +1461,20 @@ void *video_hw_init(void) break; } + /* Yes these defaults are quite high, overscan on composite sucks... */ + if (overscan_x == -1) + overscan_x = sunxi_is_composite() ? 32 : 0; + if (overscan_y == -1) + overscan_y = sunxi_is_composite() ? 20 : 0; + sunxi_display.fb_size = (mode->xres * mode->yres * 4 + 0xfff) & ~0xfff; + overscan_offset = (overscan_y * mode->xres + overscan_x) * 4; + /* We want to keep the fb_base for simplefb page aligned, where as + * the sunxi dma engines will happily accept an unaligned address. */ + if (overscan_offset) + sunxi_display.fb_size += 0x1000; + if (sunxi_display.fb_size > CONFIG_SUNXI_MAX_FB_SIZE) { printf("Error need %dkB for fb, but only %dkB is reserved\n", sunxi_display.fb_size >> 10, @@ -1395,25 +1482,36 @@ void *video_hw_init(void) return NULL; } - printf("Setting up a %dx%d%s %s console\n", mode->xres, mode->yres, + printf("Setting up a %dx%d%s %s console (overscan %dx%d)\n", + mode->xres, mode->yres, (mode->vmode == FB_VMODE_INTERLACED) ? "i" : "", - sunxi_get_mon_desc(sunxi_display.monitor)); + sunxi_get_mon_desc(sunxi_display.monitor), + overscan_x, overscan_y); gd->fb_base = gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sunxi_display.fb_size; sunxi_engines_init(); - sunxi_mode_set(mode, gd->fb_base - CONFIG_SYS_SDRAM_BASE); + + fb_dma_addr = gd->fb_base - CONFIG_SYS_SDRAM_BASE; + sunxi_display.fb_addr = gd->fb_base; + if (overscan_offset) { + fb_dma_addr += 0x1000 - (overscan_offset & 0xfff); + sunxi_display.fb_addr += (overscan_offset + 0xfff) & ~0xfff; + memset((void *)gd->fb_base, 0, sunxi_display.fb_size); + flush_cache(gd->fb_base, sunxi_display.fb_size); + } + sunxi_mode_set(mode, fb_dma_addr); /* * These are the only members of this structure that are used. All the - * others are driver specific. There is nothing to decribe pitch or - * stride, but we are lucky with our hw. + * others are driver specific. The pitch is stored in plnSizeX. */ - graphic_device->frameAdrs = gd->fb_base; + graphic_device->frameAdrs = sunxi_display.fb_addr; graphic_device->gdfIndex = GDF_32BIT_X888RGB; graphic_device->gdfBytesPP = 4; - graphic_device->winSizeX = mode->xres; - graphic_device->winSizeY = mode->yres; + graphic_device->winSizeX = mode->xres - 2 * overscan_x; + graphic_device->winSizeY = mode->yres - 2 * overscan_y; + graphic_device->plnSizeX = mode->xres * graphic_device->gdfBytesPP; return graphic_device; } @@ -1464,7 +1562,7 @@ int sunxi_simplefb_setup(void *blob) offset = fdt_node_offset_by_compatible(blob, -1, "allwinner,simple-framebuffer"); while (offset >= 0) { - ret = fdt_find_string(blob, offset, "allwinner,pipeline", + ret = fdt_stringlist_search(blob, offset, "allwinner,pipeline", pipeline); if (ret == 0) break; @@ -1490,10 +1588,9 @@ int sunxi_simplefb_setup(void *blob) return ret; } - ret = fdt_setup_simplefb_node(blob, offset, gd->fb_base, + ret = fdt_setup_simplefb_node(blob, offset, sunxi_display.fb_addr, graphic_device->winSizeX, graphic_device->winSizeY, - graphic_device->winSizeX * graphic_device->gdfBytesPP, - "x8r8g8b8"); + graphic_device->plnSizeX, "x8r8g8b8"); if (ret) eprintf("Cannot setup simplefb: Error setting properties\n");