dm: video: Add a uclass for the text console
[platform/kernel/u-boot.git] / drivers / video / vidconsole-uclass.c
1 /*
2  * Copyright (c) 2015 Google, Inc
3  * (C) Copyright 2001-2015
4  * DENX Software Engineering -- wd@denx.de
5  * Compulab Ltd - http://compulab.co.il/
6  * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
7  *
8  * SPDX-License-Identifier:     GPL-2.0+
9  */
10
11 #include <common.h>
12 #include <dm.h>
13 #include <video.h>
14 #include <video_console.h>
15 #include <video_font.h>         /* Get font data, width and height */
16
17 /* By default we scroll by a single line */
18 #ifndef CONFIG_CONSOLE_SCROLL_LINES
19 #define CONFIG_CONSOLE_SCROLL_LINES 1
20 #endif
21
22 int vidconsole_putc_xy(struct udevice *dev, uint x, uint y, char ch)
23 {
24         struct vidconsole_ops *ops = vidconsole_get_ops(dev);
25
26         if (!ops->putc_xy)
27                 return -ENOSYS;
28         return ops->putc_xy(dev, x, y, ch);
29 }
30
31 int vidconsole_move_rows(struct udevice *dev, uint rowdst, uint rowsrc,
32                          uint count)
33 {
34         struct vidconsole_ops *ops = vidconsole_get_ops(dev);
35
36         if (!ops->move_rows)
37                 return -ENOSYS;
38         return ops->move_rows(dev, rowdst, rowsrc, count);
39 }
40
41 int vidconsole_set_row(struct udevice *dev, uint row, int clr)
42 {
43         struct vidconsole_ops *ops = vidconsole_get_ops(dev);
44
45         if (!ops->set_row)
46                 return -ENOSYS;
47         return ops->set_row(dev, row, clr);
48 }
49
50 /* Move backwards one space */
51 static void vidconsole_back(struct udevice *dev)
52 {
53         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
54
55         if (--priv->curr_col < 0) {
56                 priv->curr_col = priv->cols - 1;
57                 if (--priv->curr_row < 0)
58                         priv->curr_row = 0;
59         }
60
61         vidconsole_putc_xy(dev, priv->curr_col * VIDEO_FONT_WIDTH,
62                            priv->curr_row * VIDEO_FONT_HEIGHT, ' ');
63 }
64
65 /* Move to a newline, scrolling the display if necessary */
66 static void vidconsole_newline(struct udevice *dev)
67 {
68         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
69         struct udevice *vid_dev = dev->parent;
70         struct video_priv *vid_priv = dev_get_uclass_priv(vid_dev);
71         const int rows = CONFIG_CONSOLE_SCROLL_LINES;
72         int i;
73
74         priv->curr_col = 0;
75
76         /* Check if we need to scroll the terminal */
77         if (++priv->curr_row >= priv->rows) {
78                 vidconsole_move_rows(dev, 0, rows, priv->rows - rows);
79                 for (i = 0; i < rows; i++)
80                         vidconsole_set_row(dev, priv->rows - i - 1,
81                                            vid_priv->colour_bg);
82                 priv->curr_row -= rows;
83         }
84         video_sync(dev->parent);
85 }
86
87 int vidconsole_put_char(struct udevice *dev, char ch)
88 {
89         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
90         int ret;
91
92         switch (ch) {
93         case '\r':
94                 priv->curr_col = 0;
95                 break;
96         case '\n':
97                 vidconsole_newline(dev);
98                 break;
99         case '\t':      /* Tab (8 chars alignment) */
100                 priv->curr_col +=  8;
101                 priv->curr_col &= ~7;
102
103                 if (priv->curr_col >= priv->cols)
104                         vidconsole_newline(dev);
105                 break;
106         case '\b':
107                 vidconsole_back(dev);
108                 break;
109         default:
110                 /*
111                  * Failure of this function normally indicates an unsupported
112                  * colour depth. Check this and return an error to help with
113                  * diagnosis.
114                  */
115                 ret = vidconsole_putc_xy(dev,
116                                          priv->curr_col * VIDEO_FONT_WIDTH,
117                                          priv->curr_row * VIDEO_FONT_HEIGHT,
118                                          ch);
119                 if (ret)
120                         return ret;
121                 if (++priv->curr_col >= priv->cols)
122                         vidconsole_newline(dev);
123                 break;
124         }
125
126         return 0;
127 }
128
129 static void vidconsole_putc(struct stdio_dev *sdev, const char ch)
130 {
131         struct udevice *dev = sdev->priv;
132
133         vidconsole_put_char(dev, ch);
134 }
135
136 static void vidconsole_puts(struct stdio_dev *sdev, const char *s)
137 {
138         struct udevice *dev = sdev->priv;
139
140         while (*s)
141                 vidconsole_put_char(dev, *s++);
142 }
143
144 /* Set up the number of rows and colours (rotated drivers override this) */
145 static int vidconsole_pre_probe(struct udevice *dev)
146 {
147         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
148         struct udevice *vid = dev->parent;
149         struct video_priv *vid_priv = dev_get_uclass_priv(vid);
150
151         priv->rows = vid_priv->ysize / VIDEO_FONT_HEIGHT;
152         priv->cols = vid_priv->xsize / VIDEO_FONT_WIDTH;
153
154         return 0;
155 }
156
157 /* Register the device with stdio */
158 static int vidconsole_post_probe(struct udevice *dev)
159 {
160         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
161         struct stdio_dev *sdev = &priv->sdev;
162         int ret;
163
164         strlcpy(sdev->name, dev->name, sizeof(sdev->name));
165         sdev->flags = DEV_FLAGS_OUTPUT;
166         sdev->putc = vidconsole_putc;
167         sdev->puts = vidconsole_puts;
168         sdev->priv = dev;
169         ret = stdio_register(sdev);
170         if (ret)
171                 return ret;
172
173         return 0;
174 }
175
176 UCLASS_DRIVER(vidconsole) = {
177         .id             = UCLASS_VIDEO_CONSOLE,
178         .name           = "vidconsole0",
179         .pre_probe      = vidconsole_pre_probe,
180         .post_probe     = vidconsole_post_probe,
181         .per_device_auto_alloc_size     = sizeof(struct vidconsole_priv),
182 };
183
184 void vidconsole_position_cursor(struct udevice *dev, unsigned col, unsigned row)
185 {
186         struct vidconsole_priv *priv = dev_get_uclass_priv(dev);
187
188         priv->curr_col = min_t(short, col, priv->cols - 1);
189         priv->curr_row = min_t(short, row, priv->rows - 1);
190 }
191
192 static int do_video_setcursor(cmd_tbl_t *cmdtp, int flag, int argc,
193                               char *const argv[])
194 {
195         unsigned int col, row;
196         struct udevice *dev;
197
198         if (argc != 3)
199                 return CMD_RET_USAGE;
200
201         uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev);
202         if (!dev)
203                 return CMD_RET_FAILURE;
204         col = simple_strtoul(argv[1], NULL, 10);
205         row = simple_strtoul(argv[2], NULL, 10);
206         vidconsole_position_cursor(dev, col, row);
207
208         return 0;
209 }
210
211 static int do_video_puts(cmd_tbl_t *cmdtp, int flag, int argc,
212                          char *const argv[])
213 {
214         struct udevice *dev;
215         const char *s;
216
217         if (argc != 2)
218                 return CMD_RET_USAGE;
219
220         uclass_first_device(UCLASS_VIDEO_CONSOLE, &dev);
221         if (!dev)
222                 return CMD_RET_FAILURE;
223         for (s = argv[1]; *s; s++)
224                 vidconsole_put_char(dev, *s);
225
226         return 0;
227 }
228
229 U_BOOT_CMD(
230         setcurs, 3,     1,      do_video_setcursor,
231         "set cursor position within screen",
232         "    <col> <row> in character"
233 );
234
235 U_BOOT_CMD(
236         lcdputs, 2,     1,      do_video_puts,
237         "print string on video framebuffer",
238         "    <string>"
239 );