From: Simon Glass Date: Tue, 19 Jan 2016 02:52:17 +0000 (-0700) Subject: dm: video: Add a uclass for the text console X-Git-Tag: v2016.03-rc1~243^2~11 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=83510766c90a52e58477267704fc5ca8f75c3dab;p=platform%2Fkernel%2Fu-boot.git 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 --- 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; }; diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h index 3934375..a0a3a79 100644 --- a/include/dm/uclass-id.h +++ b/include/dm/uclass-id.h @@ -68,6 +68,7 @@ enum uclass_id { UCLASS_USB_HUB, /* USB hub */ UCLASS_VIDEO, /* Video or LCD device */ UCLASS_VIDEO_BRIDGE, /* Video bridge, e.g. DisplayPort to LVDS */ + UCLASS_VIDEO_CONSOLE, /* Text console driver for video device */ UCLASS_COUNT, UCLASS_INVALID = -1, diff --git a/include/video_console.h b/include/video_console.h new file mode 100644 index 0000000..c0fc792 --- /dev/null +++ b/include/video_console.h @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2015 Google, Inc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __video_console_h +#define __video_console_h + +/** + * struct vidconsole_priv - uclass-private data about a console device + * + * @sdev: stdio device, acting as an output sink + * @curr_col: Current text column (0=left) + * @curr_row: Current row (0=top) + * @rows: Number of text rows + * @cols: Number of text columns + */ +struct vidconsole_priv { + struct stdio_dev sdev; + int curr_col; + int curr_row; + int rows; + int cols; +}; + +/** + * struct vidconsole_ops - Video console operations + * + * These operations work on either an absolute console position (measured + * in pixels) or a text row number (measured in rows, where each row consists + * of an entire line of text - typically 16 pixels). + */ +struct vidconsole_ops { + /** + * putc_xy() - write a single character to a position + * + * @dev: Device to write to + * @x: Pixel X position (0=left-most pixel) + * @y: Pixel Y position (0=top-most pixel) + * @ch: Character to write + * @return 0 if OK, -ve on error + */ + int (*putc_xy)(struct udevice *dev, uint x, uint y, char ch); + + /** + * move_rows() - Move text rows from one place to another + * + * @dev: Device to adjust + * @rowdst: Destination text row (0=top) + * @rowsrc: Source start text row + * @count: Number of text rows to move + * @return 0 if OK, -ve on error + */ + int (*move_rows)(struct udevice *dev, uint rowdst, uint rowsrc, + uint count); + + /** + * set_row() - Set the colour of a text row + * + * Every pixel contained within the text row is adjusted + * + * @dev: Device to adjust + * @row: Text row to adjust (0=top) + * @clr: Raw colour (pixel value) to write to each pixel + * @return 0 if OK, -ve on error + */ + int (*set_row)(struct udevice *dev, uint row, int clr); +}; + +/* Get a pointer to the driver operations for a video console device */ +#define vidconsole_get_ops(dev) ((struct vidconsole_ops *)(dev)->driver->ops) + +/** + * vidconsole_putc_xy() - write a single character to a position + * + * @dev: Device to write to + * @x: Pixel X position (0=left-most pixel) + * @y: Pixel Y position (0=top-most pixel) + * @ch: Character to write + * @return 0 if OK, -ve on error + */ +int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch); + +/** + * vidconsole_move_rows() - Move text rows from one place to another + * + * @dev: Device to adjust + * @rowdst: Destination text row (0=top) + * @rowsrc: Source start text row + * @count: Number of text rows to move + * @return 0 if OK, -ve on error + */ +int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc, + uint count); + +/** + * vidconsole_set_row() - Set the colour of a text row + * + * Every pixel contained within the text row is adjusted + * + * @dev: Device to adjust + * @row: Text row to adjust (0=top) + * @clr: Raw colour (pixel value) to write to each pixel + * @return 0 if OK, -ve on error + */ +int vidconsole_set_row(struct udevice *dev, uint row, int clr); + +/** + * vidconsole_put_char() - Output a character to the current console position + * + * Outputs a character to the console and advances the cursor. This function + * handles wrapping to new lines and scrolling the console. Special + * characters are handled also: \n, \r, \b and \t. + * + * The device always starts with the cursor at position 0,0 (top left). It + * can be adjusted manually using vidconsole_position_cursor(). + * + * @dev: Device to adjust + * @ch: Character to write + * @return 0 if OK, -ve on error + */ +int vidconsole_put_char(struct udevice *dev, char ch); + +/** + * vidconsole_position_cursor() - Move the text cursor + * + * @dev: Device to adjust + * @col: New cursor text column + * @row: New cursor text row + * @return 0 if OK, -ve on error + */ +void vidconsole_position_cursor(struct udevice *dev, unsigned col, + unsigned row); + +#endif