Skip to contentSkip to conversation

ansi console support for lcd driver

RFC New Andrey Danin 3 participants 10 messages Mar 13, 2015 – Mar 18, 2015 Framebuffer layer

lore
Share & tools
lore
#0 lore raw

cover ansi console support for lcd driver

Main reason for this patches is ability to use bootmenu on devices that use lcd driver (like Toshiba AC100).

Lcd driver doesn't have ansi support while cfb_console does. Ansi related code was moved from cfb_console to separate place. Then this code was used in both cfb_console and lcd driver.

There are other duplicated code between cfb_console and lcd.

I'm not very experienced with video subsystem. Maybe there is a better/more proper way to add ansi support for Toshiba AC100.

---

Andrey Danin (4):
common: add ansi console base implementation
video: cfb_console: use common ansi implementation
lcd: use ansi console
paz00: enable bootmenu

 common/Makefile             |   2 +-
 common/ansi_console.c       | 355 +++++++++++++++++++++++++++++++++++++++++
 common/lcd.c                |  24 ---
 common/lcd_console.c        | 122 ++++++++------
 drivers/video/Makefile      |   2 +-
 drivers/video/cfb_console.c | 381 ++++----------------------------------------
 include/ansi_console.h      |  39 +++++
 include/configs/paz00.h     |   5 +
 include/lcd_console.h       |  33 ++++
 9 files changed, 537 insertions(+), 426 deletions(-)
 create mode 100644 common/ansi_console.c
 create mode 100644 include/ansi_console.h
Signature · 1 line
1.9.1
#1 lore raw

RFC 1/4 common: add ansi console base implementation

This code is based on ansi implementation in cfb_console. It is adopted to be used in lcd and cfb_console drivers.

---
 common/ansi_console.c  | 355 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/ansi_console.h |  39 ++++++
 2 files changed, 394 insertions(+)
 create mode 100644 common/ansi_console.c
 create mode 100644 include/ansi_console.h
common/ansi_console.c new file +355 -0
new file mode 100644
index 0000000..08adc1b
@@ -0,0 +1,355 @@
+#include <ansi_console.h>
+
+#define COL (*(console->console_col))
+#define ROW (*(console->console_row))
+
+#ifdef CONFIG_CONSOLE_ANSI_EXTENSION_ENABLED
+static void cursor_fix(struct ansi_console_t *console)
+{
+	if (ROW < 0)
+		ROW = 0;
+	if (ROW >= console->rows)
+		ROW = console->rows - 1;
+	if (COL < 0)
+		COL = 0;
+	if (COL >= console->cols)
+		COL = console->cols - 1;
+}
+
+static void cursor_set_position(struct ansi_console_t *console,
+		int row, int col)
+{
+	if (ROW != -1)
+		ROW = row;
+	if (COL != -1)
+		COL = col;
+	cursor_fix(console);
+}
+#endif /* CONFIG_CONSOLE_ANSI_EXTENSION_ENABLED */
+
+static inline void cursor_up(struct ansi_console_t *console, int n)
+{
+	ROW -= n;
+	if (ROW < 0)
+		ROW = 0;
+}
+
+static inline void cursor_down(struct ansi_console_t *console, int n)
+{
+	ROW += n;
+	if (ROW >= console->rows)
+		ROW = console->rows - 1;
+}
+
+static inline void cursor_left(struct ansi_console_t *console, int n)
+{
+	COL -= n;
+	if (COL < 0)
+		COL = 0;
+}
+
+static inline void cursor_right(struct ansi_console_t *console, int n)
+{
+	COL += n;
+	if (COL >= console->cols)
+		COL = console->cols - 1;
+}
+
+static inline void console_previous_line(struct ansi_console_t *console, int n)
+{
+	COL = 0;
+	ROW -= n;
+
+	/* Check if we need to scroll the terminal */
+	if (ROW < 0) {
+		if (console->scroll)
+			console->scroll(1 - ROW);
+	} else if (console->sync) {
+		console->sync();
+	}
+}
+
+static void console_new_line(struct ansi_console_t *console, int n)
+{
+	COL = 0;
+	ROW += n;
+
+	/* Check if we need to scroll the terminal */
+	if (ROW >= console->rows) {
+		if (console->scroll)
+			console->scroll(console->rows - ROW + 1);
+		ROW = console->rows - 1;
+	} else if (console->sync) {
+		console->sync();
+	}
+}
+
+static void console_caret_return(struct ansi_console_t *console)
+{
+	COL = 0;
+}
+
+static inline void console_back(struct ansi_console_t *console)
+{
+	if (--COL < 0) {
+		COL = console->cols - 1;
+		if (--ROW < 0)
+			ROW = 0;
+	}
+
+	console->putc_cr(COL, ROW, ' ');
+}
+
+
+static void console_putc(struct ansi_console_t *console, const char c)
+{
+	switch (c) {
+	case '\r':		/* back to first column */
+		console_caret_return(console);
+		break;
+
+	case '\n':		/* next line */
+		console_new_line(console, 1);
+		break;
+
+	case '\t':		/* tab 8 */
+		COL |= 0x0008;
+		COL &= ~0x0007;
+
+		if (COL >= console->cols)
+			console_new_line(console, 1);
+		break;
+
+	case '\b':		/* backspace */
+		console_back(console);
+		break;
+
+	case 7:		/* bell */
+		break;	/* ignored */
+
+	default:		/* draw the char */
+		console->putc_cr(COL, ROW, c);
+		COL++;
+
+		/* check for new line */
+		if (COL >= console->cols)
+			console_new_line(console, 1);
+	}
+}
+
+
+void ansi_putc(struct ansi_console_t *console, const char c)
+{
+#ifdef CONFIG_CONSOLE_ANSI_EXTENSION_ENABLED
+	int i;
+
+	if (c == 27) {
+		for (i = 0; i < console->ansi_buf_size; ++i)
+			console_putc(console, console->ansi_buf[i]);
+		console->ansi_buf[0] = 27;
+		console->ansi_buf_size = 1;
+		return;
+	}
+
+	if (console->ansi_buf_size > 0) {
+		/*
+		 * 0 - ESC
+		 * 1 - [
+		 * 2 - num1
+		 * 3 - ..
+		 * 4 - ;
+		 * 5 - num2
+		 * 6 - ..
+		 * - cchar
+		 */
+		int next = 0;
+
+		int flush = 0;
+		int fail = 0;
+
+		int num1 = 0;
+		int num2 = 0;
+		int cchar = 0;
+
+		console->ansi_buf[console->ansi_buf_size++] = c;
+
+		if (console->ansi_buf_size >= sizeof(console->ansi_buf))
+			fail = 1;
+
+		for (i = 0; i < console->ansi_buf_size; ++i) {
+			if (fail)
+				break;
+
+			switch (next) {
+			case 0:
+				if (console->ansi_buf[i] == 27)
+					next = 1;
+				else
+					fail = 1;
+				break;
+
+			case 1:
+				if (console->ansi_buf[i] == '[')
+					next = 2;
+				else
+					fail = 1;
+				break;
+
+			case 2:
+				if (console->ansi_buf[i] >= '0' &&
+				    console->ansi_buf[i] <= '9') {
+					num1 = console->ansi_buf[i]-'0';
+					next = 3;
+				} else if (console->ansi_buf[i] != '?') {
+					--i;
+					num1 = 1;
+					next = 4;
+				}
+				break;
+
+			case 3:
+				if (console->ansi_buf[i] >= '0' &&
+				    console->ansi_buf[i] <= '9') {
+					num1 *= 10;
+					num1 += console->ansi_buf[i]-'0';
+				} else {
+					--i;
+					next = 4;
+				}
+				break;
+
+			case 4:
+				if (console->ansi_buf[i] != ';') {
+					--i;
+					next = 7;
+				} else {
+					next = 5;
+				}
+				break;
+
+			case 5:
+				if (console->ansi_buf[i] >= '0' &&
+				    console->ansi_buf[i] <= '9') {
+					num2 = console->ansi_buf[i]-'0';
+					next = 6;
+				} else {
+					fail = 1;
+				}
+				break;
+
+			case 6:
+				if (console->ansi_buf[i] >= '0' &&
+				    console->ansi_buf[i] <= '9') {
+					num2 *= 10;
+					num2 += console->ansi_buf[i]-'0';
+				} else {
+					--i;
+					next = 7;
+				}
+				break;
+
+			case 7:
+				if ((console->ansi_buf[i] >= 'A' &&
+				     console->ansi_buf[i] <= 'H') ||
+				     console->ansi_buf[i] == 'J' ||
+				     console->ansi_buf[i] == 'K' ||
+				     console->ansi_buf[i] == 'h' ||
+				     console->ansi_buf[i] == 'l' ||
+				     console->ansi_buf[i] == 'm') {
+					cchar = console->ansi_buf[i];
+					flush = 1;
+				} else {
+					fail = 1;
+				}
+				break;
+			}
+		}
+
+		if (fail) {
+			for (i = 0; i < console->ansi_buf_size; ++i)
+				console_putc(console, console->ansi_buf[i]);
+			console->ansi_buf_size = 0;
+			return;
+		}
+
+		if (flush) {
+			if (!console->ansi_cursor_hidden &&
+			    console->cursor_enable)
+				console->cursor_enable(0);
+			console->ansi_buf_size = 0;
+			switch (cchar) {
+			case 'A':
+				/* move cursor num1 rows up */
+				cursor_up(console, num1);
+				break;
+			case 'B':
+				/* move cursor num1 rows down */
+				cursor_down(console, num1);
+				break;
+			case 'C':
+				/* move cursor num1 columns forward */
+				cursor_right(console, num1);
+				break;
+			case 'D':
+				/* move cursor num1 columns back */
+				cursor_left(console, num1);
+				break;
+			case 'E':
+				/* move cursor num1 rows up at begin of row */
+				console_previous_line(console, num1);
+				break;
+			case 'F':
+				/* move cursor num1 rows down at begin of row */
+				console_new_line(console, num1);
+				break;
+			case 'G':
+				/* move cursor to column num1 */
+				cursor_set_position(console, -1, num1-1);
+				break;
+			case 'H':
+				/* move cursor to row num1, column num2 */
+				cursor_set_position(console, num1-1, num2-1);
+				break;
+			case 'J':
+				/* clear console and move cursor to 0, 0 */
+				console->clear();
+				cursor_set_position(console, 0, 0);
+				break;
+			case 'K':
+				/* clear line */
+				if (num1 == 0)
+					console->clear_line(ROW, COL, -1);
+				else if (num1 == 1)
+					console->clear_line(ROW, 0, COL);
+				else
+					console->clear_line(ROW, 0, -1);
+				break;
+			case 'h':
+				console->ansi_cursor_hidden = 0;
+				break;
+			case 'l':
+				console->ansi_cursor_hidden = 1;
+				break;
+			case 'm':
+				if (num1 == 0) { /* reset swapped colors */
+					if (console->ansi_colors_need_revert &&
+					    console->swap_colors) {
+						console->swap_colors();
+						console->ansi_colors_need_revert = 0;
+					}
+				} else if (num1 == 7) { /* once swap colors */
+					if (!console->ansi_colors_need_revert &&
+					    console->swap_colors) {
+						console->swap_colors();
+						console->ansi_colors_need_revert = 1;
+					}
+				}
+				break;
+			}
+			if (!console->ansi_cursor_hidden && console->cursor_set)
+				console->cursor_set();
+		}
+	} else
+#endif /* CONFIG_ANSI_CONSOLE_EXTENSION_ENABLED */
+		console_putc(console, c);
+}
include/ansi_console.h new file +39 -0
new file mode 100644
index 0000000..b7ec094
@@ -0,0 +1,39 @@
+/*
+ * (C) Copyright 2012
+ * Pali Roh?r <[email protected]>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+/*
+ * ANSI terminal
+ */
+
+#include <common.h>
+
+struct ansi_console_t {
+	void (*putc_cr)(int col, int row, const char c);
+
+	void (*clear_line)(int line, int begin, int end);
+
+	void (*clear)(void);
+	void (*swap_colors)(void);
+
+	/* Optional */
+	void (*cursor_set)(void);
+	void (*cursor_enable)(int state);
+	void (*sync)(void);
+	void (*scroll)(int n);
+
+	int cols;
+	int rows;
+	int *console_col;
+	int *console_row;
+
+	char ansi_buf[10];
+	int ansi_buf_size;
+	int ansi_colors_need_revert;
+	int ansi_cursor_hidden;
+};
+
+void ansi_putc(struct ansi_console_t *console, const char c);
Signature · 1 line
1.9.1
#2 lore raw

RFC 1/4 common: add ansi console base implementation

Hi Andrey,

Quoted 2 lines · Andrey Danin

This code is based on ansi implementation in cfb_console. It is adopted to be used in lcd and cfb_console drivers.

Please describe what changes you have made (if any) from the original code.

Quoted 9 lines
---
  common/ansi_console.c  | 355 +++++++++++++++++++++++++++++++++++++++++++++++++
  include/ansi_console.h |  39 ++++++
  2 files changed, 394 insertions(+)

create mode 100644 common/ansi_console.c
create mode 100644 include/ansi_console.h

[...]

Quoted 5 lines
common/ansi_console.c new file +5 -0
+
+static void console_putc(struct ansi_console_t *console, const char c)
+{
+	switch (c) {
+	case '\r':		/* back to first column */

common/ansi_console.c line 117

Quoted 10 lines
common/ansi_console.c new file +10 -0
+		console_caret_return(console);
+		break;
+
+	case '\n':		/* next line */
+		console_new_line(console, 1);
+		break;
+
+	case '\t':		/* tab 8 */
+		COL |= 0x0008;
+		COL &= ~0x0007;

This seems buggy. Not only does this limit us to one tab, it can potentially move the cursor backwards. The LCD version of COL += 8 seems more correct.

Signature · 2 lines
Regards,
Nikita Kiryanov
#3 lore raw

RFC 2/4 video: cfb_console: use common ansi implementation

In reply to Andrey Danin
---
 drivers/video/Makefile      |   2 +-
 drivers/video/cfb_console.c | 381 ++++----------------------------------------
 2 files changed, 33 insertions(+), 350 deletions(-)
drivers/video/Makefile +1 -1
index 22a316b..0819ee9 100644
@@ -8,7 +8,7 @@
 obj-$(CONFIG_ATI_RADEON_FB) += ati_radeon_fb.o videomodes.o
 obj-$(CONFIG_ATMEL_HLCD) += atmel_hlcdfb.o
 obj-$(CONFIG_ATMEL_LCD) += atmel_lcdfb.o
-obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o
+obj-$(CONFIG_CFB_CONSOLE) += cfb_console.o ansi_console.o
 obj-$(CONFIG_EXYNOS_DP) += exynos_dp.o exynos_dp_lowlevel.o
 obj-$(CONFIG_EXYNOS_FB) += exynos_fb.o exynos_fimd.o
 obj-$(CONFIG_EXYNOS_MIPI_DSIM) += exynos_mipi_dsi.o exynos_mipi_dsi_common.o \
drivers/video/cfb_console.c +32 -349

450 lines not on this page · raw message

Signature · 1 line
1.9.1
#4 lore raw

RFC 3/4 lcd: use ansi console

In reply to Andrey Danin
---
 common/Makefile       |   2 +-
 common/lcd.c          |  24 ----------
 common/lcd_console.c  | 122 +++++++++++++++++++++++++++++---------------------
 include/lcd_console.h |  33 ++++++++++++++
 4 files changed, 105 insertions(+), 76 deletions(-)
common/Makefile +1 -1
index 7216a13..790d76d 100644
@@ -199,7 +199,7 @@ obj-$(CONFIG_I2C_EDID) += edid.o
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
 obj-y += splash.o
 obj-$(CONFIG_SPLASH_SOURCE) += splash_source.o
-obj-$(CONFIG_LCD) += lcd.o lcd_console.o
+obj-$(CONFIG_LCD) += lcd.o lcd_console.o ansi_console.o
 obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o
 obj-$(CONFIG_LYNXKDI) += lynxkdi.o
 obj-$(CONFIG_MENU) += menu.o
common/lcd.c +0 -24
index f33942c..d408e08 100644
@@ -47,11 +47,7 @@ DECLARE_GLOBAL_DATA_PTR;
 
 static int lcd_init(void *lcdbase);
 static void lcd_logo(void);
-static void lcd_setfgcolor(int color);
-static void lcd_setbgcolor(int color);
 
-static int lcd_color_fg;
-static int lcd_color_bg;
 int lcd_line_length;
 char lcd_is_enabled = 0;
 static void *lcd_base;			/* Start of framebuffer memory	*/
@@ -311,26 +307,6 @@ ulong lcd_setmem(ulong addr)
 	return addr;
 }
 
-static void lcd_setfgcolor(int color)
-{
-	lcd_color_fg = color;
-}
-
-int lcd_getfgcolor(void)
-{
-	return lcd_color_fg;
-}
-
-static void lcd_setbgcolor(int color)
-{
-	lcd_color_bg = color;
-}
-
-int lcd_getbgcolor(void)
-{
-	return lcd_color_bg;
-}
-
 #ifdef CONFIG_LCD_LOGO
 __weak void lcd_logo_set_cmap(void)
 {
common/lcd_console.c +71 -51

169 lines not on this page · raw message

include/lcd_console.h +33 -0
index 429214d..ea66ddb 100644
@@ -40,6 +40,39 @@ void lcd_set_col(short col);
 void lcd_set_row(short row);
 
 /**
+ * lcd_getbgcolor() - Get current background color value
+ *
+ * @return: Color value
+ */
+int lcd_getbgcolor(void);
+
+/**
+ * lcd_setbgcolor() - Set background color value
+ *
+ * @color: Color value
+ */
+void lcd_setbgcolor(int color);
+
+/**
+ * lcd_getfgcolor() - Get current foreground color value
+ *
+ * @return: Color value
+ */
+int lcd_getfgcolor(void);
+
+/**
+ * lcd_setfgcolor() - Set foreground color value
+ *
+ * @color: Color value
+ */
+void lcd_setfgcolor(int color);
+
+/**
+ * lcd_swap_colors() - Swap background and foreground color values
+ */
+void lcd_swap_colors(void);
+
+/**
  * lcd_position_cursor() - Position the cursor on the screen
  *
  * Position the cursor at the given coordinates on the screen.
Signature · 1 line
1.9.1
#5 lore raw

RFC 4/4 paz00: enable bootmenu

In reply to Andrey Danin
---
 include/configs/paz00.h | 5 +++++
 1 file changed, 5 insertions(+)
include/configs/paz00.h +5 -0
index 284419f..17063d0 100644
@@ -64,6 +64,11 @@
 #define CONFIG_SYS_WHITE_ON_BLACK
 #define CONFIG_CONSOLE_SCROLL_LINES	10
 
+#define CONFIG_CONSOLE_ANSI_EXTENSION_ENABLED
+#define CONFIG_CMD_BOOTMENU
+#define CONFIG_MENU
+#define CONFIG_AUTOBOOT_KEYED
+
 #include "tegra-common-post.h"
 
 #endif /* __CONFIG_H */
Signature · 1 line
1.9.1
#6 lore raw

RFC 4/4 paz00: enable bootmenu

Quoted 1 line · Andrey Danin

Some explanation of what these new options do might be nice; the features/benefits they enable.

Are any of the options generally useful? If so, should they be enabled in tegra-common*.h?

You also didn't Cc Tom Warren (Tegra maintainer) so he likely won't see the emails and hence won't apply this patch.

#7 lore raw

RFC 4/4 paz00: enable bootmenu

Quoted 11 lines · Stephen Warren
Quoted 1 line · Andrey Danin

Some explanation of what these new options do might be nice; the features/benefits they enable.

Are any of the options generally useful? If so, should they be enabled in tegra-common*.h?

You also didn't Cc Tom Warren (Tegra maintainer) so he likely won't see the emails and hence won't apply this patch.

Thanks for the reply Stephen! I will add Tom Warren to Cc next time.

Bootmenu allows to create user friendly menu to choose different boot options. For example next 3 lines in boot config
---
setenv bootmenu_0 "Boot kernel = setenv bootargs ..." setenv bootmenu_1 "Boot recovery = setenv bootargs ..." bootmenu 20
---
will generate bootmenu with 3 options:
---
*** U-Boot Boot Menu ***

Boot kernel
Boot recovery
U-Boot console

Press UP/DOWN to move, ENTER to select
---

More information about bootmenu is available at doc/README.bootmenu

I was focused on console part for this RFC. This patch is for testing purposes. I will use tegra config instead of paz00 next time to allow other users of tegra devices to try changes.

#8 lore raw

RFC 4/4 paz00: enable bootmenu

Quoted 32 lines · Andrey Danin
Quoted 11 lines · Stephen Warren
Quoted 1 line · Andrey Danin

Some explanation of what these new options do might be nice; the features/benefits they enable.

Are any of the options generally useful? If so, should they be enabled in tegra-common*.h?

You also didn't Cc Tom Warren (Tegra maintainer) so he likely won't see the emails and hence won't apply this patch.

Thanks for the reply Stephen! I will add Tom Warren to Cc next time.

Bootmenu allows to create user friendly menu to choose different boot options. For example next 3 lines in boot config
---
setenv bootmenu_0 "Boot kernel = setenv bootargs ..." setenv bootmenu_1 "Boot recovery = setenv bootargs ..." bootmenu 20
---
will generate bootmenu with 3 options:
---
*** U-Boot Boot Menu ***

Boot kernel
Boot recovery
U-Boot console

Quoted 4 lines

Press UP/DOWN to move, ENTER to select
---

More information about bootmenu is available at doc/README.bootmenu

OK. Might it be better to use an extlinux.conf file? It already has support for displaying a menu for all the options in the file, and allowing the user to select which to boot. (Although I suspect the extlinux.conf menu could be prettied up some). I suppose your example above are more than just selecting which kernel to boot though, so perhaps extlinux.conf wouldn't work.

Quoted 3 lines

I was focused on console part for this RFC. This patch is for testing purposes. I will use tegra config instead of paz00 next time to allow other users of tegra devices to try changes.

#9 lore raw

cover ansi console support for lcd driver

In reply to Andrey Danin

Hi Andrey,

Quoted 11 lines · Andrey Danin

Main reason for this patches is ability to use bootmenu on devices that use lcd driver (like Toshiba AC100).

Lcd driver doesn't have ansi support while cfb_console does. Ansi related code was moved from cfb_console to separate place. Then this code was used in both cfb_console and lcd driver.

There are other duplicated code between cfb_console and lcd.

I'm not very experienced with video subsystem. Maybe there is a better/more proper way to add ansi support for Toshiba AC100.

I think you have the right idea. As you pointed out, there's a lot of duplication between lcd code and video code which should be eliminated, and this is a step in that direction.

Hannes Petermaier is currently also working on lcd console functionality (Introduce lcd_console rotation), and I recommend that you sync with his patches (V2 has just been posted).

Quoted 22 lines

---

Andrey Danin (4):
common: add ansi console base implementation
video: cfb_console: use common ansi implementation
lcd: use ansi console
paz00: enable bootmenu

  common/Makefile             |   2 +-
  common/ansi_console.c       | 355 +++++++++++++++++++++++++++++++++++++++++
  common/lcd.c                |  24 ---
  common/lcd_console.c        | 122 ++++++++------
  drivers/video/Makefile      |   2 +-
  drivers/video/cfb_console.c | 381 ++++----------------------------------------
  include/ansi_console.h      |  39 +++++
  include/configs/paz00.h     |   5 +
  include/lcd_console.h       |  33 ++++
  9 files changed, 537 insertions(+), 426 deletions(-)

create mode 100644 common/ansi_console.c
create mode 100644 include/ansi_console.h

Signature · 2 lines
Regards,
Nikita Kiryanov