summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig4
-rw-r--r--drivers/clk/clk-uclass.c10
-rw-r--r--drivers/clk/clk_rk3036.c2
-rw-r--r--drivers/clk/clk_rk3288.c2
-rw-r--r--drivers/clk/clk_sandbox.c5
-rw-r--r--drivers/core/device.c7
-rw-r--r--drivers/core/uclass.c9
-rw-r--r--drivers/gpio/gpio-uclass.c8
-rw-r--r--drivers/mmc/mmc.c20
-rw-r--r--drivers/pinctrl/pinctrl-uclass.c4
-rw-r--r--drivers/serial/Kconfig6
-rw-r--r--drivers/serial/sandbox.c5
-rw-r--r--drivers/serial/serial_lpuart.c286
-rw-r--r--drivers/timer/Kconfig6
-rw-r--r--drivers/timer/Makefile1
-rw-r--r--drivers/timer/omap-timer.c108
-rw-r--r--drivers/timer/sandbox_timer.c10
-rw-r--r--drivers/timer/timer-uclass.c56
-rw-r--r--drivers/video/Kconfig62
-rw-r--r--drivers/video/Makefile3
-rw-r--r--drivers/video/console_normal.c141
-rw-r--r--drivers/video/console_rotate.c436
-rw-r--r--drivers/video/sandbox_sdl.c90
-rw-r--r--drivers/video/vidconsole-uclass.c239
-rw-r--r--drivers/video/video-uclass.c249
-rw-r--r--drivers/video/video_bmp.c353
26 files changed, 2016 insertions, 106 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 890f22f48e..9fcde39b71 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -1,3 +1,5 @@
+menu "Clock"
+
config CLK
bool "Enable clock driver support"
depends on DM
@@ -17,3 +19,5 @@ config SPL_CLK
SPL, enable this option. It might provide a cleaner interface to
setting up clocks within SPL, and allows the same drivers to be
used as U-Boot proper.
+
+endmenu
diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 73dfd7d016..19f6f07c6f 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -32,6 +32,16 @@ ulong clk_set_rate(struct udevice *dev, ulong rate)
return ops->set_rate(dev, rate);
}
+int clk_enable(struct udevice *dev, int periph)
+{
+ struct clk_ops *ops = clk_get_ops(dev);
+
+ if (!ops->enable)
+ return -ENOSYS;
+
+ return ops->enable(dev, periph);
+}
+
ulong clk_get_periph_rate(struct udevice *dev, int periph)
{
struct clk_ops *ops = clk_get_ops(dev);
diff --git a/drivers/clk/clk_rk3036.c b/drivers/clk/clk_rk3036.c
index 6c802b6283..f650810250 100644
--- a/drivers/clk/clk_rk3036.c
+++ b/drivers/clk/clk_rk3036.c
@@ -314,7 +314,7 @@ static ulong rk3036_clk_set_rate(struct udevice *dev, ulong rate)
return 0;
}
-ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+static ulong rk3036_set_periph_rate(struct udevice *dev, int periph, ulong rate)
{
struct rk3036_clk_priv *priv = dev_get_priv(dev);
ulong new_rate;
diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c
index 54d49305b0..0172ad13bb 100644
--- a/drivers/clk/clk_rk3288.c
+++ b/drivers/clk/clk_rk3288.c
@@ -508,7 +508,7 @@ static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate,
return rockchip_spi_get_clk(cru, clk_general_rate, periph);
}
-ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+static ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate)
{
struct rk3288_clk_priv *priv = dev_get_priv(dev);
ulong new_rate;
diff --git a/drivers/clk/clk_sandbox.c b/drivers/clk/clk_sandbox.c
index 058225a766..367130f8b7 100644
--- a/drivers/clk/clk_sandbox.c
+++ b/drivers/clk/clk_sandbox.c
@@ -32,7 +32,7 @@ static ulong sandbox_clk_set_rate(struct udevice *dev, ulong rate)
return 0;
}
-ulong sandbox_get_periph_rate(struct udevice *dev, int periph)
+static ulong sandbox_get_periph_rate(struct udevice *dev, int periph)
{
struct sandbox_clk_priv *priv = dev_get_priv(dev);
@@ -41,7 +41,8 @@ ulong sandbox_get_periph_rate(struct udevice *dev, int periph)
return priv->periph_rate[periph];
}
-ulong sandbox_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+static ulong sandbox_set_periph_rate(struct udevice *dev, int periph,
+ ulong rate)
{
struct sandbox_clk_priv *priv = dev_get_priv(dev);
ulong old_rate;
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 818d03fac1..1e5584a7ce 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -135,6 +135,11 @@ int device_bind(struct udevice *parent, const struct driver *drv,
if (ret)
goto fail_child_post_bind;
}
+ if (uc->uc_drv->post_bind) {
+ ret = uc->uc_drv->post_bind(dev);
+ if (ret)
+ goto fail_uclass_post_bind;
+ }
if (parent)
dm_dbg("Bound device %s to %s\n", dev->name, parent->name);
@@ -145,6 +150,8 @@ int device_bind(struct udevice *parent, const struct driver *drv,
return 0;
+fail_uclass_post_bind:
+ /* There is no child unbind() method, so no clean-up required */
fail_child_post_bind:
if (CONFIG_IS_ENABLED(DM_DEVICE_REMOVE)) {
if (drv->unbind && drv->unbind(dev)) {
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index 1af09472a2..e1acefe727 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -278,6 +278,7 @@ static int uclass_find_device_by_of_offset(enum uclass_id id, int node,
return -ENODEV;
}
+#if CONFIG_IS_ENABLED(OF_CONTROL)
static int uclass_find_device_by_phandle(enum uclass_id id,
struct udevice *parent,
const char *name,
@@ -308,6 +309,7 @@ static int uclass_find_device_by_phandle(enum uclass_id id,
return -ENODEV;
}
+#endif
int uclass_get_device_tail(struct udevice *dev, int ret,
struct udevice **devp)
@@ -374,6 +376,7 @@ int uclass_get_device_by_of_offset(enum uclass_id id, int node,
return uclass_get_device_tail(dev, ret, devp);
}
+#if CONFIG_IS_ENABLED(OF_CONTROL)
int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
const char *name, struct udevice **devp)
{
@@ -384,6 +387,7 @@ int uclass_get_device_by_phandle(enum uclass_id id, struct udevice *parent,
ret = uclass_find_device_by_phandle(id, parent, name, &dev);
return uclass_get_device_tail(dev, ret, devp);
}
+#endif
int uclass_first_device(enum uclass_id id, struct udevice **devp)
{
@@ -426,11 +430,6 @@ int uclass_bind_device(struct udevice *dev)
goto err;
}
}
- if (uc->uc_drv->post_bind) {
- ret = uc->uc_drv->post_bind(dev);
- if (ret)
- goto err;
- }
return 0;
err:
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index 4cce11fe21..3ed4d8914c 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -154,6 +154,7 @@ int dm_gpio_request(struct gpio_desc *desc, const char *label)
static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...)
{
+#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF)
va_list args;
char buf[40];
@@ -161,6 +162,9 @@ static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...)
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return dm_gpio_request(desc, buf);
+#else
+ return dm_gpio_request(desc, fmt);
+#endif
}
/**
@@ -199,6 +203,7 @@ int gpio_request(unsigned gpio, const char *label)
*/
int gpio_requestf(unsigned gpio, const char *fmt, ...)
{
+#if !defined(CONFIG_SPL_BUILD) || !defined(CONFIG_USE_TINY_PRINTF)
va_list args;
char buf[40];
@@ -206,6 +211,9 @@ int gpio_requestf(unsigned gpio, const char *fmt, ...)
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return gpio_request(gpio, buf);
+#else
+ return gpio_request(gpio, fmt);
+#endif
}
int _dm_gpio_free(struct udevice *dev, uint offset)
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index e6028d503f..ede5d6eeec 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -1780,18 +1780,28 @@ static int mmc_probe(bd_t *bis)
#elif defined(CONFIG_DM_MMC)
static int mmc_probe(bd_t *bis)
{
- int ret;
+ int ret, i;
struct uclass *uc;
- struct udevice *m;
+ struct udevice *dev;
ret = uclass_get(UCLASS_MMC, &uc);
if (ret)
return ret;
- uclass_foreach_dev(m, uc) {
- ret = device_probe(m);
+ /*
+ * Try to add them in sequence order. Really with driver model we
+ * should allow holes, but the current MMC list does not allow that.
+ * So if we request 0, 1, 3 we will get 0, 1, 2.
+ */
+ for (i = 0; ; i++) {
+ ret = uclass_get_device_by_seq(UCLASS_MMC, i, &dev);
+ if (ret == -ENODEV)
+ break;
+ }
+ uclass_foreach_dev(dev, uc) {
+ ret = device_probe(dev);
if (ret)
- printf("%s - probe failed: %d\n", m->name, ret);
+ printf("%s - probe failed: %d\n", dev->name, ret);
}
return 0;
diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c
index b5fdcd12a8..c42b312ddd 100644
--- a/drivers/pinctrl/pinctrl-uclass.c
+++ b/drivers/pinctrl/pinctrl-uclass.c
@@ -111,12 +111,16 @@ static int pinconfig_post_bind(struct udevice *dev)
{
const void *fdt = gd->fdt_blob;
int offset = dev->of_offset;
+ bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
const char *name;
int ret;
for (offset = fdt_first_subnode(fdt, offset);
offset > 0;
offset = fdt_next_subnode(fdt, offset)) {
+ if (pre_reloc_only &&
+ !fdt_getprop(fdt, offset, "u-boot,dm-pre-reloc", NULL))
+ continue;
/*
* If this node has "compatible" property, this is not
* a pin configuration node, but a normal device. skip.
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 92084a2908..83068cfd50 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -228,6 +228,12 @@ config ALTERA_UART
Select this to enable an UART for Altera devices. Please find
details on the "Embedded Peripherals IP User Guide" of Altera.
+config FSL_LPUART
+ bool "Freescale LPUART support"
+ help
+ Select this to enable a Low Power UART for Freescale VF610 and
+ QorIQ Layerscape devices.
+
config SYS_NS16550
bool "NS16550 UART or compatible"
help
diff --git a/drivers/serial/sandbox.c b/drivers/serial/sandbox.c
index cd2f91e28e..45dff98d9d 100644
--- a/drivers/serial/sandbox.c
+++ b/drivers/serial/sandbox.c
@@ -16,6 +16,7 @@
#include <lcd.h>
#include <os.h>
#include <serial.h>
+#include <video.h>
#include <linux/compiler.h>
#include <asm/state.h>
@@ -114,9 +115,7 @@ static int sandbox_serial_pending(struct udevice *dev, bool input)
return 0;
os_usleep(100);
-#ifdef CONFIG_LCD
- lcd_sync();
-#endif
+ video_sync_all();
if (next_index == serial_buf_read)
return 1; /* buffer full */
diff --git a/drivers/serial/serial_lpuart.c b/drivers/serial/serial_lpuart.c
index 63fc388b26..3f9c4d14ea 100644
--- a/drivers/serial/serial_lpuart.c
+++ b/drivers/serial/serial_lpuart.c
@@ -5,6 +5,7 @@
*/
#include <common.h>
+#include <dm.h>
#include <watchdog.h>
#include <asm/io.h>
#include <serial.h>
@@ -12,15 +13,15 @@
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
-#define US1_TDRE (1 << 7)
-#define US1_RDRF (1 << 5)
-#define US1_OR (1 << 3)
-#define UC2_TE (1 << 3)
-#define UC2_RE (1 << 2)
-#define CFIFO_TXFLUSH (1 << 7)
-#define CFIFO_RXFLUSH (1 << 6)
-#define SFIFO_RXOF (1 << 2)
-#define SFIFO_RXUF (1 << 0)
+#define US1_TDRE (1 << 7)
+#define US1_RDRF (1 << 5)
+#define US1_OR (1 << 3)
+#define UC2_TE (1 << 3)
+#define UC2_RE (1 << 2)
+#define CFIFO_TXFLUSH (1 << 7)
+#define CFIFO_RXFLUSH (1 << 6)
+#define SFIFO_RXOF (1 << 2)
+#define SFIFO_RXUF (1 << 0)
#define STAT_LBKDIF (1 << 31)
#define STAT_RXEDGIF (1 << 30)
@@ -34,7 +35,7 @@
#define STAT_MA1F (1 << 15)
#define STAT_MA2F (1 << 14)
#define STAT_FLAGS (STAT_LBKDIF | STAT_RXEDGIF | STAT_IDLE | STAT_OR | \
- STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
+ STAT_NF | STAT_FE | STAT_PF | STAT_MA1F | STAT_MA2F)
#define CTRL_TE (1 << 19)
#define CTRL_RE (1 << 18)
@@ -49,23 +50,24 @@ DECLARE_GLOBAL_DATA_PTR;
struct lpuart_fsl *base = (struct lpuart_fsl *)LPUART_BASE;
+struct lpuart_serial_platdata {
+ struct lpuart_fsl *reg;
+};
+
#ifndef CONFIG_LPUART_32B_REG
-static void lpuart_serial_setbrg(void)
+static void _lpuart_serial_setbrg(struct lpuart_fsl *base, int baudrate)
{
u32 clk = mxc_get_clock(MXC_UART_CLK);
u16 sbr;
- if (!gd->baudrate)
- gd->baudrate = CONFIG_BAUDRATE;
+ sbr = (u16)(clk / (16 * baudrate));
- sbr = (u16)(clk / (16 * gd->baudrate));
/* place adjustment later - n/32 BRFA */
-
__raw_writeb(sbr >> 8, &base->ubdh);
__raw_writeb(sbr & 0xff, &base->ubdl);
}
-static int lpuart_serial_getc(void)
+static int _lpuart_serial_getc(struct lpuart_fsl *base)
{
while (!(__raw_readb(&base->us1) & (US1_RDRF | US1_OR)))
WATCHDOG_RESET();
@@ -75,10 +77,10 @@ static int lpuart_serial_getc(void)
return __raw_readb(&base->ud);
}
-static void lpuart_serial_putc(const char c)
+static void _lpuart_serial_putc(struct lpuart_fsl *base, const char c)
{
if (c == '\n')
- serial_putc('\r');
+ _lpuart_serial_putc(base, '\r');
while (!(__raw_readb(&base->us1) & US1_TDRE))
WATCHDOG_RESET();
@@ -86,10 +88,8 @@ static void lpuart_serial_putc(const char c)
__raw_writeb(c, &base->ud);
}
-/*
- * Test whether a character is in the RX buffer
- */
-static int lpuart_serial_tstc(void)
+/* Test whether a character is in the RX buffer */
+static int _lpuart_serial_tstc(struct lpuart_fsl *base)
{
if (__raw_readb(&base->urcfifo) == 0)
return 0;
@@ -101,7 +101,7 @@ static int lpuart_serial_tstc(void)
* Initialise the serial port with the given baudrate. The settings
* are always 8 data bits, no parity, 1 stop bit, no start bits.
*/
-static int lpuart_serial_init(void)
+static int _lpuart_serial_init(struct lpuart_fsl *base)
{
u8 ctrl;
@@ -120,14 +120,39 @@ static int lpuart_serial_init(void)
__raw_writeb(CFIFO_TXFLUSH | CFIFO_RXFLUSH, &base->ucfifo);
/* provide data bits, parity, stop bit, etc */
-
- serial_setbrg();
+ _lpuart_serial_setbrg(base, gd->baudrate);
__raw_writeb(UC2_RE | UC2_TE, &base->uc2);
return 0;
}
+#ifndef CONFIG_DM_SERIAL
+static void lpuart_serial_setbrg(void)
+{
+ _lpuart_serial_setbrg(base, gd->baudrate);
+}
+
+static int lpuart_serial_getc(void)
+{
+ return _lpuart_serial_getc(base);
+}
+
+static void lpuart_serial_putc(const char c)
+{
+ _lpuart_serial_putc(base, c);
+}
+
+static int lpuart_serial_tstc(void)
+{
+ return _lpuart_serial_tstc(base);
+}
+
+static int lpuart_serial_init(void)
+{
+ return _lpuart_serial_init(base);
+}
+
static struct serial_device lpuart_serial_drv = {
.name = "lpuart_serial",
.start = lpuart_serial_init,
@@ -138,22 +163,67 @@ static struct serial_device lpuart_serial_drv = {
.getc = lpuart_serial_getc,
.tstc = lpuart_serial_tstc,
};
+#else /* CONFIG_DM_SERIAL */
+static int lpuart_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ _lpuart_serial_setbrg(reg, baudrate);
+
+ return 0;
+}
+
+static int lpuart_serial_getc(struct udevice *dev)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ return _lpuart_serial_getc(reg);
+}
+
+static int lpuart_serial_putc(struct udevice *dev, const char c)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ _lpuart_serial_putc(reg, c);
+
+ return 0;
+}
+
+static int lpuart_serial_pending(struct udevice *dev, bool input)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ if (input)
+ return _lpuart_serial_tstc(reg);
+ else
+ return __raw_readb(&reg->us1) & US1_TDRE ? 0 : 1;
+}
+
+static int lpuart_serial_probe(struct udevice *dev)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ return _lpuart_serial_init(reg);
+}
+#endif /* CONFIG_DM_SERIAL */
#else
-static void lpuart32_serial_setbrg(void)
+static void _lpuart32_serial_setbrg(struct lpuart_fsl *base, int baudrate)
{
u32 clk = CONFIG_SYS_CLK_FREQ;
u32 sbr;
- if (!gd->baudrate)
- gd->baudrate = CONFIG_BAUDRATE;
+ sbr = (clk / (16 * baudrate));
- sbr = (clk / (16 * gd->baudrate));
/* place adjustment later - n/32 BRFA */
-
out_be32(&base->baud, sbr);
}
-static int lpuart32_serial_getc(void)
+static int _lpuart32_serial_getc(struct lpuart_fsl *base)
{
u32 stat;
@@ -165,10 +235,10 @@ static int lpuart32_serial_getc(void)
return in_be32(&base->data) & 0x3ff;
}
-static void lpuart32_serial_putc(const char c)
+static void _lpuart32_serial_putc(struct lpuart_fsl *base, const char c)
{
if (c == '\n')
- serial_putc('\r');
+ _lpuart32_serial_putc(base, '\r');
while (!(in_be32(&base->stat) & STAT_TDRE))
WATCHDOG_RESET();
@@ -176,10 +246,8 @@ static void lpuart32_serial_putc(const char c)
out_be32(&base->data, c);
}
-/*
- * Test whether a character is in the RX buffer
- */
-static int lpuart32_serial_tstc(void)
+/* Test whether a character is in the RX buffer */
+static int _lpuart32_serial_tstc(struct lpuart_fsl *base)
{
if ((in_be32(&base->water) >> 24) == 0)
return 0;
@@ -191,7 +259,7 @@ static int lpuart32_serial_tstc(void)
* Initialise the serial port with the given baudrate. The settings
* are always 8 data bits, no parity, 1 stop bit, no start bits.
*/
-static int lpuart32_serial_init(void)
+static int _lpuart32_serial_init(struct lpuart_fsl *base)
{
u8 ctrl;
@@ -204,15 +272,41 @@ static int lpuart32_serial_init(void)
out_be32(&base->fifo, ~(FIFO_TXFE | FIFO_RXFE));
out_be32(&base->match, 0);
- /* provide data bits, parity, stop bit, etc */
- serial_setbrg();
+ /* provide data bits, parity, stop bit, etc */
+ _lpuart32_serial_setbrg(base, gd->baudrate);
out_be32(&base->ctrl, CTRL_RE | CTRL_TE);
return 0;
}
+#ifndef CONFIG_DM_SERIAL
+static void lpuart32_serial_setbrg(void)
+{
+ _lpuart32_serial_setbrg(base, gd->baudrate);
+}
+
+static int lpuart32_serial_getc(void)
+{
+ return _lpuart32_serial_getc(base);
+}
+
+static void lpuart32_serial_putc(const char c)
+{
+ _lpuart32_serial_putc(base, c);
+}
+
+static int lpuart32_serial_tstc(void)
+{
+ return _lpuart32_serial_tstc(base);
+}
+
+static int lpuart32_serial_init(void)
+{
+ return _lpuart32_serial_init(base);
+}
+
static struct serial_device lpuart32_serial_drv = {
.name = "lpuart32_serial",
.start = lpuart32_serial_init,
@@ -223,8 +317,57 @@ static struct serial_device lpuart32_serial_drv = {
.getc = lpuart32_serial_getc,
.tstc = lpuart32_serial_tstc,
};
+#else /* CONFIG_DM_SERIAL */
+static int lpuart32_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ _lpuart32_serial_setbrg(reg, baudrate);
+
+ return 0;
+}
+
+static int lpuart32_serial_getc(struct udevice *dev)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ return _lpuart32_serial_getc(reg);
+}
+
+static int lpuart32_serial_putc(struct udevice *dev, const char c)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ _lpuart32_serial_putc(reg, c);
+
+ return 0;
+}
+
+static int lpuart32_serial_pending(struct udevice *dev, bool input)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ if (input)
+ return _lpuart32_serial_tstc(reg);
+ else
+ return in_be32(&reg->stat) & STAT_TDRE ? 0 : 1;
+}
+
+static int lpuart32_serial_probe(struct udevice *dev)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ struct lpuart_fsl *reg = plat->reg;
+
+ return _lpuart32_serial_init(reg);
+}
+#endif /* CONFIG_DM_SERIAL */
#endif
+#ifndef CONFIG_DM_SERIAL
void lpuart_serial_initialize(void)
{
#ifdef CONFIG_LPUART_32B_REG
@@ -242,3 +385,66 @@ __weak struct serial_device *default_serial_console(void)
return &lpuart_serial_drv;
#endif
}
+#else /* CONFIG_DM_SERIAL */
+static int lpuart_serial_ofdata_to_platdata(struct udevice *dev)
+{
+ struct lpuart_serial_platdata *plat = dev->platdata;
+ fdt_addr_t addr;
+
+ addr = dev_get_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ plat->reg = (struct lpuart_fsl *)addr;
+
+ return 0;
+}
+
+#ifndef CONFIG_LPUART_32B_REG
+static const struct dm_serial_ops lpuart_serial_ops = {
+ .putc = lpuart_serial_putc,
+ .pending = lpuart_serial_pending,
+ .getc = lpuart_serial_getc,
+ .setbrg = lpuart_serial_setbrg,
+};
+
+static const struct udevice_id lpuart_serial_ids[] = {
+ { .compatible = "fsl,vf610-lpuart" },
+ { }
+};
+
+U_BOOT_DRIVER(serial_lpuart) = {
+ .name = "serial_lpuart",
+ .id = UCLASS_SERIAL,
+ .of_match = lpuart_serial_ids,
+ .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
+ .probe = lpuart_serial_probe,
+ .ops = &lpuart_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+#else /* CONFIG_LPUART_32B_REG */
+static const struct dm_serial_ops lpuart32_serial_ops = {
+ .putc = lpuart32_serial_putc,
+ .pending = lpuart32_serial_pending,
+ .getc = lpuart32_serial_getc,
+ .setbrg = lpuart32_serial_setbrg,
+};
+
+static const struct udevice_id lpuart32_serial_ids[] = {
+ { .compatible = "fsl,ls1021a-lpuart" },
+ { }
+};
+
+U_BOOT_DRIVER(serial_lpuart32) = {
+ .name = "serial_lpuart32",
+ .id = UCLASS_SERIAL,
+ .of_match = lpuart32_serial_ids,
+ .ofdata_to_platdata = lpuart_serial_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct lpuart_serial_platdata),
+ .probe = lpuart32_serial_probe,
+ .ops = &lpuart32_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+#endif /* CONFIG_LPUART_32B_REG */
+#endif /* CONFIG_DM_SERIAL */
diff --git a/drivers/timer/Kconfig b/drivers/timer/Kconfig
index 2b10d2bc6c..ff65a731de 100644
--- a/drivers/timer/Kconfig
+++ b/drivers/timer/Kconfig
@@ -30,4 +30,10 @@ config X86_TSC_TIMER
help
Select this to enable Time-Stamp Counter (TSC) timer for x86.
+config OMAP_TIMER
+ bool "Omap timer support"
+ depends on TIMER
+ help
+ Select this to enable an timer for Omap devices.
+
endmenu
diff --git a/drivers/timer/Makefile b/drivers/timer/Makefile
index fe954eca9a..f351fbb4e0 100644
--- a/drivers/timer/Makefile
+++ b/drivers/timer/Makefile
@@ -8,3 +8,4 @@ obj-$(CONFIG_TIMER) += timer-uclass.o
obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o
obj-$(CONFIG_SANDBOX_TIMER) += sandbox_timer.o
obj-$(CONFIG_X86_TSC_TIMER) += tsc_timer.o
+obj-$(CONFIG_OMAP_TIMER) += omap-timer.o
diff --git a/drivers/timer/omap-timer.c b/drivers/timer/omap-timer.c
new file mode 100644
index 0000000000..3bb38c522c
--- /dev/null
+++ b/drivers/timer/omap-timer.c
@@ -0,0 +1,108 @@
+/*
+ * TI OMAP timer driver
+ *
+ * Copyright (C) 2015, Texas Instruments, Incorporated
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Timer register bits */
+#define TCLR_START BIT(0) /* Start=1 */
+#define TCLR_AUTO_RELOAD BIT(1) /* Auto reload */
+#define TCLR_PRE_EN BIT(5) /* Pre-scaler enable */
+#define TCLR_PTV_SHIFT (2) /* Pre-scaler shift value */
+
+#define TIMER_CLOCK (V_SCLK / (2 << CONFIG_SYS_PTV))
+
+struct omap_gptimer_regs {
+ unsigned int tidr; /* offset 0x00 */
+ unsigned char res1[12];
+ unsigned int tiocp_cfg; /* offset 0x10 */
+ unsigned char res2[12];
+ unsigned int tier; /* offset 0x20 */
+ unsigned int tistatr; /* offset 0x24 */
+ unsigned int tistat; /* offset 0x28 */
+ unsigned int tisr; /* offset 0x2c */
+ unsigned int tcicr; /* offset 0x30 */
+ unsigned int twer; /* offset 0x34 */
+ unsigned int tclr; /* offset 0x38 */
+ unsigned int tcrr; /* offset 0x3c */
+ unsigned int tldr; /* offset 0x40 */
+ unsigned int ttgr; /* offset 0x44 */
+ unsigned int twpc; /* offset 0x48 */
+ unsigned int tmar; /* offset 0x4c */
+ unsigned int tcar1; /* offset 0x50 */
+ unsigned int tscir; /* offset 0x54 */
+ unsigned int tcar2; /* offset 0x58 */
+};
+
+/* Omap Timer Priv */
+struct omap_timer_priv {
+ struct omap_gptimer_regs *regs;
+};
+
+static int omap_timer_get_count(struct udevice *dev, u64 *count)
+{
+ struct omap_timer_priv *priv = dev_get_priv(dev);
+
+ *count = readl(&priv->regs->tcrr);
+
+ return 0;
+}
+
+static int omap_timer_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct omap_timer_priv *priv = dev_get_priv(dev);
+
+ uc_priv->clock_rate = TIMER_CLOCK;
+
+ /* start the counter ticking up, reload value on overflow */
+ writel(0, &priv->regs->tldr);
+ /* enable timer */
+ writel((CONFIG_SYS_PTV << 2) | TCLR_PRE_EN | TCLR_AUTO_RELOAD |
+ TCLR_START, &priv->regs->tclr);
+
+ return 0;
+}
+
+static int omap_timer_ofdata_to_platdata(struct udevice *dev)
+{
+ struct omap_timer_priv *priv = dev_get_priv(dev);
+
+ priv->regs = (struct omap_gptimer_regs *)dev_get_addr(dev);
+
+ return 0;
+}
+
+
+static const struct timer_ops omap_timer_ops = {
+ .get_count = omap_timer_get_count,
+};
+
+static const struct udevice_id omap_timer_ids[] = {
+ { .compatible = "ti,am335x-timer" },
+ { .compatible = "ti,am4372-timer" },
+ { .compatible = "ti,omap5430-timer" },
+ {}
+};
+
+U_BOOT_DRIVER(omap_timer) = {
+ .name = "omap_timer",
+ .id = UCLASS_TIMER,
+ .of_match = omap_timer_ids,
+ .ofdata_to_platdata = omap_timer_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct omap_timer_priv),
+ .probe = omap_timer_probe,
+ .ops = &omap_timer_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/timer/sandbox_timer.c b/drivers/timer/sandbox_timer.c
index 00a9944f78..a8da936349 100644
--- a/drivers/timer/sandbox_timer.c
+++ b/drivers/timer/sandbox_timer.c
@@ -27,6 +27,11 @@ static int sandbox_timer_get_count(struct udevice *dev, u64 *count)
static int sandbox_timer_probe(struct udevice *dev)
{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (!uc_priv->clock_rate)
+ uc_priv->clock_rate = 1000000;
+
return 0;
}
@@ -47,3 +52,8 @@ U_BOOT_DRIVER(sandbox_timer) = {
.ops = &sandbox_timer_ops,
.flags = DM_FLAG_PRE_RELOC,
};
+
+/* This is here in case we don't have a device tree */
+U_BOOT_DEVICE(sandbox_timer_non_fdt) = {
+ .name = "sandbox_timer",
+};
diff --git a/drivers/timer/timer-uclass.c b/drivers/timer/timer-uclass.c
index aca421bdea..83d1a35e06 100644
--- a/drivers/timer/timer-uclass.c
+++ b/drivers/timer/timer-uclass.c
@@ -6,6 +6,8 @@
#include <common.h>
#include <dm.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
#include <errno.h>
#include <timer.h>
@@ -47,6 +49,16 @@ static int timer_pre_probe(struct udevice *dev)
return 0;
}
+static int timer_post_probe(struct udevice *dev)
+{
+ struct timer_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+ if (!uc_priv->clock_rate)
+ return -EINVAL;
+
+ return 0;
+}
+
u64 timer_conv_64(u32 count)
{
/* increment tbh if tbl has rolled over */
@@ -56,9 +68,53 @@ u64 timer_conv_64(u32 count)
return ((u64)gd->timebase_h << 32) | gd->timebase_l;
}
+int notrace dm_timer_init(void)
+{
+ const void *blob = gd->fdt_blob;
+ struct udevice *dev = NULL;
+ int node;
+ int ret;
+
+ if (gd->timer)
+ return 0;
+
+ /* Check for a chosen timer to be used for tick */
+ node = fdtdec_get_chosen_node(blob, "tick-timer");
+ if (node < 0) {
+ /* No chosen timer, trying first available timer */
+ ret = uclass_first_device(UCLASS_TIMER, &dev);
+ if (ret)
+ return ret;
+ if (!dev)
+ return -ENODEV;
+ } else {
+ if (uclass_get_device_by_of_offset(UCLASS_TIMER, node, &dev)) {
+ /*
+ * If the timer is not marked to be bound before
+ * relocation, bind it anyway.
+ */
+ if (node > 0 &&
+ !lists_bind_fdt(gd->dm_root, blob, node, &dev)) {
+ ret = device_probe(dev);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+
+ if (dev) {
+ gd->timer = dev;
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
UCLASS_DRIVER(timer) = {
.id = UCLASS_TIMER,
.name = "timer",
.pre_probe = timer_pre_probe,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .post_probe = timer_post_probe,
.per_device_auto_alloc_size = sizeof(struct timer_dev_priv),
};
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index caf1efcbb3..ae122daa04 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -4,6 +4,59 @@
menu "Graphics support"
+config DM_VIDEO
+ bool "Enable driver model support for LCD/video"
+ depends on DM
+ help
+ This enables driver model for LCD and video devices. These support
+ a bitmap display of various sizes and depths which can be drawn on
+ to display a command-line console or splash screen. Enabling this
+ option compiles in the video uclass and routes all LCD/video access
+ through this.
+
+config VIDEO_BPP8
+ bool "Support 8-bit-per-pixel displays"
+ depends on DM_VIDEO
+ default y if DM_VIDEO
+ help
+ Support drawing text and bitmaps onto a 8-bit-per-pixel display.
+ Enabling this will include code to support this display. Without
+ this option, such displays will not be supported and console output
+ will be empty.
+
+config VIDEO_BPP16
+ bool "Support 16-bit-per-pixel displays"
+ depends on DM_VIDEO
+ default y if DM_VIDEO
+ help
+ Support drawing text and bitmaps onto a 16-bit-per-pixel display.
+ Enabling this will include code to support this display. Without
+ this option, such displays will not be supported and console output
+ will be empty.
+
+config VIDEO_BPP32
+ bool "Support 32-bit-per-pixel displays"
+ depends on DM_VIDEO
+ default y if DM_VIDEO
+ help
+ Support drawing text and bitmaps onto a 32-bit-per-pixel display.
+ Enabling this will include code to support this display. Without
+ this option, such displays will not be supported and console output
+ will be empty.
+
+config VIDEO_ROTATION
+ bool "Support rotated displays"
+ depends on DM_VIDEO
+ help
+ Sometimes, for example if the display is mounted in portrait
+ mode or even if it's mounted landscape but rotated by 180degree,
+ we need to rotate our content of the display relative to the
+ framebuffer, so that user can read the messages which are
+ printed out. Enable this option to include a text driver which can
+ support this. The rotation is set by the 'rot' parameter in
+ struct video_priv: 0=unrotated, 1=90 degrees clockwise, 2=180
+ degrees, 3=270 degrees.
+
config VIDEO_VESA
bool "Enable VESA video driver support"
default n
@@ -247,6 +300,15 @@ config DISPLAY_PORT
to drive LCD panels. This framework provides support for enabling
these displays where supported by the video hardware.
+config VIDEO_SANDBOX_SDL
+ bool "Enable sandbox video console using SDL"
+ depends on SANDBOX
+ help
+ When using sandbox you can enable an emulated LCD display which
+ appears as an SDL (Simple DirectMedia Layer) window. This is a
+ console device and can display stdout output. Within U-Boot is is
+ a normal bitmap display and can display images as well as text.
+
config VIDEO_TEGRA124
bool "Enable video support on Tegra124"
help
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index e85fd8a677..ee046296e6 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -7,6 +7,9 @@
ifdef CONFIG_DM
obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o
+obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o console_normal.o
+obj-$(CONFIG_DM_VIDEO) += video_bmp.o
+obj-$(CONFIG_VIDEO_ROTATION) += console_rotate.o
endif
obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
diff --git a/drivers/video/console_normal.c b/drivers/video/console_normal.c
new file mode 100644
index 0000000000..d1031c8ed1
--- /dev/null
+++ b/drivers/video/console_normal.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * (C) Copyright 2001-2015
+ * DENX Software Engineering -- wd@denx.de
+ * Compulab Ltd - http://compulab.co.il/
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <video.h>
+#include <video_console.h>
+#include <video_font.h> /* Get font data, width and height */
+
+static int console_normal_set_row(struct udevice *dev, uint row, int clr)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *line;
+ int pixels = VIDEO_FONT_HEIGHT * vid_priv->line_length;
+ int i;
+
+ line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static int console_normal_move_rows(struct udevice *dev, uint rowdst,
+ uint rowsrc, uint count)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *dst;
+ void *src;
+
+ dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+ src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+ memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count);
+
+ return 0;
+}
+
+static int console_normal_putc_xy(struct udevice *dev, uint x, uint y, char ch)
+{
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ int i, row;
+ void *line = vid_priv->fb + y * vid_priv->line_length +
+ x * VNBYTES(vid_priv->bpix);
+
+ for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
+ uchar bits = video_fontdata[ch * VIDEO_FONT_HEIGHT + row];
+
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+ *dst++ = (bits & 0x80) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ bits <<= 1;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+ *dst++ = (bits & 0x80) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ bits <<= 1;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+ *dst++ = (bits & 0x80) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ bits <<= 1;
+ }
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line += vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+struct vidconsole_ops console_normal_ops = {
+ .putc_xy = console_normal_putc_xy,
+ .move_rows = console_normal_move_rows,
+ .set_row = console_normal_set_row,
+};
+
+U_BOOT_DRIVER(vidconsole_normal) = {
+ .name = "vidconsole0",
+ .id = UCLASS_VIDEO_CONSOLE,
+ .ops = &console_normal_ops,
+};
diff --git a/drivers/video/console_rotate.c b/drivers/video/console_rotate.c
new file mode 100644
index 0000000000..ebb31d8cd0
--- /dev/null
+++ b/drivers/video/console_rotate.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * (C) Copyright 2015
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <video.h>
+#include <video_console.h>
+#include <video_font.h> /* Get font data, width and height */
+
+static int console_set_row_1(struct udevice *dev, uint row, int clr)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ int pbytes = VNBYTES(vid_priv->bpix);
+ void *line;
+ int i, j;
+
+ line = vid_priv->fb + vid_priv->line_length -
+ (row + 1) * VIDEO_FONT_HEIGHT * pbytes;
+ for (j = 0; j < vid_priv->ysize; j++) {
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line += vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+static int console_move_rows_1(struct udevice *dev, uint rowdst, uint rowsrc,
+ uint count)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *dst;
+ void *src;
+ int pbytes = VNBYTES(vid_priv->bpix);
+ int j;
+
+ dst = vid_priv->fb + vid_priv->line_length -
+ (rowdst + count) * VIDEO_FONT_HEIGHT * pbytes;
+ src = vid_priv->fb + vid_priv->line_length -
+ (rowsrc + count) * VIDEO_FONT_HEIGHT * pbytes;
+
+ for (j = 0; j < vid_priv->ysize; j++) {
+ memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count);
+ src += vid_priv->line_length;
+ dst += vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+static int console_putc_xy_1(struct udevice *dev, uint x, uint y, char ch)
+{
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ int pbytes = VNBYTES(vid_priv->bpix);
+ int i, col;
+ int mask = 0x80;
+ void *line = vid_priv->fb + (x + 1) * vid_priv->line_length -
+ (y + 1) * pbytes;
+ uchar *pfont = video_fontdata + ch * VIDEO_FONT_HEIGHT;
+
+ for (col = 0; col < VIDEO_FONT_HEIGHT; col++) {
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+ *dst-- = (pfont[i] & mask) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+ *dst-- = (pfont[i] & mask) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+ *dst-- = (pfont[i] & mask) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ }
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line += vid_priv->line_length;
+ mask >>= 1;
+ }
+
+ return 0;
+}
+
+
+static int console_set_row_2(struct udevice *dev, uint row, int clr)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *line;
+ int pixels = VIDEO_FONT_HEIGHT * vid_priv->xsize;
+ int i;
+
+ line = vid_priv->fb + vid_priv->ysize * vid_priv->line_length -
+ (row + 1) * VIDEO_FONT_HEIGHT * vid_priv->line_length;
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < pixels; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+
+ return 0;
+}
+
+static int console_move_rows_2(struct udevice *dev, uint rowdst, uint rowsrc,
+ uint count)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *dst;
+ void *src;
+ void *end;
+
+ end = vid_priv->fb + vid_priv->ysize * vid_priv->line_length;
+ dst = end - (rowdst + count) * VIDEO_FONT_HEIGHT *
+ vid_priv->line_length;
+ src = end - (rowsrc + count) * VIDEO_FONT_HEIGHT *
+ vid_priv->line_length;
+ memmove(dst, src, VIDEO_FONT_HEIGHT * vid_priv->line_length * count);
+
+ return 0;
+}
+
+static int console_putc_xy_2(struct udevice *dev, uint x, uint y, char ch)
+{
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ int i, row;
+ void *line;
+
+ line = vid_priv->fb + (vid_priv->ysize - y - 1) *
+ vid_priv->line_length +
+ (vid_priv->xsize - x - VIDEO_FONT_WIDTH - 1) *
+ VNBYTES(vid_priv->bpix);
+
+ for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
+ uchar bits = video_fontdata[ch * VIDEO_FONT_HEIGHT + row];
+
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+ *dst-- = (bits & 0x80) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ bits <<= 1;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+ *dst-- = (bits & 0x80) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ bits <<= 1;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_WIDTH; i++) {
+ *dst-- = (bits & 0x80) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ bits <<= 1;
+ }
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line -= vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+static int console_set_row_3(struct udevice *dev, uint row, int clr)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ int pbytes = VNBYTES(vid_priv->bpix);
+ void *line;
+ int i, j;
+
+ line = vid_priv->fb + row * VIDEO_FONT_HEIGHT * pbytes;
+ for (j = 0; j < vid_priv->ysize; j++) {
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+ *dst++ = clr;
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line += vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+static int console_move_rows_3(struct udevice *dev, uint rowdst, uint rowsrc,
+ uint count)
+{
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+ void *dst;
+ void *src;
+ int pbytes = VNBYTES(vid_priv->bpix);
+ int j;
+
+ dst = vid_priv->fb + rowdst * VIDEO_FONT_HEIGHT * pbytes;
+ src = vid_priv->fb + rowsrc * VIDEO_FONT_HEIGHT * pbytes;
+
+ for (j = 0; j < vid_priv->ysize; j++) {
+ memmove(dst, src, VIDEO_FONT_HEIGHT * pbytes * count);
+ src += vid_priv->line_length;
+ dst += vid_priv->line_length;
+ }
+
+ return 0;
+}
+
+static int console_putc_xy_3(struct udevice *dev, uint x, uint y, char ch)
+{
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+ int pbytes = VNBYTES(vid_priv->bpix);
+ int i, col;
+ int mask = 0x80;
+ void *line = vid_priv->fb + (vid_priv->ysize - x - 1) *
+ vid_priv->line_length + y * pbytes;
+ uchar *pfont = video_fontdata + ch * VIDEO_FONT_HEIGHT;
+
+ for (col = 0; col < VIDEO_FONT_HEIGHT; col++) {
+ switch (vid_priv->bpix) {
+#ifdef CONFIG_VIDEO_BPP8
+ case VIDEO_BPP8: {
+ uint8_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+ *dst++ = (pfont[i] & mask) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP16
+ case VIDEO_BPP16: {
+ uint16_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+ *dst++ = (pfont[i] & mask) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ }
+ break;
+ }
+#endif
+#ifdef CONFIG_VIDEO_BPP32
+ case VIDEO_BPP32: {
+ uint32_t *dst = line;
+
+ for (i = 0; i < VIDEO_FONT_HEIGHT; i++) {
+ *dst++ = (pfont[i] & mask) ? vid_priv->colour_fg
+ : vid_priv->colour_bg;
+ }
+ break;
+ }
+#endif
+ default:
+ return -ENOSYS;
+ }
+ line -= vid_priv->line_length;
+ mask >>= 1;
+ }
+
+ return 0;
+}
+
+
+static int console_probe_1_3(struct udevice *dev)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ struct video_priv *vid_priv = dev_get_uclass_priv(dev->parent);
+
+ priv->cols = vid_priv->ysize / VIDEO_FONT_WIDTH;
+ priv->rows = vid_priv->xsize / VIDEO_FONT_HEIGHT;
+
+ return 0;
+}
+
+struct vidconsole_ops console_ops_1 = {
+ .putc_xy = console_putc_xy_1,
+ .move_rows = console_move_rows_1,
+ .set_row = console_set_row_1,
+};
+
+struct vidconsole_ops console_ops_2 = {
+ .putc_xy = console_putc_xy_2,
+ .move_rows = console_move_rows_2,
+ .set_row = console_set_row_2,
+};
+
+struct vidconsole_ops console_ops_3 = {
+ .putc_xy = console_putc_xy_3,
+ .move_rows = console_move_rows_3,
+ .set_row = console_set_row_3,
+};
+
+U_BOOT_DRIVER(vidconsole_1) = {
+ .name = "vidconsole1",
+ .id = UCLASS_VIDEO_CONSOLE,
+ .ops = &console_ops_1,
+ .probe = console_probe_1_3,
+};
+
+U_BOOT_DRIVER(vidconsole_2) = {
+ .name = "vidconsole2",
+ .id = UCLASS_VIDEO_CONSOLE,
+ .ops = &console_ops_2,
+};
+
+U_BOOT_DRIVER(vidconsole_3) = {
+ .name = "vidconsole3",
+ .id = UCLASS_VIDEO_CONSOLE,
+ .ops = &console_ops_3,
+ .probe = console_probe_1_3,
+};
diff --git a/drivers/video/sandbox_sdl.c b/drivers/video/sandbox_sdl.c
index ba4578e9d1..21448a1411 100644
--- a/drivers/video/sandbox_sdl.c
+++ b/drivers/video/sandbox_sdl.c
@@ -5,75 +5,67 @@
*/
#include <common.h>
+#include <dm.h>
#include <fdtdec.h>
-#include <lcd.h>
-#include <malloc.h>
+#include <video.h>
#include <asm/sdl.h>
#include <asm/u-boot-sandbox.h>
+#include <dm/test.h>
DECLARE_GLOBAL_DATA_PTR;
enum {
- /* Maximum LCD size we support */
+ /* Default LCD size we support */
LCD_MAX_WIDTH = 1366,
LCD_MAX_HEIGHT = 768,
- LCD_MAX_LOG2_BPP = 4, /* 2^4 = 16 bpp */
};
-vidinfo_t panel_info;
-
-void lcd_setcolreg(ushort regno, ushort red, ushort green, ushort blue)
+static int sandbox_sdl_probe(struct udevice *dev)
{
-}
+ struct sandbox_sdl_plat *plat = dev_get_platdata(dev);
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ int ret;
-void lcd_ctrl_init(void *lcdbase)
-{
- /*
- * Allocate memory to keep BMP color conversion map. This is required
- * for 8 bit BMPs only (hence 256 colors). If malloc fails - keep
- * going, it is not even clear if displyaing the bitmap will be
- * required on the way up.
- */
- panel_info.cmap = malloc(256 * NBITS(panel_info.vl_bpix) / 8);
-}
-
-void lcd_enable(void)
-{
- if (sandbox_sdl_init_display(panel_info.vl_col, panel_info.vl_row,
- panel_info.vl_bpix))
+ ret = sandbox_sdl_init_display(plat->xres, plat->yres, plat->bpix);
+ if (ret) {
puts("LCD init failed\n");
+ return ret;
+ }
+ uc_priv->xsize = plat->xres;
+ uc_priv->ysize = plat->yres;
+ uc_priv->bpix = plat->bpix;
+ uc_priv->rot = plat->rot;
+
+ return 0;
}
-int sandbox_lcd_sdl_early_init(void)
+static int sandbox_sdl_bind(struct udevice *dev)
{
+ struct video_uc_platdata *uc_plat = dev_get_uclass_platdata(dev);
+ struct sandbox_sdl_plat *plat = dev_get_platdata(dev);
const void *blob = gd->fdt_blob;
- int xres = LCD_MAX_WIDTH, yres = LCD_MAX_HEIGHT;
- int node;
+ int node = dev->of_offset;
int ret = 0;
- /*
- * The code in common/lcd.c does not cope with not being able to
- * set up a frame buffer. It will just happily keep writing to
- * invalid memory. So here we make sure that at least some buffer
- * is available even if it actually won't be displayed.
- */
- node = fdtdec_next_compatible(blob, 0, COMPAT_SANDBOX_LCD_SDL);
- if (node >= 0) {
- xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH);
- yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT);
- if (xres < 0 || xres > LCD_MAX_WIDTH) {
- xres = LCD_MAX_WIDTH;
- ret = -EINVAL;
- }
- if (yres < 0 || yres > LCD_MAX_HEIGHT) {
- yres = LCD_MAX_HEIGHT;
- ret = -EINVAL;
- }
- }
-
- panel_info.vl_col = xres;
- panel_info.vl_row = yres;
- panel_info.vl_bpix = LCD_COLOR16;
+ plat->xres = fdtdec_get_int(blob, node, "xres", LCD_MAX_WIDTH);
+ plat->yres = fdtdec_get_int(blob, node, "yres", LCD_MAX_HEIGHT);
+ plat->bpix = VIDEO_BPP16;
+ uc_plat->size = plat->xres * plat->yres * (1 << plat->bpix) / 8;
+ debug("%s: Frame buffer size %x\n", __func__, uc_plat->size);
return ret;
}
+
+static const struct udevice_id sandbox_sdl_ids[] = {
+ { .compatible = "sandbox,lcd-sdl" },
+ { }
+};
+
+U_BOOT_DRIVER(sdl_sandbox) = {
+ .name = "sdl_sandbox",
+ .id = UCLASS_VIDEO,
+ .of_match = sandbox_sdl_ids,
+ .bind = sandbox_sdl_bind,
+ .probe = sandbox_sdl_probe,
+ .platdata_auto_alloc_size = sizeof(struct sandbox_sdl_plat),
+};
diff --git a/drivers/video/vidconsole-uclass.c b/drivers/video/vidconsole-uclass.c
new file mode 100644
index 0000000000..ea10189432
--- /dev/null
+++ b/drivers/video/vidconsole-uclass.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ * (C) Copyright 2001-2015
+ * DENX Software Engineering -- wd@denx.de
+ * Compulab Ltd - http://compulab.co.il/
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <video.h>
+#include <video_console.h>
+#include <video_font.h> /* Get font data, width and height */
+
+/* By default we scroll by a single line */
+#ifndef CONFIG_CONSOLE_SCROLL_LINES
+#define CONFIG_CONSOLE_SCROLL_LINES 1
+#endif
+
+int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
+{
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+
+ if (!ops->putc_xy)
+ return -ENOSYS;
+ return ops->putc_xy(dev, x, y, ch);
+}
+
+int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
+ uint count)
+{
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+
+ if (!ops->move_rows)
+ return -ENOSYS;
+ return ops->move_rows(dev, rowdst, rowsrc, count);
+}
+
+int vidconsole_set_row(struct udevice *dev, uint row, int clr)
+{
+ struct vidconsole_ops *ops = vidconsole_get_ops(dev);
+
+ if (!ops->set_row)
+ return -ENOSYS;
+ return ops->set_row(dev, row, clr);
+}
+
+/* Move backwards one space */
+static void vidconsole_back(struct udevice *dev)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+
+ if (--priv->curr_col < 0) {
+ priv->curr_col = priv->cols - 1;
+ if (--priv->curr_row < 0)
+ priv->curr_row = 0;
+ }
+
+ vidconsole_putc_xy(dev, priv->curr_col * VIDEO_FONT_WIDTH,
+ priv->curr_row * VIDEO_FONT_HEIGHT, ' ');
+}
+
+/* Move to a newline, scrolling the display if necessary */
+static void vidconsole_newline(struct udevice *dev)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ struct udevice *vid_dev = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
+ const int rows = CONFIG_CONSOLE_SCROLL_LINES;
+ int i;
+
+ priv->curr_col = 0;
+
+ /* Check if we need to scroll the terminal */
+ if (++priv->curr_row >= priv->rows) {
+ vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
+ for (i = 0; i < rows; i++)
+ vidconsole_set_row(dev, priv->rows - i - 1,
+ vid_priv->colour_bg);
+ priv->curr_row -= rows;
+ }
+ video_sync(dev->parent);
+}
+
+int vidconsole_put_char(struct udevice *dev, char ch)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ int ret;
+
+ switch (ch) {
+ case '\r':
+ priv->curr_col = 0;
+ break;
+ case '\n':
+ vidconsole_newline(dev);
+ break;
+ case '\t': /* Tab (8 chars alignment) */
+ priv->curr_col += 8;
+ priv->curr_col &= ~7;
+
+ if (priv->curr_col >= priv->cols)
+ vidconsole_newline(dev);
+ break;
+ case '\b':
+ vidconsole_back(dev);
+ break;
+ default:
+ /*
+ * Failure of this function normally indicates an unsupported
+ * colour depth. Check this and return an error to help with
+ * diagnosis.
+ */
+ ret = vidconsole_putc_xy(dev,
+ priv->curr_col * VIDEO_FONT_WIDTH,
+ priv->curr_row * VIDEO_FONT_HEIGHT,
+ ch);
+ if (ret)
+ return ret;
+ if (++priv->curr_col >= priv->cols)
+ vidconsole_newline(dev);
+ break;
+ }
+
+ return 0;
+}
+
+static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
+{
+ struct udevice *dev = sdev->priv;
+
+ vidconsole_put_char(dev, ch);
+}
+
+static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
+{
+ struct udevice *dev = sdev->priv;
+
+ while (*s)
+ vidconsole_put_char(dev, *s++);
+}
+
+/* Set up the number of rows and colours (rotated drivers override this) */
+static int vidconsole_pre_probe(struct udevice *dev)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ struct udevice *vid = dev->parent;
+ struct video_priv *vid_priv = dev_get_uclass_priv(vid);
+
+ priv->rows = vid_priv->ysize / VIDEO_FONT_HEIGHT;
+ priv->cols = vid_priv->xsize / VIDEO_FONT_WIDTH;
+
+ return 0;
+}
+
+/* Register the device with stdio */
+static int vidconsole_post_probe(struct udevice *dev)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+ struct stdio_dev *sdev = &priv->sdev;
+ int ret;
+
+ strlcpy(sdev->name, dev->name, sizeof(sdev->name));
+ sdev->flags = DEV_FLAGS_OUTPUT;
+ sdev->putc = vidconsole_putc;
+ sdev->puts = vidconsole_puts;
+ sdev->priv = dev;
+ ret = stdio_register(sdev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+UCLASS_DRIVER(vidconsole) = {
+ .id = UCLASS_VIDEO_CONSOLE,
+ .name = "vidconsole0",
+ .pre_probe = vidconsole_pre_probe,
+ .post_probe = vidconsole_post_probe,
+ .per_device_auto_alloc_size = sizeof(struct vidconsole_priv),
+};
+
+void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
+{
+ struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
+
+ priv->curr_col = min_t(short, col, priv->cols - 1);
+ priv->curr_row = min_t(short, row, priv->rows - 1);
+}
+
+static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ unsigned int col, row;
+ struct udevice *dev;
+
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev);
+ if (!dev)
+ return CMD_RET_FAILURE;
+ col = simple_strtoul(argv[1], NULL, 10);
+ row = simple_strtoul(argv[2], NULL, 10);
+ vidconsole_position_cursor(dev, col, row);
+
+ return 0;
+}
+
+static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *dev;
+ const char *s;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev);
+ if (!dev)
+ return CMD_RET_FAILURE;
+ for (s = argv[1]; *s; s++)
+ vidconsole_put_char(dev, *s);
+
+ return 0;
+}
+
+U_BOOT_CMD(
+ setcurs, 3, 1, do_video_setcursor,
+ "set cursor position within screen",
+ " <col> <row> in character"
+);
+
+U_BOOT_CMD(
+ lcdputs, 2, 1, do_video_puts,
+ "print string on video framebuffer",
+ " <string>"
+);
diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c
new file mode 100644
index 0000000000..63d0d9d7d3
--- /dev/null
+++ b/drivers/video/video-uclass.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <stdio_dev.h>
+#include <video.h>
+#include <video_console.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#ifdef CONFIG_SANDBOX
+#include <asm/sdl.h>
+#endif
+
+/*
+ * Theory of operation:
+ *
+ * Before relocation each device is bound. The driver for each device must
+ * set the @align and @size values in struct video_uc_platdata. This
+ * information represents the requires size and alignment of the frame buffer
+ * for the device. The values can be an over-estimate but cannot be too
+ * small. The actual values will be suppled (in the same manner) by the bind()
+ * method after relocation.
+ *
+ * This information is then picked up by video_reserve() which works out how
+ * much memory is needed for all devices. This is allocated between
+ * gd->video_bottom and gd->video_top.
+ *
+ * After relocation the same process occurs. The driver supplies the same
+ * @size and @align information and this time video_post_bind() checks that
+ * the drivers does not overflow the allocated memory.
+ *
+ * The frame buffer address is actually set (to plat->base) in
+ * video_post_probe(). This function also clears the frame buffer and
+ * allocates a suitable text console device. This can then be used to write
+ * text to the video device.
+ */
+DECLARE_GLOBAL_DATA_PTR;
+
+static ulong alloc_fb(struct udevice *dev, ulong *addrp)
+{
+ struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+ ulong base, align, size;
+
+ align = plat->align ? plat->align : 1 << 20;
+ base = *addrp - plat->size;
+ base &= ~(align - 1);
+ plat->base = base;
+ size = *addrp - base;
+ *addrp = base;
+
+ return size;
+}
+
+int video_reserve(ulong *addrp)
+{
+ struct udevice *dev;
+ ulong size;
+
+ gd->video_top = *addrp;
+ for (uclass_find_first_device(UCLASS_VIDEO, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ size = alloc_fb(dev, addrp);
+ debug("%s: Reserving %lx bytes at %lx for video device '%s'\n",
+ __func__, size, *addrp, dev->name);
+ }
+ gd->video_bottom = *addrp;
+ debug("Video frame buffers from %lx to %lx\n", gd->video_bottom,
+ gd->video_top);
+
+ return 0;
+}
+
+static int video_clear(struct udevice *dev)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+
+ if (priv->bpix == VIDEO_BPP32) {
+ u32 *ppix = priv->fb;
+ u32 *end = priv->fb + priv->fb_size;
+
+ while (ppix < end)
+ *ppix++ = priv->colour_bg;
+ } else {
+ memset(priv->fb, priv->colour_bg, priv->fb_size);
+ }
+
+ return 0;
+}
+
+/* Flush video activity to the caches */
+void video_sync(struct udevice *vid)
+{
+ /*
+ * flush_dcache_range() is declared in common.h but it seems that some
+ * architectures do not actually implement it. Is there a way to find
+ * out whether it exists? For now, ARM is safe.
+ */
+#if defined(CONFIG_ARM) && !defined(CONFIG_SYS_DCACHE_OFF)
+ struct video_priv *priv = dev_get_uclass_priv(vid);
+
+ if (priv->flush_dcache) {
+ flush_dcache_range((ulong)priv->fb,
+ (ulong)priv->fb + priv->fb_size);
+ }
+#elif defined(CONFIG_VIDEO_SANDBOX_SDL)
+ struct video_priv *priv = dev_get_uclass_priv(vid);
+ static ulong last_sync;
+
+ if (get_timer(last_sync) > 10) {
+ sandbox_sdl_sync(priv->fb);
+ last_sync = get_timer(0);
+ }
+#endif
+}
+
+void video_sync_all(void)
+{
+ struct udevice *dev;
+
+ for (uclass_find_first_device(UCLASS_VIDEO, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ if (device_active(dev))
+ video_sync(dev);
+ }
+}
+
+int video_get_xsize(struct udevice *dev)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+
+ return priv->xsize;
+}
+
+int video_get_ysize(struct udevice *dev)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+
+ return priv->ysize;
+}
+
+/* Set up the colour map */
+static int video_pre_probe(struct udevice *dev)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+
+ priv->cmap = calloc(256, sizeof(ushort));
+ if (!priv->cmap)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int video_pre_remove(struct udevice *dev)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+
+ free(priv->cmap);
+
+ return 0;
+}
+
+/* Set up the display ready for use */
+static int video_post_probe(struct udevice *dev)
+{
+ struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ char name[30], drv[15], *str;
+ struct udevice *cons;
+ int ret;
+
+ /* Set up the line and display size */
+ priv->fb = map_sysmem(plat->base, plat->size);
+ priv->line_length = priv->xsize * VNBYTES(priv->bpix);
+ priv->fb_size = priv->line_length * priv->ysize;
+
+ /* Set up colours - we could in future support other colours */
+#ifdef CONFIG_SYS_WHITE_ON_BLACK
+ priv->colour_fg = 0xffffff;
+#else
+ priv->colour_bg = 0xffffff;
+#endif
+ video_clear(dev);
+
+ /*
+ * Create a text console devices. For now we always do this, although
+ * it might be useful to support only bitmap drawing on the device
+ * for boards that don't need to display text.
+ */
+ snprintf(name, sizeof(name), "%s.vidconsole", dev->name);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ snprintf(drv, sizeof(drv), "vidconsole%d", priv->rot);
+ ret = device_bind_driver(dev, drv, str, &cons);
+ if (ret) {
+ debug("%s: Cannot bind console driver\n", __func__);
+ return ret;
+ }
+ ret = device_probe(cons);
+ if (ret) {
+ debug("%s: Cannot probe console driver\n", __func__);
+ return ret;
+ }
+
+ return 0;
+};
+
+/* Post-relocation, allocate memory for the frame buffer */
+static int video_post_bind(struct udevice *dev)
+{
+ ulong addr = gd->video_top;
+ ulong size;
+
+ /* Before relocation there is nothing to do here */
+ if ((!gd->flags & GD_FLG_RELOC))
+ return 0;
+ size = alloc_fb(dev, &addr);
+ if (addr < gd->video_bottom) {
+ /* Device tree node may need the 'u-boot,dm-pre-reloc' tag */
+ printf("Video device '%s' cannot allocate frame buffer memory -ensure the device is set up before relocation\n",
+ dev->name);
+ return -ENOSPC;
+ }
+ debug("%s: Claiming %lx bytes at %lx for video device '%s'\n",
+ __func__, size, addr, dev->name);
+ gd->video_bottom = addr;
+
+ return 0;
+}
+
+UCLASS_DRIVER(video) = {
+ .id = UCLASS_VIDEO,
+ .name = "video",
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+ .post_bind = video_post_bind,
+ .pre_probe = video_pre_probe,
+ .post_probe = video_post_probe,
+ .pre_remove = video_pre_remove,
+ .per_device_auto_alloc_size = sizeof(struct video_priv),
+ .per_device_platdata_auto_alloc_size = sizeof(struct video_uc_platdata),
+};
diff --git a/drivers/video/video_bmp.c b/drivers/video/video_bmp.c
new file mode 100644
index 0000000000..c9075d62dd
--- /dev/null
+++ b/drivers/video/video_bmp.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <bmp_layout.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <video.h>
+#include <watchdog.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_VIDEO_BMP_RLE8
+#define BMP_RLE8_ESCAPE 0
+#define BMP_RLE8_EOL 0
+#define BMP_RLE8_EOBMP 1
+#define BMP_RLE8_DELTA 2
+
+static void draw_unencoded_bitmap(ushort **fbp, uchar *bmap, ushort *cmap,
+ int cnt)
+{
+ while (cnt > 0) {
+ *(*fbp)++ = cmap[*bmap++];
+ cnt--;
+ }
+}
+
+static void draw_encoded_bitmap(ushort **fbp, ushort col, int cnt)
+{
+ ushort *fb = *fbp;
+
+ while (cnt > 0) {
+ *fb++ = col;
+ cnt--;
+ }
+ *fbp = fb;
+}
+
+static void video_display_rle8_bitmap(struct udevice *dev,
+ struct bmp_image *bmp, ushort *cmap,
+ uchar *fb, int x_off, int y_off)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ uchar *bmap;
+ ulong width, height;
+ ulong cnt, runlen;
+ int x, y;
+ int decode = 1;
+
+ debug("%s\n", __func__);
+ width = get_unaligned_le32(&bmp->header.width);
+ height = get_unaligned_le32(&bmp->header.height);
+ bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
+
+ x = 0;
+ y = height - 1;
+
+ while (decode) {
+ if (bmap[0] == BMP_RLE8_ESCAPE) {
+ switch (bmap[1]) {
+ case BMP_RLE8_EOL:
+ /* end of line */
+ bmap += 2;
+ x = 0;
+ y--;
+ /* 16bpix, 2-byte per pixel, width should *2 */
+ fb -= (width * 2 + priv->line_length);
+ break;
+ case BMP_RLE8_EOBMP:
+ /* end of bitmap */
+ decode = 0;
+ break;
+ case BMP_RLE8_DELTA:
+ /* delta run */
+ x += bmap[2];
+ y -= bmap[3];
+ /* 16bpix, 2-byte per pixel, x should *2 */
+ fb = (uchar *)(priv->fb + (y + y_off - 1)
+ * priv->line_length + (x + x_off) * 2);
+ bmap += 4;
+ break;
+ default:
+ /* unencoded run */
+ runlen = bmap[1];
+ bmap += 2;
+ if (y < height) {
+ if (x < width) {
+ if (x + runlen > width)
+ cnt = width - x;
+ else
+ cnt = runlen;
+ draw_unencoded_bitmap(
+ (ushort **)&fb,
+ bmap, cmap, cnt);
+ }
+ x += runlen;
+ }
+ bmap += runlen;
+ if (runlen & 1)
+ bmap++;
+ }
+ } else {
+ /* encoded run */
+ if (y < height) {
+ runlen = bmap[0];
+ if (x < width) {
+ /* aggregate the same code */
+ while (bmap[0] == 0xff &&
+ bmap[2] != BMP_RLE8_ESCAPE &&
+ bmap[1] == bmap[3]) {
+ runlen += bmap[2];
+ bmap += 2;
+ }
+ if (x + runlen > width)
+ cnt = width - x;
+ else
+ cnt = runlen;
+ draw_encoded_bitmap((ushort **)&fb,
+ cmap[bmap[1]], cnt);
+ }
+ x += runlen;
+ }
+ bmap += 2;
+ }
+ }
+}
+#endif
+
+__weak void fb_put_byte(uchar **fb, uchar **from)
+{
+ *(*fb)++ = *(*from)++;
+}
+
+#if defined(CONFIG_BMP_16BPP)
+__weak void fb_put_word(uchar **fb, uchar **from)
+{
+ *(*fb)++ = *(*from)++;
+ *(*fb)++ = *(*from)++;
+}
+#endif /* CONFIG_BMP_16BPP */
+
+#define BMP_ALIGN_CENTER 0x7fff
+
+/**
+ * video_splash_align_axis() - Align a single coordinate
+ *
+ *- if a coordinate is 0x7fff then the image will be centred in
+ * that direction
+ *- if a coordinate is -ve then it will be offset to the
+ * left/top of the centre by that many pixels
+ *- if a coordinate is positive it will be used unchnaged.
+ *
+ * @axis: Input and output coordinate
+ * @panel_size: Size of panel in pixels for that axis
+ * @picture_size: Size of bitmap in pixels for that axis
+ */
+static void video_splash_align_axis(int *axis, unsigned long panel_size,
+ unsigned long picture_size)
+{
+ unsigned long panel_picture_delta = panel_size - picture_size;
+ unsigned long axis_alignment;
+
+ if (*axis == BMP_ALIGN_CENTER)
+ axis_alignment = panel_picture_delta / 2;
+ else if (*axis < 0)
+ axis_alignment = panel_picture_delta + *axis + 1;
+ else
+ return;
+
+ *axis = max(0, (int)axis_alignment);
+}
+
+static void video_set_cmap(struct udevice *dev,
+ struct bmp_color_table_entry *cte, unsigned colours)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ int i;
+ ushort *cmap = priv->cmap;
+
+ debug("%s: colours=%d\n", __func__, colours);
+ for (i = 0; i < colours; ++i) {
+ *cmap = ((cte->red << 8) & 0xf800) |
+ ((cte->green << 3) & 0x07e0) |
+ ((cte->blue >> 3) & 0x001f);
+ cmap++;
+ cte++;
+ }
+}
+
+int video_bmp_display(struct udevice *dev, ulong bmp_image, int x, int y,
+ bool align)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ ushort *cmap_base = NULL;
+ ushort i, j;
+ uchar *fb;
+ struct bmp_image *bmp = map_sysmem(bmp_image, 0);
+ uchar *bmap;
+ ushort padded_width;
+ unsigned long width, height, byte_width;
+ unsigned long pwidth = priv->xsize;
+ unsigned colours, bpix, bmp_bpix;
+ struct bmp_color_table_entry *palette;
+ int hdr_size;
+
+ if (!bmp || !(bmp->header.signature[0] == 'B' &&
+ bmp->header.signature[1] == 'M')) {
+ printf("Error: no valid bmp image at %lx\n", bmp_image);
+
+ return -EINVAL;
+ }
+
+ width = get_unaligned_le32(&bmp->header.width);
+ height = get_unaligned_le32(&bmp->header.height);
+ bmp_bpix = get_unaligned_le16(&bmp->header.bit_count);
+ hdr_size = get_unaligned_le16(&bmp->header.size);
+ debug("hdr_size=%d, bmp_bpix=%d\n", hdr_size, bmp_bpix);
+ palette = (void *)bmp + 14 + hdr_size;
+
+ colours = 1 << bmp_bpix;
+
+ bpix = VNBITS(priv->bpix);
+
+ if (bpix != 1 && bpix != 8 && bpix != 16 && bpix != 32) {
+ printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
+ bpix, bmp_bpix);
+
+ return -EINVAL;
+ }
+
+ /*
+ * We support displaying 8bpp BMPs on 16bpp LCDs
+ * and displaying 24bpp BMPs on 32bpp LCDs
+ * */
+ if (bpix != bmp_bpix &&
+ !(bmp_bpix == 8 && bpix == 16) &&
+ !(bmp_bpix == 24 && bpix == 32)) {
+ printf("Error: %d bit/pixel mode, but BMP has %d bit/pixel\n",
+ bpix, get_unaligned_le16(&bmp->header.bit_count));
+ return -EPERM;
+ }
+
+ debug("Display-bmp: %d x %d with %d colours, display %d\n",
+ (int)width, (int)height, (int)colours, 1 << bpix);
+
+ if (bmp_bpix == 8)
+ video_set_cmap(dev, palette, colours);
+
+ padded_width = (width & 0x3 ? (width & ~0x3) + 4 : width);
+
+ if (align) {
+ video_splash_align_axis(&x, priv->xsize, width);
+ video_splash_align_axis(&y, priv->ysize, height);
+ }
+
+ if ((x + width) > pwidth)
+ width = pwidth - x;
+ if ((y + height) > priv->ysize)
+ height = priv->ysize - y;
+
+ bmap = (uchar *)bmp + get_unaligned_le32(&bmp->header.data_offset);
+ fb = (uchar *)(priv->fb +
+ (y + height - 1) * priv->line_length + x * bpix / 8);
+
+ switch (bmp_bpix) {
+ case 1:
+ case 8: {
+ cmap_base = priv->cmap;
+#ifdef CONFIG_VIDEO_BMP_RLE8
+ u32 compression = get_unaligned_le32(&bmp->header.compression);
+ debug("compressed %d %d\n", compression, BMP_BI_RLE8);
+ if (compression == BMP_BI_RLE8) {
+ if (bpix != 16) {
+ /* TODO implement render code for bpix != 16 */
+ printf("Error: only support 16 bpix");
+ return -EPROTONOSUPPORT;
+ }
+ video_display_rle8_bitmap(dev, bmp, cmap_base, fb, x,
+ y);
+ break;
+ }
+#endif
+
+ if (bpix != 16)
+ byte_width = width;
+ else
+ byte_width = width * 2;
+
+ for (i = 0; i < height; ++i) {
+ WATCHDOG_RESET();
+ for (j = 0; j < width; j++) {
+ if (bpix != 16) {
+ fb_put_byte(&fb, &bmap);
+ } else {
+ *(uint16_t *)fb = cmap_base[*bmap];
+ bmap++;
+ fb += sizeof(uint16_t) / sizeof(*fb);
+ }
+ }
+ bmap += (padded_width - width);
+ fb -= byte_width + priv->line_length;
+ }
+ break;
+ }
+#if defined(CONFIG_BMP_16BPP)
+ case 16:
+ for (i = 0; i < height; ++i) {
+ WATCHDOG_RESET();
+ for (j = 0; j < width; j++)
+ fb_put_word(&fb, &bmap);
+
+ bmap += (padded_width - width) * 2;
+ fb -= width * 2 + lcd_line_length;
+ }
+ break;
+#endif /* CONFIG_BMP_16BPP */
+#if defined(CONFIG_BMP_24BMP)
+ case 24:
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; j++) {
+ *(fb++) = *(bmap++);
+ *(fb++) = *(bmap++);
+ *(fb++) = *(bmap++);
+ *(fb++) = 0;
+ }
+ fb -= lcd_line_length + width * (bpix / 8);
+ }
+ break;
+#endif /* CONFIG_BMP_24BMP */
+#if defined(CONFIG_BMP_32BPP)
+ case 32:
+ for (i = 0; i < height; ++i) {
+ for (j = 0; j < width; j++) {
+ *(fb++) = *(bmap++);
+ *(fb++) = *(bmap++);
+ *(fb++) = *(bmap++);
+ *(fb++) = *(bmap++);
+ }
+ fb -= lcd_line_length + width * (bpix / 8);
+ }
+ break;
+#endif /* CONFIG_BMP_32BPP */
+ default:
+ break;
+ };
+
+ video_sync(dev);
+
+ return 0;
+}
+