summaryrefslogtreecommitdiff
path: root/drivers/video
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2016-01-21 16:49:49 (GMT)
committerTom Rini <trini@konsulko.com>2016-01-21 16:49:49 (GMT)
commit6905f4d3c7be46fed4859f51f0a8f9a1107c22e7 (patch)
treedd2fb0d12e56b3d69560b26a11ee0f0130ea0a5a /drivers/video
parent45fe3809b9923b92f221d70eb45ae071059fd5e0 (diff)
parent747440d0fa95f2205a8fcef49b6c7845700b6246 (diff)
downloadu-boot-6905f4d3c7be46fed4859f51f0a8f9a1107c22e7.tar.xz
Merge git://git.denx.de/u-boot-dm
Diffstat (limited to 'drivers/video')
-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
8 files changed, 1524 insertions, 49 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index caf1efc..ae122da 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 e85fd8a..ee04629 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 0000000..d1031c8
--- /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 0000000..ebb31d8
--- /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 ba4578e..21448a1 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 0000000..ea10189
--- /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 0000000..63d0d9d
--- /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 0000000..c9075d6
--- /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;
+}
+