From 83510766c90a52e58477267704fc5ca8f75c3dab Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Mon, 18 Jan 2016 19:52:17 -0700 Subject: dm: video: Add a uclass for the text console The existing LCD/video interface suffers from conflating the bitmap display with text output on that display. As a result the implementation is more complex than it needs to me. We can support multiple text console drivers. Create a separate uclass to support this, with its own API. Signed-off-by: Simon Glass Acked-by: Anatolij Gustschin --- drivers/video/Makefile | 2 +- drivers/video/vidconsole-uclass.c | 239 ++++++++++++++++++++++++++++++++++++++ drivers/video/video-uclass.c | 21 ++++ 3 files changed, 261 insertions(+), 1 deletion(-) create mode 100644 drivers/video/vidconsole-uclass.c (limited to 'drivers/video') diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 7192013..e82f1ae 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -7,7 +7,7 @@ ifdef CONFIG_DM obj-$(CONFIG_DISPLAY_PORT) += dp-uclass.o -obj-$(CONFIG_DM_VIDEO) += video-uclass.o +obj-$(CONFIG_DM_VIDEO) += video-uclass.o vidconsole-uclass.o endif obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o 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 +#include +#include +#include +#include /* 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", + " in character" +); + +U_BOOT_CMD( + lcdputs, 2, 1, do_video_puts, + "print string on video framebuffer", + " " +); diff --git a/drivers/video/video-uclass.c b/drivers/video/video-uclass.c index 1615889..63d0d9d 100644 --- a/drivers/video/video-uclass.c +++ b/drivers/video/video-uclass.c @@ -189,6 +189,27 @@ static int video_post_probe(struct udevice *dev) #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; }; -- cgit v1.1