diff --git a/arch/arm/dts/imx7-colibri-emmc.dts b/arch/arm/dts/imx7-colibri-emmc.dts index efd600091d..8db2a62707 100644 --- a/arch/arm/dts/imx7-colibri-emmc.dts +++ b/arch/arm/dts/imx7-colibri-emmc.dts @@ -11,8 +11,10 @@ compatible = "toradex,imx7d-colibri-emmc", "fsl,imx7d"; aliases { + u-boot,dm-pre-reloc; mmc0 = &usdhc3; mmc1 = &usdhc1; + display1 = &lcdif; }; chosen { diff --git a/arch/arm/dts/imx7-colibri.dtsi b/arch/arm/dts/imx7-colibri.dtsi index a85702f519..81717c233d 100644 --- a/arch/arm/dts/imx7-colibri.dtsi +++ b/arch/arm/dts/imx7-colibri.dtsi @@ -111,3 +111,31 @@ >; }; }; + +&lcdif { + u-boot,dm-pre-reloc; + status = "okay"; + + display-timings { + native-mode = <&timing_vga>; + + /* Standard VGA timing */ + timing_vga: 640x480 { + u-boot,dm-pre-reloc; + clock-frequency = <25175000>; + hactive = <640>; + vactive = <480>; + hback-porch = <48>; + hfront-porch = <16>; + vback-porch = <33>; + vfront-porch = <10>; + hsync-len = <96>; + vsync-len = <2>; + + de-active = <1>; + hsync-active = <0>; + vsync-active = <0>; + pixelclk-active = <0>; + }; + }; +}; diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index 6b83f92662..64a0670fcf 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -298,7 +298,7 @@ void arch_preboot_os(void) /* disable video before launching O/S */ ipuv3_fb_shutdown(); #endif -#if defined(CONFIG_VIDEO_MXS) +#if defined(CONFIG_VIDEO_MXS) && !defined(CONFIG_DM_VIDEO) lcdif_power_down(); #endif } diff --git a/arch/arm/mach-imx/mx7/soc.c b/arch/arm/mach-imx/mx7/soc.c index 7cfdff0981..4a914fca5e 100644 --- a/arch/arm/mach-imx/mx7/soc.c +++ b/arch/arm/mach-imx/mx7/soc.c @@ -369,7 +369,7 @@ void s_init(void) void reset_misc(void) { #ifndef CONFIG_SPL_BUILD -#ifdef CONFIG_VIDEO_MXS +#if defined(CONFIG_VIDEO_MXS) && !defined(CONFIG_DM_VIDEO) lcdif_power_down(); #endif #endif diff --git a/common/lcd.c b/common/lcd.c index 95526b1e17..b34754fe51 100644 --- a/common/lcd.c +++ b/common/lcd.c @@ -171,8 +171,7 @@ int drv_lcd_init(void) void lcd_clear(void) { int bg_color; - char *s; - ulong addr; + __maybe_unused ulong addr; static int do_splash = 1; #if LCD_BPP == LCD_COLOR8 /* Setting the palette */ @@ -222,14 +221,10 @@ void lcd_clear(void) /* Paint the logo and retrieve LCD base address */ debug("[LCD] Drawing the logo...\n"); if (do_splash) { - s = env_get("splashimage"); - if (s) { + if (splash_display() == 0) { do_splash = 0; - addr = simple_strtoul(s, NULL, 16); - if (lcd_splash(addr) == 0) { - lcd_sync(); - return; - } + lcd_sync(); + return; } } diff --git a/common/splash.c b/common/splash.c index d251b3b654..79d50577ee 100644 --- a/common/splash.c +++ b/common/splash.c @@ -80,11 +80,23 @@ void splash_get_pos(int *x, int *y) } #endif /* CONFIG_SPLASH_SCREEN_ALIGN */ -#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_LCD) -int lcd_splash(ulong addr) +/* + * Common function to show a splash image if env("splashimage") is set. + * Is used for both dm_video and lcd video stacks. For additional + * details please refer to doc/README.splashprepare. + */ +#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_BMP) +int splash_display(void) { + ulong addr; + char *s; int x = 0, y = 0, ret; + s = env_get("splashimage"); + if (!s) + return -EINVAL; + + addr = simple_strtoul(s, NULL, 16); ret = splash_screen_prepare(); if (ret) return ret; diff --git a/common/stdio.c b/common/stdio.c index ee22c1fd58..54ecc9b53d 100644 --- a/common/stdio.c +++ b/common/stdio.c @@ -16,6 +16,7 @@ #include #include #include +#include #if defined(CONFIG_SYS_I2C) #include @@ -366,6 +367,9 @@ int stdio_add_devices(void) if (ret) printf("%s: Video device failed (ret=%d)\n", __func__, ret); #endif /* !CONFIG_SYS_CONSOLE_IS_IN_ENV */ +#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_BMP) + splash_display(); +#endif /* CONFIG_SPLASH_SCREEN && CONFIG_CMD_BMP */ #else # if defined(CONFIG_LCD) drv_lcd_init (); diff --git a/configs/colibri_imx6_defconfig b/configs/colibri_imx6_defconfig index 72e9128a69..9e307dae32 100644 --- a/configs/colibri_imx6_defconfig +++ b/configs/colibri_imx6_defconfig @@ -85,3 +85,4 @@ CONFIG_DM_VIDEO=y CONFIG_VIDEO_IPUV3=y CONFIG_FAT_WRITE=y CONFIG_OF_LIBFDT_OVERLAY=y +CONFIG_SYS_WHITE_ON_BLACK=y diff --git a/configs/colibri_imx7_emmc_defconfig b/configs/colibri_imx7_emmc_defconfig index af3cf93946..9312de9406 100644 --- a/configs/colibri_imx7_emmc_defconfig +++ b/configs/colibri_imx7_emmc_defconfig @@ -63,6 +63,6 @@ CONFIG_USB_GADGET_VENDOR_NUM=0x1b67 CONFIG_USB_GADGET_PRODUCT_NUM=0x4000 CONFIG_CI_UDC=y CONFIG_USB_GADGET_DOWNLOAD=y -CONFIG_VIDEO=y +CONFIG_DM_VIDEO=y CONFIG_FAT_WRITE=y CONFIG_OF_LIBFDT_OVERLAY=y diff --git a/configs/colibri_t20_defconfig b/configs/colibri_t20_defconfig index 15fb9555f3..d1bfd3c87a 100644 --- a/configs/colibri_t20_defconfig +++ b/configs/colibri_t20_defconfig @@ -64,3 +64,4 @@ CONFIG_DM_VIDEO=y CONFIG_VIDEO_TEGRA20=y CONFIG_CONSOLE_SCROLL_LINES=10 CONFIG_OF_LIBFDT_OVERLAY=y +CONFIG_SYS_WHITE_ON_BLACK=y diff --git a/drivers/video/meson/meson_dw_hdmi.c b/drivers/video/meson/meson_dw_hdmi.c index 7a1c060856..483c93f6b6 100644 --- a/drivers/video/meson/meson_dw_hdmi.c +++ b/drivers/video/meson/meson_dw_hdmi.c @@ -361,13 +361,19 @@ static int meson_dw_hdmi_probe(struct udevice *dev) priv->hdmi.i2c_clk_high = 0x67; priv->hdmi.i2c_clk_low = 0x78; +#if CONFIG_IS_ENABLED(DM_REGULATOR) ret = device_get_supply_regulator(dev, "hdmi-supply", &supply); - if (ret) + if (ret && ret != -ENOENT) { + pr_err("Failed to get HDMI regulator\n"); return ret; + } - ret = regulator_set_enable(supply, true); - if (ret) - return ret; + if (!ret) { + ret = regulator_set_enable(supply, true); + if (ret) + return ret; + } +#endif ret = reset_get_bulk(dev, &resets); if (ret) diff --git a/drivers/video/mxsfb.c b/drivers/video/mxsfb.c index 02fde05908..f02ba20138 100644 --- a/drivers/video/mxsfb.c +++ b/drivers/video/mxsfb.c @@ -5,22 +5,26 @@ * Copyright (C) 2011-2013 Marek Vasut */ #include +#include +#include #include +#include #include -#include #include +#include #include -#include -#include - #include +#include #include "videomodes.h" #define PS2KHZ(ps) (1000000000UL / (ps)) +#define HZ2PS(hz) (1000000000UL / ((hz) / 1000)) + +#define BITS_PP 18 +#define BYTES_PP 4 -static GraphicDevice panel; struct mxs_dma_desc desc; /** @@ -46,8 +50,7 @@ __weak void mxsfb_system_setup(void) * le:89,ri:164,up:23,lo:10,hs:10,vs:10,sync:0,vmode:0 */ -static void mxs_lcd_init(GraphicDevice *panel, - struct ctfb_res_modes *mode, int bpp) +static void mxs_lcd_init(u32 fb_addr, struct ctfb_res_modes *mode, int bpp) { struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; uint32_t word_len = 0, bus_width = 0; @@ -112,8 +115,8 @@ static void mxs_lcd_init(GraphicDevice *panel, writel((0 << LCDIF_VDCTRL4_DOTCLK_DLY_SEL_OFFSET) | mode->xres, ®s->hw_lcdif_vdctrl4); - writel(panel->frameAdrs, ®s->hw_lcdif_cur_buf); - writel(panel->frameAdrs, ®s->hw_lcdif_next_buf); + writel(fb_addr, ®s->hw_lcdif_cur_buf); + writel(fb_addr, ®s->hw_lcdif_next_buf); /* Flush FIFO first */ writel(LCDIF_CTRL1_FIFO_CLEAR, ®s->hw_lcdif_ctrl1_set); @@ -130,16 +133,47 @@ static void mxs_lcd_init(GraphicDevice *panel, writel(LCDIF_CTRL_RUN, ®s->hw_lcdif_ctrl_set); } -void lcdif_power_down(void) +static int mxs_probe_common(struct ctfb_res_modes *mode, int bpp, u32 fb) +{ + /* Start framebuffer */ + mxs_lcd_init(fb, mode, bpp); + +#ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM + /* + * If the LCD runs in system mode, the LCD refresh has to be triggered + * manually by setting the RUN bit in HW_LCDIF_CTRL register. To avoid + * having to set this bit manually after every single change in the + * framebuffer memory, we set up specially crafted circular DMA, which + * sets the RUN bit, then waits until it gets cleared and repeats this + * infinitelly. This way, we get smooth continuous updates of the LCD. + */ + struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; + + memset(&desc, 0, sizeof(struct mxs_dma_desc)); + desc.address = (dma_addr_t)&desc; + desc.cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | + MXS_DMA_DESC_WAIT4END | + (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); + desc.cmd.pio_words[0] = readl(®s->hw_lcdif_ctrl) | LCDIF_CTRL_RUN; + desc.cmd.next = (uint32_t)&desc.cmd; + + /* Execute the DMA chain. */ + mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc); +#endif + + return 0; +} + +static int mxs_remove_common(u32 fb) { struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; int timeout = 1000000; - if (!panel.frameAdrs) - return; + if (!fb) + return -EINVAL; - writel(panel.frameAdrs, ®s->hw_lcdif_cur_buf_reg); - writel(panel.frameAdrs, ®s->hw_lcdif_next_buf_reg); + writel(fb, ®s->hw_lcdif_cur_buf_reg); + writel(fb, ®s->hw_lcdif_next_buf_reg); writel(LCDIF_CTRL1_VSYNC_EDGE_IRQ, ®s->hw_lcdif_ctrl1_clr); while (--timeout) { if (readl(®s->hw_lcdif_ctrl1_reg) & @@ -148,13 +182,25 @@ void lcdif_power_down(void) udelay(1); } mxs_reset_block((struct mxs_register_32 *)®s->hw_lcdif_ctrl_reg); + + return 0; +} + +#ifndef CONFIG_DM_VIDEO + +static GraphicDevice panel; + +void lcdif_power_down(void) +{ + mxs_remove_common(panel.frameAdrs); } void *video_hw_init(void) { int bpp = -1; + int ret = 0; char *penv; - void *fb; + void *fb = NULL; struct ctfb_res_modes mode; puts("Video: "); @@ -169,8 +215,7 @@ void *video_hw_init(void) bpp = video_get_params(&mode, penv); /* fill in Graphic device struct */ - sprintf(panel.modeIdent, "%dx%dx%d", - mode.xres, mode.yres, bpp); + sprintf(panel.modeIdent, "%dx%dx%d", mode.xres, mode.yres, bpp); panel.winSizeX = mode.xres; panel.winSizeY = mode.yres; @@ -213,31 +258,125 @@ void *video_hw_init(void) printf("%s\n", panel.modeIdent); - /* Start framebuffer */ - mxs_lcd_init(&panel, &mode, bpp); - -#ifdef CONFIG_VIDEO_MXS_MODE_SYSTEM - /* - * If the LCD runs in system mode, the LCD refresh has to be triggered - * manually by setting the RUN bit in HW_LCDIF_CTRL register. To avoid - * having to set this bit manually after every single change in the - * framebuffer memory, we set up specially crafted circular DMA, which - * sets the RUN bit, then waits until it gets cleared and repeats this - * infinitelly. This way, we get smooth continuous updates of the LCD. - */ - struct mxs_lcdif_regs *regs = (struct mxs_lcdif_regs *)MXS_LCDIF_BASE; - - memset(&desc, 0, sizeof(struct mxs_dma_desc)); - desc.address = (dma_addr_t)&desc; - desc.cmd.data = MXS_DMA_DESC_COMMAND_NO_DMAXFER | MXS_DMA_DESC_CHAIN | - MXS_DMA_DESC_WAIT4END | - (1 << MXS_DMA_DESC_PIO_WORDS_OFFSET); - desc.cmd.pio_words[0] = readl(®s->hw_lcdif_ctrl) | LCDIF_CTRL_RUN; - desc.cmd.next = (uint32_t)&desc.cmd; - - /* Execute the DMA chain. */ - mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc); -#endif + ret = mxs_probe_common(&mode, bpp, (u32)fb); + if (ret) + goto dealloc_fb; return (void *)&panel; + +dealloc_fb: + free(fb); + + return NULL; } +#else /* ifndef CONFIG_DM_VIDEO */ + +static int mxs_video_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 ctfb_res_modes mode; + struct display_timing timings; + int bpp = -1; + u32 fb_start, fb_end; + int ret; + + debug("%s() plat: base 0x%lx, size 0x%x\n", + __func__, plat->base, plat->size); + + ret = ofnode_decode_display_timing(dev_ofnode(dev), 0, &timings); + if (ret) { + dev_err(dev, "failed to get any display timings\n"); + return -EINVAL; + } + + mode.xres = timings.hactive.typ; + mode.yres = timings.vactive.typ; + mode.left_margin = timings.hback_porch.typ; + mode.right_margin = timings.hfront_porch.typ; + mode.upper_margin = timings.vback_porch.typ; + mode.lower_margin = timings.vfront_porch.typ; + mode.hsync_len = timings.hsync_len.typ; + mode.vsync_len = timings.vsync_len.typ; + mode.pixclock = HZ2PS(timings.pixelclock.typ); + + bpp = BITS_PP; + + ret = mxs_probe_common(&mode, bpp, plat->base); + if (ret) + return ret; + + switch (bpp) { + case 24: + case 18: + uc_priv->bpix = VIDEO_BPP32; + break; + case 16: + uc_priv->bpix = VIDEO_BPP16; + break; + case 8: + uc_priv->bpix = VIDEO_BPP8; + break; + default: + dev_err(dev, "invalid bpp specified (bpp = %i)\n", bpp); + return -EINVAL; + } + + uc_priv->xsize = mode.xres; + uc_priv->ysize = mode.yres; + + /* Enable dcache for the frame buffer */ + fb_start = plat->base & ~(MMU_SECTION_SIZE - 1); + fb_end = plat->base + plat->size; + fb_end = ALIGN(fb_end, 1 << MMU_SECTION_SHIFT); + mmu_set_region_dcache_behaviour(fb_start, fb_end - fb_start, + DCACHE_WRITEBACK); + video_set_flush_dcache(dev, true); + + return ret; +} + +static int mxs_video_bind(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + struct display_timing timings; + int ret; + + ret = ofnode_decode_display_timing(dev_ofnode(dev), 0, &timings); + if (ret) { + dev_err(dev, "failed to get any display timings\n"); + return -EINVAL; + } + + plat->size = timings.hactive.typ * timings.vactive.typ * BYTES_PP; + + return 0; +} + +static int mxs_video_remove(struct udevice *dev) +{ + struct video_uc_platdata *plat = dev_get_uclass_platdata(dev); + + mxs_remove_common(plat->base); + + return 0; +} + +static const struct udevice_id mxs_video_ids[] = { + { .compatible = "fsl,imx23-lcdif" }, + { .compatible = "fsl,imx28-lcdif" }, + { .compatible = "fsl,imx7ulp-lcdif" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(mxs_video) = { + .name = "mxs_video", + .id = UCLASS_VIDEO, + .of_match = mxs_video_ids, + .bind = mxs_video_bind, + .probe = mxs_video_probe, + .remove = mxs_video_remove, + .flags = DM_FLAG_PRE_RELOC, +}; +#endif /* ifndef CONFIG_DM_VIDEO */ diff --git a/include/configs/colibri_imx7.h b/include/configs/colibri_imx7.h index 7dfc92c085..c3b353b2c2 100644 --- a/include/configs/colibri_imx7.h +++ b/include/configs/colibri_imx7.h @@ -225,7 +225,7 @@ #define CONFIG_SYS_DFU_DATA_BUF_SIZE SZ_16M #define DFU_DEFAULT_POLL_TIMEOUT 300 -#ifdef CONFIG_VIDEO +#if defined(CONFIG_VIDEO) || defined(CONFIG_DM_VIDEO) #define CONFIG_VIDEO_MXS #define CONFIG_VIDEO_LOGO #define CONFIG_SPLASH_SCREEN diff --git a/include/splash.h b/include/splash.h index 228aff441b..7fd2de8fea 100644 --- a/include/splash.h +++ b/include/splash.h @@ -66,10 +66,10 @@ void splash_get_pos(int *x, int *y); static inline void splash_get_pos(int *x, int *y) { } #endif -#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_LCD) -int lcd_splash(ulong addr); +#if defined(CONFIG_SPLASH_SCREEN) && defined(CONFIG_CMD_BMP) +int splash_display(void); #else -static inline int lcd_splash(ulong addr) +static inline int splash_display(void) { return -ENOSYS; }