dm: video: Add a uclass for the text console
authorSimon Glass <sjg@chromium.org>
Tue, 19 Jan 2016 02:52:17 +0000 (19:52 -0700)
committerSimon Glass <sjg@chromium.org>
Thu, 21 Jan 2016 02:10:15 +0000 (19:10 -0700)
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 <sjg@chromium.org>
Acked-by: Anatolij Gustschin <agust@denx.de>
drivers/video/Makefile
drivers/video/vidconsole-uclass.c [new file with mode: 0644]
drivers/video/video-uclass.c
include/dm/uclass-id.h
include/video_console.h [new file with mode: 0644]

index 7192013..e82f1ae 100644 (file)
@@ -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 (file)
index 0000000..ea10189
--- /dev/null
@@ -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>"
+);
index 1615889..63d0d9d 100644 (file)
@@ -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;
 };
 
index 3934375..a0a3a79 100644 (file)
@@ -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 (file)
index 0000000..c0fc792
--- /dev/null
@@ -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