diff options
Diffstat (limited to 'drivers/video/mxsfb.c')
-rw-r--r-- | drivers/video/mxsfb.c | 219 |
1 files changed, 179 insertions, 40 deletions
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 <marex@denx.de> */ #include <common.h> +#include <dm.h> +#include <linux/errno.h> #include <malloc.h> +#include <video.h> #include <video_fb.h> -#include <asm/arch/imx-regs.h> #include <asm/arch/clock.h> +#include <asm/arch/imx-regs.h> #include <asm/arch/sys_proto.h> -#include <linux/errno.h> -#include <asm/io.h> - #include <asm/mach-imx/dma.h> +#include <asm/io.h> #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); + ret = mxs_probe_common(&mode, bpp, (u32)fb); + if (ret) + goto dealloc_fb; -#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; + return (void *)&panel; - 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; +dealloc_fb: + free(fb); - /* Execute the DMA chain. */ - mxs_dma_circ_start(MXS_DMA_CHANNEL_AHB_APBH_LCDIF, &desc); -#endif + return NULL; +} +#else /* ifndef CONFIG_DM_VIDEO */ - return (void *)&panel; +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 */ |