From: Songjun Wu <songjun.wu@microchip.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH RESEND v2] at91: video: Support driver-model for the HLCD driver
Date: Wed, 11 Jan 2017 10:36:48 +0800 [thread overview]
Message-ID: <20170111023648.25874-1-songjun.wu@microchip.com> (raw)
Add driver-model support to this driver.
Signed-off-by: Songjun Wu <songjun.wu@microchip.com>
---
Changes in v2:
- Due to the peripheral clock driver improvement, remove
the unneccessary clock calling.
drivers/video/Kconfig | 7 +
drivers/video/Makefile | 2 +-
drivers/video/atmel_hlcdfb.c | 314 ++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 319 insertions(+), 4 deletions(-)
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 8361a717e7..c40b2cffb0 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -362,6 +362,13 @@ config DISPLAY
The devices provide a simple interface to start up the display,
read display information and enable it.
+config VIDEO_ATMEL_HLCD
+ bool "Enable ATMEL video support using HLCDC"
+ depends on DM_VIDEO
+ help
+ Only the LCD is supported in U-Boot. This option enables this support
+ which can be used on devices which have an LCD display connected.
+
config VIDEO_BROADWELL_IGD
bool "Enable Intel Broadwell integrated graphics device"
depends on X86
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 3f045fe578..3d54cf91d1 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -22,7 +22,6 @@ endif
obj-$(CONFIG_VIDEO_BROADWELL_IGD) += broadwell_igd.o
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
-obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o
obj-$(CONFIG_FSL_DIU_FB) += fsl_diu_fb.o videomodes.o
@@ -35,6 +34,7 @@ obj-$(CONFIG_S6E8AX0) += s6e8ax0.o
obj-$(CONFIG_S6E63D6) += s6e63d6.o
obj-$(CONFIG_LD9040) += ld9040.o
obj-$(CONFIG_SED156X) += sed156x.o
+obj-$(CONFIG_VIDEO_ATMEL_HLCD) += atmel_hlcdfb.o
obj-$(CONFIG_VIDEO_BCM2835) += bcm2835.o
obj-$(CONFIG_VIDEO_COREBOOT) += coreboot_fb.o
obj-$(CONFIG_VIDEO_CT69000) += ct69000.o videomodes.o
diff --git a/drivers/video/atmel_hlcdfb.c b/drivers/video/atmel_hlcdfb.c
index 960b474b76..0bcb92b2cf 100644
--- a/drivers/video/atmel_hlcdfb.c
+++ b/drivers/video/atmel_hlcdfb.c
@@ -10,13 +10,24 @@
#include <asm/io.h>
#include <asm/arch/gpio.h>
#include <asm/arch/clk.h>
+#include <clk.h>
+#include <dm.h>
+#include <fdtdec.h>
#include <lcd.h>
+#include <video.h>
#include <atmel_hlcdc.h>
#if defined(CONFIG_LCD_LOGO)
#include <bmp_logo.h>
#endif
+DECLARE_GLOBAL_DATA_PTR;
+
+#define lcdc_readl(reg) __raw_readl((reg))
+#define lcdc_writel(reg, val) __raw_writel((val), (reg))
+
+#ifndef CONFIG_DM_VIDEO
+
/* configurable parameters */
#define ATMEL_LCDC_CVAL_DEFAULT 0xc8
#define ATMEL_LCDC_DMA_BURST_LEN 8
@@ -26,9 +37,6 @@
#define ATMEL_LCDC_FIFO_SIZE 512
-#define lcdc_readl(reg) __raw_readl((reg))
-#define lcdc_writel(reg, val) __raw_writel((val), (reg))
-
/*
* the CLUT register map as following
* RCLUT(24 ~ 16), GCLUT(15 ~ 8), BCLUT(7 ~ 0)
@@ -218,3 +226,303 @@ void lcd_ctrl_init(void *lcdbase)
/* Enable flushing if we enabled dcache */
lcd_set_flush_dcache(1);
}
+
+#else
+
+enum {
+ LCD_MAX_WIDTH = 1024,
+ LCD_MAX_HEIGHT = 768,
+ LCD_MAX_LOG2_BPP = VIDEO_BPP16,
+};
+
+struct atmel_hlcdc_priv {
+ struct atmel_hlcd_regs *regs;
+ struct display_timing timing;
+ unsigned int vl_bpix;
+ unsigned int guard_time;
+ ulong clk_rate;
+};
+
+static int at91_hlcdc_enable_clk(struct udevice *dev)
+{
+ struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
+ struct clk clk;
+ ulong clk_rate;
+ int ret;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ if (ret)
+ return -EINVAL;
+
+ ret = clk_enable(&clk);
+ if (ret)
+ return ret;
+
+ clk_rate = clk_get_rate(&clk);
+ if (!clk_rate)
+ return -ENODEV;
+
+ priv->clk_rate = clk_rate;
+
+ clk_free(&clk);
+
+ return 0;
+}
+
+static void atmel_hlcdc_init(struct udevice *dev)
+{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+ struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
+ struct atmel_hlcd_regs *regs = priv->regs;
+ struct display_timing *timing = &priv->timing;
+ struct lcd_dma_desc *desc;
+ unsigned long value, vl_clk_pol;
+
+ /* Disable DISP signal */
+ lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_DISPDIS);
+ while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS))
+ udelay(1);
+ /* Disable synchronization */
+ lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_SYNCDIS);
+ while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS))
+ udelay(1);
+ /* Disable pixel clock */
+ lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_CLKDIS);
+ while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS))
+ udelay(1);
+ /* Disable PWM */
+ lcdc_writel(®s->lcdc_lcddis, LCDC_LCDDIS_PWMDIS);
+ while ((lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS))
+ udelay(1);
+
+ /* Set pixel clock */
+ value = priv->clk_rate / timing->pixelclock.typ;
+ if (priv->clk_rate % timing->pixelclock.typ)
+ value++;
+
+ vl_clk_pol = 0;
+ if (timing->flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE)
+ vl_clk_pol = LCDC_LCDCFG0_CLKPOL;
+
+ if (value < 1) {
+ /* Using system clock as pixel clock */
+ lcdc_writel(®s->lcdc_lcdcfg0,
+ LCDC_LCDCFG0_CLKDIV(0)
+ | LCDC_LCDCFG0_CGDISHCR
+ | LCDC_LCDCFG0_CGDISHEO
+ | LCDC_LCDCFG0_CGDISOVR1
+ | LCDC_LCDCFG0_CGDISBASE
+ | vl_clk_pol
+ | LCDC_LCDCFG0_CLKSEL);
+
+ } else {
+ lcdc_writel(®s->lcdc_lcdcfg0,
+ LCDC_LCDCFG0_CLKDIV(value - 2)
+ | LCDC_LCDCFG0_CGDISHCR
+ | LCDC_LCDCFG0_CGDISHEO
+ | LCDC_LCDCFG0_CGDISOVR1
+ | LCDC_LCDCFG0_CGDISBASE
+ | vl_clk_pol);
+ }
+
+ /* Initialize control register 5 */
+ value = 0;
+
+ if (!(timing->flags & DISPLAY_FLAGS_HSYNC_HIGH))
+ value |= LCDC_LCDCFG5_HSPOL;
+ if (!(timing->flags & DISPLAY_FLAGS_VSYNC_HIGH))
+ value |= LCDC_LCDCFG5_VSPOL;
+
+#ifndef LCD_OUTPUT_BPP
+ /* Output is 24bpp */
+ value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
+#else
+ switch (LCD_OUTPUT_BPP) {
+ case 12:
+ value |= LCDC_LCDCFG5_MODE_OUTPUT_12BPP;
+ break;
+ case 16:
+ value |= LCDC_LCDCFG5_MODE_OUTPUT_16BPP;
+ break;
+ case 18:
+ value |= LCDC_LCDCFG5_MODE_OUTPUT_18BPP;
+ break;
+ case 24:
+ value |= LCDC_LCDCFG5_MODE_OUTPUT_24BPP;
+ break;
+ default:
+ BUG();
+ break;
+ }
+#endif
+
+ value |= LCDC_LCDCFG5_GUARDTIME(priv->guard_time);
+ value |= (LCDC_LCDCFG5_DISPDLY | LCDC_LCDCFG5_VSPDLYS);
+ lcdc_writel(®s->lcdc_lcdcfg5, value);
+
+ /* Vertical & Horizontal Timing */
+ value = LCDC_LCDCFG1_VSPW(timing->vsync_len.typ - 1);
+ value |= LCDC_LCDCFG1_HSPW(timing->hsync_len.typ - 1);
+ lcdc_writel(®s->lcdc_lcdcfg1, value);
+
+ value = LCDC_LCDCFG2_VBPW(timing->vback_porch.typ);
+ value |= LCDC_LCDCFG2_VFPW(timing->vfront_porch.typ - 1);
+ lcdc_writel(®s->lcdc_lcdcfg2, value);
+
+ value = LCDC_LCDCFG3_HBPW(timing->hback_porch.typ - 1);
+ value |= LCDC_LCDCFG3_HFPW(timing->hfront_porch.typ - 1);
+ lcdc_writel(®s->lcdc_lcdcfg3, value);
+
+ /* Display size */
+ value = LCDC_LCDCFG4_RPF(timing->vactive.typ - 1);
+ value |= LCDC_LCDCFG4_PPL(timing->hactive.typ - 1);
+ lcdc_writel(®s->lcdc_lcdcfg4, value);
+
+ lcdc_writel(®s->lcdc_basecfg0,
+ LCDC_BASECFG0_BLEN_AHB_INCR4 | LCDC_BASECFG0_DLBO);
+
+ switch (VNBITS(priv->vl_bpix)) {
+ case 16:
+ lcdc_writel(®s->lcdc_basecfg1,
+ LCDC_BASECFG1_RGBMODE_16BPP_RGB_565);
+ break;
+ case 32:
+ lcdc_writel(®s->lcdc_basecfg1,
+ LCDC_BASECFG1_RGBMODE_24BPP_RGB_888);
+ break;
+ default:
+ BUG();
+ break;
+ }
+
+ lcdc_writel(®s->lcdc_basecfg2, LCDC_BASECFG2_XSTRIDE(0));
+ lcdc_writel(®s->lcdc_basecfg3, 0);
+ lcdc_writel(®s->lcdc_basecfg4, LCDC_BASECFG4_DMA);
+
+ /* Disable all interrupts */
+ lcdc_writel(®s->lcdc_lcdidr, ~0UL);
+ lcdc_writel(®s->lcdc_baseidr, ~0UL);
+
+ /* Setup the DMA descriptor, this descriptor will loop to itself */
+ desc = (struct lcd_dma_desc *)(uc_plat->base - 16);
+
+ desc->address = (u32)uc_plat->base;
+
+ /* Disable DMA transfer interrupt & descriptor loaded interrupt. */
+ desc->control = LCDC_BASECTRL_ADDIEN | LCDC_BASECTRL_DSCRIEN
+ | LCDC_BASECTRL_DMAIEN | LCDC_BASECTRL_DFETCH;
+ desc->next = (u32)desc;
+
+ /* Flush the DMA descriptor if we enabled dcache */
+ flush_dcache_range((u32)desc, (u32)desc + sizeof(*desc));
+
+ lcdc_writel(®s->lcdc_baseaddr, desc->address);
+ lcdc_writel(®s->lcdc_basectrl, desc->control);
+ lcdc_writel(®s->lcdc_basenext, desc->next);
+ lcdc_writel(®s->lcdc_basecher,
+ LCDC_BASECHER_CHEN | LCDC_BASECHER_UPDATEEN);
+
+ /* Enable LCD */
+ value = lcdc_readl(®s->lcdc_lcden);
+ lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_CLKEN);
+ while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_CLKSTS))
+ udelay(1);
+ value = lcdc_readl(®s->lcdc_lcden);
+ lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_SYNCEN);
+ while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_LCDSTS))
+ udelay(1);
+ value = lcdc_readl(®s->lcdc_lcden);
+ lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_DISPEN);
+ while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_DISPSTS))
+ udelay(1);
+ value = lcdc_readl(®s->lcdc_lcden);
+ lcdc_writel(®s->lcdc_lcden, value | LCDC_LCDEN_PWMEN);
+ while (!(lcdc_readl(®s->lcdc_lcdsr) & LCDC_LCDSR_PWMSTS))
+ udelay(1);
+}
+
+static int atmel_hlcdc_probe(struct udevice *dev)
+{
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = at91_hlcdc_enable_clk(dev);
+ if (ret)
+ return ret;
+
+ atmel_hlcdc_init(dev);
+
+ uc_priv->xsize = priv->timing.hactive.typ;
+ uc_priv->ysize = priv->timing.vactive.typ;
+ uc_priv->bpix = priv->vl_bpix;
+
+ /* Enable flushing if we enabled dcache */
+ video_set_flush_dcache(dev, true);
+
+ return 0;
+}
+
+static int atmel_hlcdc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct atmel_hlcdc_priv *priv = dev_get_priv(dev);
+ const void *blob = gd->fdt_blob;
+ int node = dev->of_offset;
+
+ priv->regs = (struct atmel_hlcd_regs *)dev_get_addr(dev);
+ if (!priv->regs) {
+ debug("%s: No display controller address\n", __func__);
+ return -EINVAL;
+ }
+
+ if (fdtdec_decode_display_timing(blob, dev->of_offset,
+ 0, &priv->timing)) {
+ debug("%s: Failed to decode display timing\n", __func__);
+ return -EINVAL;
+ }
+
+ if (priv->timing.hactive.typ > LCD_MAX_WIDTH)
+ priv->timing.hactive.typ = LCD_MAX_WIDTH;
+
+ if (priv->timing.vactive.typ > LCD_MAX_HEIGHT)
+ priv->timing.vactive.typ = LCD_MAX_HEIGHT;
+
+ priv->vl_bpix = fdtdec_get_int(blob, node, "atmel,vl-bpix", 0);
+ if (!priv->vl_bpix) {
+ debug("%s: Failed to get bits per pixel\n", __func__);
+ return -EINVAL;
+ }
+
+ priv->guard_time = fdtdec_get_int(blob, node, "atmel,guard-time", 1);
+
+ return 0;
+}
+
+static int atmel_hlcdc_bind(struct udevice *dev)
+{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+
+ uc_plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
+ (1 << LCD_MAX_LOG2_BPP) / 8;
+
+ debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
+
+ return 0;
+}
+
+static const struct udevice_id atmel_hlcdc_ids[] = {
+ { .compatible = "atmel,sama5d2-hlcdc" },
+ { }
+};
+
+U_BOOT_DRIVER(atmel_hlcdfb) = {
+ .name = "atmel_hlcdfb",
+ .id = UCLASS_VIDEO,
+ .of_match = atmel_hlcdc_ids,
+ .bind = atmel_hlcdc_bind,
+ .probe = atmel_hlcdc_probe,
+ .ofdata_to_platdata = atmel_hlcdc_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct atmel_hlcdc_priv),
+};
+
+#endif
--
2.11.0
next reply other threads:[~2017-01-11 2:36 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-01-11 2:36 Songjun Wu [this message]
2017-01-13 19:17 ` [U-Boot] [PATCH RESEND v2] at91: video: Support driver-model for the HLCD driver Anatolij Gustschin
2017-01-16 2:44 ` Wu, Songjun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20170111023648.25874-1-songjun.wu@microchip.com \
--to=songjun.wu@microchip.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.