diff mbox

[U-Boot,v2,4/4] common/lcd_console: introduce display/framebuffer rotation

Message ID 1426664243-30998-5-git-send-email-oe5hpm@oevsv.at
State Superseded
Delegated to: Anatolij Gustschin
Headers show

Commit Message

Hannes Schmelzer March 18, 2015, 7:37 a.m. UTC
From: Hannes Petermaier <hannes.petermaier@br-automation.com>

Sometimes, for example if the display is mounted in portrait mode or even if it
mounted landscape but rotated by 180 degrees, we need to rotate our content of
the display respectively the framebuffer, so that user can read the messages
who are printed out.

For this we introduce the feature called "CONFIG_LCD_ROTATION", this may be
defined in the board-configuration if needed. After this the lcd_console will
be initialized with a given rotation from "vl_rot" out of "vidinfo_t" which is
provided by the board specific code.

If CONFIG_LCD_ROTATION is not defined, the console will be initialized with
0 degrees rotation.

Signed-off-by: Hannes Petermaier <hannes.petermaier@br-automation.com>
Signed-off-by: Hannes Petermaier <oe5hpm@oevsv.at>
---

Changes in v2:
- cleanup README text for feature
- don't make code cleanups (lcd_console.c) within this patch
- remove (unnary) comment in lcd_console.h
- update year to 2015 within copyright in lcd_console.c
- move rotation related code into separate file lcd_console_rotation.c
- rework rotation code
- change meaning of vl_rot to match fbcon=rotate:<n> from the linux-kernel

 README                        |   22 +++++
 common/Makefile               |    1 +
 common/lcd.c                  |   15 ++-
 common/lcd_console.c          |  168 ++++++++++++++++++--------------
 common/lcd_console_rotation.c |  216 +++++++++++++++++++++++++++++++++++++++++
 include/lcd.h                 |    1 +
 include/lcd_console.h         |   18 +++-
 7 files changed, 357 insertions(+), 84 deletions(-)
 create mode 100644 common/lcd_console_rotation.c

Comments

Igor Grinberg March 18, 2015, 12:56 p.m. UTC | #1
Hi Hannes,

On 03/18/15 09:37, Hannes Petermaier wrote:
> From: Hannes Petermaier <hannes.petermaier@br-automation.com>
> 
> Sometimes, for example if the display is mounted in portrait mode or even if it
> mounted landscape but rotated by 180 degrees, we need to rotate our content of
> the display respectively the framebuffer, so that user can read the messages
> who are printed out.
> 
> For this we introduce the feature called "CONFIG_LCD_ROTATION", this may be
> defined in the board-configuration if needed. After this the lcd_console will
> be initialized with a given rotation from "vl_rot" out of "vidinfo_t" which is
> provided by the board specific code.
> 
> If CONFIG_LCD_ROTATION is not defined, the console will be initialized with
> 0 degrees rotation.
> 
> Signed-off-by: Hannes Petermaier <hannes.petermaier@br-automation.com>
> Signed-off-by: Hannes Petermaier <oe5hpm@oevsv.at>

[...]

> diff --git a/common/lcd_console.c b/common/lcd_console.c
> index cac77be..14fd890 100644
> --- a/common/lcd_console.c
> +++ b/common/lcd_console.c

[...]

> @@ -10,26 +11,16 @@
>  #include <lcd.h>
>  #include <video_font.h>		/* Get font data, width and height */
>  
> -#define CONSOLE_ROW_SIZE	(VIDEO_FONT_HEIGHT * lcd_line_length)
> -#define CONSOLE_ROW_FIRST	cons.lcd_address
> -#define CONSOLE_SIZE		(CONSOLE_ROW_SIZE * cons.rows)
> +#if LCD_BPP == LCD_COLOR16
> +	#define fbptr_t ushort
> +#elif LCD_BPP == LCD_COLOR32
> +	#define fbptr_t u32
> +#else
> +	#define fbptr_t uchar
> +#endif
>  
> -struct console_t {
> -	short curr_col, curr_row;
> -	short cols, rows;
> -	void *lcd_address;
> -};
>  static struct console_t cons;
>  
> -void lcd_init_console(void *address, int rows, int cols)
> -{
> -	memset(&cons, 0, sizeof(cons));
> -	cons.cols = cols;
> -	cons.rows = rows;
> -	cons.lcd_address = address;
> -
> -}
> -
>  void lcd_set_col(short col)
>  {
>  	cons.curr_col = col;
> @@ -56,61 +47,50 @@ int lcd_get_screen_columns(void)
>  	return cons.cols;
>  }
>  
> -static void lcd_putc_xy(ushort x, ushort y, char c)
> +static void lcd_putc_xy0(struct console_t *pcons, ushort x, ushort y, char c)
>  {
> -	uchar *dest;
> -	ushort row;
>  	int fg_color = lcd_getfgcolor();
>  	int bg_color = lcd_getbgcolor();
> -	int i;
> -
> -	dest = (uchar *)(cons.lcd_address +
> -			 y * lcd_line_length + x * NBITS(LCD_BPP) / 8);
> -
> -	for (row = 0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) {
> -#if LCD_BPP == LCD_COLOR16
> -		ushort *d = (ushort *)dest;
> -#elif LCD_BPP == LCD_COLOR32
> -		u32 *d = (u32 *)dest;
> -#else
> -		uchar *d = dest;
> -#endif
> -		uchar bits;
> -		bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
> -
> -		for (i = 0; i < 8; ++i) {
> -			*d++ = (bits & 0x80) ? fg_color : bg_color;
> +	int i, row;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  y * pcons->lcdsizex +
> +				  x;
> +
> +	for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
> +		uchar bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
> +		for (i = 0; i < VIDEO_FONT_WIDTH; ++i) {
> +			*dst++ = (bits & 0x80) ? fg_color : bg_color;
>  			bits <<= 1;
>  		}
> +		dst += (pcons->lcdsizex - VIDEO_FONT_WIDTH);
>  	}
>  }
>  
> -static void console_scrollup(void)
> +static inline void console_setrow0(struct console_t *pcons, u32 row, int clr)
>  {
> -	const int rows = CONFIG_CONSOLE_SCROLL_LINES;
> -	int bg_color = lcd_getbgcolor();
> +	int i;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  row * VIDEO_FONT_HEIGHT *
> +				  pcons->lcdsizex;
>  
> -	/* Copy up rows ignoring those that will be overwritten */
> -	memcpy(CONSOLE_ROW_FIRST,
> -	       cons.lcd_address + CONSOLE_ROW_SIZE * rows,
> -	       CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows);
> +	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
> +		*dst++ = clr;
> +}
>  
> -	/* Clear the last rows */
> -#if (LCD_BPP != LCD_COLOR32)
> -	memset(lcd_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows,
> -	       bg_color, CONSOLE_ROW_SIZE * rows);
> -#else
> -	u32 *ppix = cons.lcd_address +
> -		    CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows;
> -	u32 i;
> -	for (i = 0;
> -	    i < (CONSOLE_ROW_SIZE * rows) / NBYTES(panel_info.vl_bpix);
> -	    i++) {
> -		*ppix++ = bg_color;
> -	}
> -#endif
> -	lcd_sync();
> -	cons.curr_row -= rows;
> +static inline void console_moverow0(struct console_t *pcons,
> +				    u32 rowdst, u32 rowsrc)
> +{
> +	int i;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  rowdst * VIDEO_FONT_HEIGHT *
> +				  pcons->lcdsizex;
> +
> +	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
> +				  rowsrc * VIDEO_FONT_HEIGHT *
> +				  pcons->lcdsizex;
> +
> +	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
> +		*dst++ = *src++;
>  }
>  
>  static inline void console_back(void)
> @@ -121,19 +101,65 @@ static inline void console_back(void)
>  			cons.curr_row = 0;
>  	}
>  
> -	lcd_putc_xy(cons.curr_col * VIDEO_FONT_WIDTH,
> -		    cons.curr_row * VIDEO_FONT_HEIGHT, ' ');
> +	cons.fp_putc_xy(&cons,
> +			cons.curr_col * VIDEO_FONT_WIDTH,
> +			cons.curr_row * VIDEO_FONT_HEIGHT, ' ');
>  }
>  
>  static inline void console_newline(void)
>  {
> +	const int rows = CONFIG_CONSOLE_SCROLL_LINES;
> +	int bg_color = lcd_getbgcolor();
> +	int i;
> +
>  	cons.curr_col = 0;
>  
>  	/* Check if we need to scroll the terminal */
> -	if (++cons.curr_row >= cons.rows)
> -		console_scrollup();
> -	else
> -		lcd_sync();
> +	if (++cons.curr_row >= cons.rows) {
> +		for (i = 0; i < cons.rows-rows; i++)
> +			cons.fp_console_moverow(&cons, i, i+rows);
> +		for (i = 0; i < rows; i++)
> +			cons.fp_console_setrow(&cons, cons.rows-i-1, bg_color);
> +		cons.curr_row -= rows;
> +	}
> +	lcd_sync();
> +}
> +
> +static void console_calc_rowcol(struct console_t *pcons)
> +{
> +	pcons->cols = pcons->lcdsizex / VIDEO_FONT_WIDTH;
> +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
> +	pcons->rows = (pcons->lcdsizey - BMP_LOGO_HEIGHT);
> +	pcons->rows /= VIDEO_FONT_HEIGHT;
> +#else
> +	pcons->rows = pcons->lcdsizey / VIDEO_FONT_HEIGHT;
> +#endif
> +}
> +
> +void __weak lcd_init_console_rot(struct console_t *pcons)
> +{
> +	return;
> +}
> +
> +void lcd_init_console(void *address, int vl_cols, int vl_rows, int vl_rot)
> +{
> +	memset(&cons, 0, sizeof(cons));
> +	cons.lcd_address = address;
> +
> +	cons.lcdsizex = vl_cols;
> +	cons.lcdsizey = vl_rows;
> +	cons.lcdrot = vl_rot;
> +
> +	cons.fp_putc_xy = &lcd_putc_xy0;
> +	cons.fp_console_moverow = &console_moverow0;
> +	cons.fp_console_setrow = &console_setrow0;
> +	console_calc_rowcol(&cons);

I think the above four lines is exactly what should be placed in the
__weak variant of lcd_init_console_rot() function (the one just above
this one).

> +
> +	lcd_init_console_rot(&cons);
> +
> +	debug("lcd_console: have %d/%d col/rws on scr %dx%d (%d deg rotated)\n",
> +	      cons.cols, cons.rows, cons.lcdsizex, cons.lcdsizey, vl_rot);
> +

no need for the empty line here.

>  }
>  
>  void lcd_putc(const char c)
> @@ -165,8 +191,9 @@ void lcd_putc(const char c)
>  
>  		return;
>  	default:
> -		lcd_putc_xy(cons.curr_col * VIDEO_FONT_WIDTH,
> -			    cons.curr_row * VIDEO_FONT_HEIGHT, c);
> +		cons.fp_putc_xy(&cons,
> +				cons.curr_col * VIDEO_FONT_WIDTH,
> +				cons.curr_row * VIDEO_FONT_HEIGHT, c);
>  		if (++cons.curr_col >= cons.cols)
>  			console_newline();
>  	}
> @@ -235,4 +262,3 @@ U_BOOT_CMD(
>  	"print string on lcd-framebuffer",
>  	"    <string>"
>  );
> -
> diff --git a/common/lcd_console_rotation.c b/common/lcd_console_rotation.c
> new file mode 100644
> index 0000000..3b6bb4f
> --- /dev/null
> +++ b/common/lcd_console_rotation.c
> @@ -0,0 +1,216 @@
> +/*
> + * (C) Copyright 2015
> + * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <lcd.h>
> +#include <video_font.h>		/* Get font data, width and height */
> +
> +#if LCD_BPP == LCD_COLOR16
> +	#define fbptr_t ushort
> +#elif LCD_BPP == LCD_COLOR32
> +	#define fbptr_t u32
> +#else
> +	#define fbptr_t uchar
> +#endif

That is a duplication of the code in lcd_console.c.
If we can get rid of these size games, probably we should have in the lcd.h,
or lcd_console.h, or ...

> +
> +static void lcd_putc_xy90(struct console_t *pcons, ushort x, ushort y, char c)
> +{
> +	int fg_color = lcd_getfgcolor();
> +	int bg_color = lcd_getbgcolor();
> +	int col, i;
> +
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  (x+1) * pcons->lcdsizex -
> +				  y;
> +
> +	uchar msk = 0x80;
> +	uchar *pfont = video_fontdata + c * VIDEO_FONT_HEIGHT;
> +	for (col = 0; col < VIDEO_FONT_WIDTH; ++col) {
> +		for (i = 0; i < VIDEO_FONT_HEIGHT; ++i)
> +			*dst-- = (*(pfont + i) & msk) ? fg_color : bg_color;
> +		msk >>= 1;
> +		dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
> +	}
> +}
> +
> +static inline void console_setrow90(struct console_t *pcons, u32 row, int clr)
> +{
> +	int i, j;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  pcons->lcdsizex -
> +				  row*VIDEO_FONT_HEIGHT+1;
> +
> +	for (j = 0; j < pcons->lcdsizey; j++) {
> +		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
> +			*dst-- = clr;
> +		dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
> +	}
> +}
> +
> +static inline void console_moverow90(struct console_t *pcons,
> +				      u32 rowdst, u32 rowsrc)
> +{
> +	int i, j;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  pcons->lcdsizex -
> +				  (rowdst*VIDEO_FONT_HEIGHT+1);
> +
> +	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
> +				  pcons->lcdsizex -
> +				  (rowsrc*VIDEO_FONT_HEIGHT+1);
> +
> +	for (j = 0; j < pcons->lcdsizey; j++) {
> +		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
> +			*dst-- = *src--;
> +		src += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
> +		dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
> +	}
> +}
> +static void lcd_putc_xy180(struct console_t *pcons, ushort x, ushort y, char c)
> +{
> +	int fg_color = lcd_getfgcolor();
> +	int bg_color = lcd_getbgcolor();
> +	int i, row;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  pcons->lcdsizex +
> +				  pcons->lcdsizey * pcons->lcdsizex -
> +				  y * pcons->lcdsizex -
> +				  (x+1);
> +
> +	for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
> +		uchar bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
> +
> +		for (i = 0; i < VIDEO_FONT_WIDTH; ++i) {
> +			*dst-- = (bits & 0x80) ? fg_color : bg_color;
> +			bits <<= 1;
> +		}
> +		dst -= (pcons->lcdsizex - VIDEO_FONT_WIDTH);
> +	}
> +}
> +
> +static inline void console_setrow180(struct console_t *pcons, u32 row, int clr)
> +{
> +	int i;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  (pcons->rows-row-1) * VIDEO_FONT_HEIGHT *
> +				  pcons->lcdsizex;
> +
> +	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
> +		*dst++ = clr;
> +}
> +
> +static inline void console_moverow180(struct console_t *pcons,
> +				      u32 rowdst, u32 rowsrc)
> +{
> +	int i;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  (pcons->rows-rowdst-1) * VIDEO_FONT_HEIGHT *
> +				  pcons->lcdsizex;
> +
> +	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
> +				  (pcons->rows-rowsrc-1) * VIDEO_FONT_HEIGHT *
> +				  pcons->lcdsizex;
> +
> +	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
> +		*dst++ = *src++;
> +}
> +
> +static void lcd_putc_xy270(struct console_t *pcons, ushort x, ushort y, char c)
> +{
> +	int fg_color = lcd_getfgcolor();
> +	int bg_color = lcd_getbgcolor();
> +	int i, col;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  pcons->lcdsizey * pcons->lcdsizex -
> +				  (x+1) * pcons->lcdsizex +
> +				  y;
> +
> +	uchar msk = 0x80;
> +	uchar *pfont = video_fontdata + c * VIDEO_FONT_HEIGHT;
> +	for (col = 0; col < VIDEO_FONT_WIDTH; ++col) {
> +		for (i = 0; i < VIDEO_FONT_HEIGHT; ++i)
> +			*dst++ = (*(pfont + i) & msk) ? fg_color : bg_color;
> +		msk >>= 1;
> +		dst -= (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
> +	}
> +}
> +
> +static inline void console_setrow270(struct console_t *pcons, u32 row, int clr)
> +{
> +	int i, j;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  row*VIDEO_FONT_HEIGHT;
> +
> +	for (j = 0; j < pcons->lcdsizey; j++) {
> +		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
> +			*dst++ = clr;
> +		dst += (pcons->lcdsizex - VIDEO_FONT_HEIGHT);
> +	}
> +}
> +
> +static inline void console_moverow270(struct console_t *pcons,
> +				     u32 rowdst, u32 rowsrc)
> +{
> +	int i, j;
> +	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
> +				  rowdst*VIDEO_FONT_HEIGHT;
> +
> +	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
> +				  rowsrc*VIDEO_FONT_HEIGHT;
> +
> +	for (j = 0; j < pcons->lcdsizey; j++) {
> +		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
> +			*dst++ = *src++;
> +		src += (pcons->lcdsizex - VIDEO_FONT_HEIGHT);
> +		dst += (pcons->lcdsizex - VIDEO_FONT_HEIGHT);
> +	}
> +}
> +
> +static void console_calc_rowcol_rot(struct console_t *pcons)
> +{
> +	u32 cols, rows;
> +
> +	if (pcons->lcdrot == 1 || pcons->lcdrot == 3) {
> +		cols = pcons->lcdsizey;
> +		rows = pcons->lcdsizex;
> +	} else {
> +		cols = pcons->lcdsizex;
> +		rows = pcons->lcdsizey;
> +	}
> +
> +	pcons->cols = cols / VIDEO_FONT_WIDTH;
> +#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
> +	pcons->rows = (rows - BMP_LOGO_HEIGHT);
> +	pcons->rows /= VIDEO_FONT_HEIGHT;
> +#else
> +	pcons->rows = rows / VIDEO_FONT_HEIGHT;
> +#endif
> +}
> +
> +void lcd_init_console_rot(struct console_t *pcons)
> +{
> +	if (pcons->lcdrot == 0) {
> +		return;
> +	} else if (pcons->lcdrot == 1) {
> +		pcons->fp_putc_xy = &lcd_putc_xy90;
> +		pcons->fp_console_moverow = &console_moverow90;
> +		pcons->fp_console_setrow = &console_setrow90;
> +	} else if (pcons->lcdrot == 2) {
> +		pcons->fp_putc_xy = &lcd_putc_xy180;
> +		pcons->fp_console_moverow = &console_moverow180;
> +		pcons->fp_console_setrow = &console_setrow180;
> +	} else if (pcons->lcdrot == 3) {
> +		pcons->fp_putc_xy = &lcd_putc_xy270;
> +		pcons->fp_console_moverow = &console_moverow270;
> +		pcons->fp_console_setrow = &console_setrow270;
> +	} else {
> +		puts("lcd_init_console_rot: invalid framebuffer rotation!\n");

How about
printf("%s: invalid framebuffer rotation!\n", __func__);
?

> +		puts("                      using def. 0 degree rotation.\n");
> +		return;
> +	}
> +	console_calc_rowcol_rot(pcons);
> +}
> diff --git a/include/lcd.h b/include/lcd.h
> index f049fd3..1329280 100644
> --- a/include/lcd.h
> +++ b/include/lcd.h
> @@ -51,6 +51,7 @@ void lcd_set_flush_dcache(int flush);
>  typedef struct vidinfo {
>  	ushort	vl_col;		/* Number of columns (i.e. 160) */
>  	ushort	vl_row;		/* Number of rows (i.e. 100) */
> +	ushort	vl_rot;		/* Rotation of Display (0, 1, 2, 3) */
>  	u_char	vl_bpix;	/* Bits per pixel, 0 = 1 */
>  	ushort	*cmap;		/* Pointer to the colormap */
>  	void	*priv;		/* Pointer to driver-specific data */
> diff --git a/include/lcd_console.h b/include/lcd_console.h
> index 429214d..6612a38 100644
> --- a/include/lcd_console.h
> +++ b/include/lcd_console.h
> @@ -9,6 +9,16 @@
>  #define CONFIG_CONSOLE_SCROLL_LINES 1
>  #endif
>  
> +struct console_t {
> +	short curr_col, curr_row;
> +	short cols, rows;
> +	void *lcd_address;
> +	u32 lcdsizex, lcdsizey, lcdrot;
> +	void (*fp_putc_xy)(struct console_t *pcons, ushort x, ushort y, char c);
> +	void (*fp_console_moverow)(struct console_t *pcons,
> +				   u32 rowdst, u32 rowsrc);
> +	void (*fp_console_setrow)(struct console_t *pcons, u32 row, int clr);
> +};
>  /**
>   * lcd_init_console() - Initialize lcd console parameters
>   *
> @@ -16,11 +26,11 @@
>   * console has.
>   *
>   * @address: Console base address
> - * @rows: Number of rows in the console
> - * @cols: Number of columns in the console
> + * @vl_rows: Number of rows in the console
> + * @vl_cols: Number of columns in the console
> + * @vl_rot: Rotation of display in degree (0 - 90 - 180 - 270) counterlockwise
>   */
> -void lcd_init_console(void *address, int rows, int cols);
> -
> +void lcd_init_console(void *address, int vl_cols, int vl_rows, int vl_rot);
>  /**
>   * lcd_set_col() - Set the number of the current lcd console column
>   *
>
Hannes Schmelzer March 18, 2015, 2:59 p.m. UTC | #2
"U-Boot" <u-boot-bounces@lists.denx.de> schrieb am 18.03.2015 13:56:49:

> 
> Hi Hannes,
Hi Igor,
thanks for response - we come closer to the final solution :-)

> 
> > +}
> > +
> > +void __weak lcd_init_console_rot(struct console_t *pcons)
> > +{
> > +   return;
> > +}
> > +
> > +void lcd_init_console(void *address, int vl_cols, int vl_rows, int 
vl_rot)
> > +{
> > +   memset(&cons, 0, sizeof(cons));
> > +   cons.lcd_address = address;
> > +
> > +   cons.lcdsizex = vl_cols;
> > +   cons.lcdsizey = vl_rows;
> > +   cons.lcdrot = vl_rot;
> > +
> > +   cons.fp_putc_xy = &lcd_putc_xy0;
> > +   cons.fp_console_moverow = &console_moverow0;
> > +   cons.fp_console_setrow = &console_setrow0;
> > +   console_calc_rowcol(&cons);
> 
> I think the above four lines is exactly what should be placed in the
> __weak variant of lcd_init_console_rot() function (the one just above
> this one).
I think not so.
If the lcd_console_rotation.c is compiled in, the __weak function isn't 
called anymore.
And if user wants to have 0 degree rotation, the init-function from 
lcd_console_rotation.c doesn't anything.
Therefore it is necessary to initialize these pointers here.

> > +
> > +   lcd_init_console_rot(&cons);
> > +
> > +   debug("lcd_console: have %d/%d col/rws on scr %dx%d (%d deg 
rotated)\n",
> > +         cons.cols, cons.rows, cons.lcdsizex, cons.lcdsizey, vl_rot);
> > +
> 
> no need for the empty line here.
Will be changed in v3.

> > +
> > +#include <common.h>
> > +#include <lcd.h>
> > +#include <video_font.h>      /* Get font data, width and height */
> > +
> > +#if LCD_BPP == LCD_COLOR16
> > +   #define fbptr_t ushort
> > +#elif LCD_BPP == LCD_COLOR32
> > +   #define fbptr_t u32
> > +#else
> > +   #define fbptr_t uchar
> > +#endif
> 
> That is a duplication of the code in lcd_console.c.
> If we can get rid of these size games, probably we should have in the 
lcd.h,
> or lcd_console.h, or ...
It might be possible to move this into lcd.h, in every case it is 
necesarry that common.h
is included before - from here the information about LCD_BPP is coming.
I will have a 2nd look to that to avoid this duplication.

> > +
> > +void lcd_init_console_rot(struct console_t *pcons)
> > +{
> > +   if (pcons->lcdrot == 0) {
> > +      return;
> > +   } else if (pcons->lcdrot == 1) {
> > +      pcons->fp_putc_xy = &lcd_putc_xy90;
> > +      pcons->fp_console_moverow = &console_moverow90;
> > +      pcons->fp_console_setrow = &console_setrow90;
> > +   } else if (pcons->lcdrot == 2) {
> > +      pcons->fp_putc_xy = &lcd_putc_xy180;
> > +      pcons->fp_console_moverow = &console_moverow180;
> > +      pcons->fp_console_setrow = &console_setrow180;
> > +   } else if (pcons->lcdrot == 3) {
> > +      pcons->fp_putc_xy = &lcd_putc_xy270;
> > +      pcons->fp_console_moverow = &console_moverow270;
> > +      pcons->fp_console_setrow = &console_setrow270;
> > +   } else {
> > +      puts("lcd_init_console_rot: invalid framebuffer rotation!\n");
> 
> How about
> printf("%s: invalid framebuffer rotation!\n", __func__);
> ?
Okay, i will change that.
Sometime ago, somebody told me on the mailing list that i should prefer 
the puts function if i don't have to print out some values.
If we want to use the printf we can also printout the given rotation 
pcons->lcdrot.

> -- 
> Regards,
> Igor.
best regards,
Hannes
Igor Grinberg March 18, 2015, 3:27 p.m. UTC | #3
On 03/18/15 16:59, Hannes Petermaier wrote:
> "U-Boot" <u-boot-bounces@lists.denx.de> schrieb am 18.03.2015 13:56:49:
> 
>>
>> Hi Hannes,
> Hi Igor,
> thanks for response - we come closer to the final solution :-)

Indeed :-)

> 
>>
>>> +}
>>> +
>>> +void __weak lcd_init_console_rot(struct console_t *pcons)
>>> +{
>>> +   return;
>>> +}
>>> +
>>> +void lcd_init_console(void *address, int vl_cols, int vl_rows, int 
> vl_rot)
>>> +{
>>> +   memset(&cons, 0, sizeof(cons));
>>> +   cons.lcd_address = address;
>>> +
>>> +   cons.lcdsizex = vl_cols;
>>> +   cons.lcdsizey = vl_rows;
>>> +   cons.lcdrot = vl_rot;
>>> +
>>> +   cons.fp_putc_xy = &lcd_putc_xy0;
>>> +   cons.fp_console_moverow = &console_moverow0;
>>> +   cons.fp_console_setrow = &console_setrow0;
>>> +   console_calc_rowcol(&cons);
>>
>> I think the above four lines is exactly what should be placed in the
>> __weak variant of lcd_init_console_rot() function (the one just above
>> this one).
> I think not so.
> If the lcd_console_rotation.c is compiled in, the __weak function isn't 
> called anymore.
> And if user wants to have 0 degree rotation, the init-function from 
> lcd_console_rotation.c doesn't anything.
> Therefore it is necessary to initialize these pointers here.

Right. Forgot about the 0 case...

> 
>>> +
>>> +   lcd_init_console_rot(&cons);
>>> +
>>> +   debug("lcd_console: have %d/%d col/rws on scr %dx%d (%d deg 
> rotated)\n",
>>> +         cons.cols, cons.rows, cons.lcdsizex, cons.lcdsizey, vl_rot);
>>> +
>>
>> no need for the empty line here.
> Will be changed in v3.
> 
>>> +
>>> +#include <common.h>
>>> +#include <lcd.h>
>>> +#include <video_font.h>      /* Get font data, width and height */
>>> +
>>> +#if LCD_BPP == LCD_COLOR16
>>> +   #define fbptr_t ushort
>>> +#elif LCD_BPP == LCD_COLOR32
>>> +   #define fbptr_t u32
>>> +#else
>>> +   #define fbptr_t uchar
>>> +#endif
>>
>> That is a duplication of the code in lcd_console.c.
>> If we can get rid of these size games, probably we should have in the 
> lcd.h,
>> or lcd_console.h, or ...
> It might be possible to move this into lcd.h, in every case it is 
> necesarry that common.h
> is included before - from here the information about LCD_BPP is coming.
> I will have a 2nd look to that to avoid this duplication.
> 
>>> +
>>> +void lcd_init_console_rot(struct console_t *pcons)
>>> +{
>>> +   if (pcons->lcdrot == 0) {
>>> +      return;
>>> +   } else if (pcons->lcdrot == 1) {
>>> +      pcons->fp_putc_xy = &lcd_putc_xy90;
>>> +      pcons->fp_console_moverow = &console_moverow90;
>>> +      pcons->fp_console_setrow = &console_setrow90;
>>> +   } else if (pcons->lcdrot == 2) {
>>> +      pcons->fp_putc_xy = &lcd_putc_xy180;
>>> +      pcons->fp_console_moverow = &console_moverow180;
>>> +      pcons->fp_console_setrow = &console_setrow180;
>>> +   } else if (pcons->lcdrot == 3) {
>>> +      pcons->fp_putc_xy = &lcd_putc_xy270;
>>> +      pcons->fp_console_moverow = &console_moverow270;
>>> +      pcons->fp_console_setrow = &console_setrow270;
>>> +   } else {
>>> +      puts("lcd_init_console_rot: invalid framebuffer rotation!\n");
>>
>> How about
>> printf("%s: invalid framebuffer rotation!\n", __func__);
>> ?
> Okay, i will change that.
> Sometime ago, somebody told me on the mailing list that i should prefer 
> the puts function if i don't have to print out some values.

Well, this is no longer true as we have discussed this during the last
U-Boot summit.

> If we want to use the printf we can also printout the given rotation 
> pcons->lcdrot.

Good.
diff mbox

Patch

diff --git a/README b/README
index b0124d6..c649de1 100644
--- a/README
+++ b/README
@@ -1947,6 +1947,28 @@  CBFS (Coreboot Filesystem) support
 		the console jump but can help speed up operation when scrolling
 		is slow.
 
+		CONFIG_LCD_ROTATION
+
+		Sometimes, for example if the display is mounted in portrait
+		mode or even if it mounted landscape but rotated by 180degree,
+		we need to rotate our content of the display respectively the
+		framebuffer, so that user can read the messages who are printed
+		out.
+		For this we introduce the feature called "CONFIG_LCD_ROTATION",
+		this may be defined in the board-configuration if needed. After
+		this the lcd_console will be initialized with a given rotation
+		from "vl_rot" out of "vidinfo_t" which is provided by the board
+		specific code.
+		The value for vl_rot is coded as following (matching to
+		fbcon=rotate:<n> linux-kernel commandline):
+		0 = no rotation respectively 0 degree
+		1 = 90 degree rotation
+		2 = 180 degree rotation
+		3 = 270 degree rotation
+
+		If CONFIG_LCD_ROTATION is not defined, the console will be
+		initialized with 0degree rotation.
+
 		CONFIG_LCD_BMP_RLE8
 
 		Support drawing of RLE8-compressed bitmaps on the LCD.
diff --git a/common/Makefile b/common/Makefile
index 7216a13..a1b3c09 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -200,6 +200,7 @@  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_ROTATION) += lcd_console_rotation.o
 obj-$(CONFIG_LCD_DT_SIMPLEFB) += lcd_simplefb.o
 obj-$(CONFIG_LYNXKDI) += lynxkdi.o
 obj-$(CONFIG_MENU) += menu.o
diff --git a/common/lcd.c b/common/lcd.c
index f33942c..d1b4ca7 100644
--- a/common/lcd.c
+++ b/common/lcd.c
@@ -167,7 +167,6 @@  int drv_lcd_init(void)
 
 void lcd_clear(void)
 {
-	short console_rows, console_cols;
 	int bg_color;
 	char *s;
 	ulong addr;
@@ -211,16 +210,14 @@  void lcd_clear(void)
 	}
 #endif
 #endif
+	/* setup text-console */
+	debug("[LCD] setting up console...\n");
+	lcd_init_console(lcd_base,
+			 panel_info.vl_col,
+			 panel_info.vl_row,
+			 panel_info.vl_rot);
 	/* Paint the logo and retrieve LCD base address */
 	debug("[LCD] Drawing the logo...\n");
-#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
-	console_rows = (panel_info.vl_row - BMP_LOGO_HEIGHT);
-	console_rows /= VIDEO_FONT_HEIGHT;
-#else
-	console_rows = panel_info.vl_row / VIDEO_FONT_HEIGHT;
-#endif
-	console_cols = panel_info.vl_col / VIDEO_FONT_WIDTH;
-	lcd_init_console(lcd_base, console_rows, console_cols);
 	if (do_splash) {
 		s = getenv("splashimage");
 		if (s) {
diff --git a/common/lcd_console.c b/common/lcd_console.c
index cac77be..14fd890 100644
--- a/common/lcd_console.c
+++ b/common/lcd_console.c
@@ -1,7 +1,8 @@ 
 /*
- * (C) Copyright 2001-2014
+ * (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+
  */
@@ -10,26 +11,16 @@ 
 #include <lcd.h>
 #include <video_font.h>		/* Get font data, width and height */
 
-#define CONSOLE_ROW_SIZE	(VIDEO_FONT_HEIGHT * lcd_line_length)
-#define CONSOLE_ROW_FIRST	cons.lcd_address
-#define CONSOLE_SIZE		(CONSOLE_ROW_SIZE * cons.rows)
+#if LCD_BPP == LCD_COLOR16
+	#define fbptr_t ushort
+#elif LCD_BPP == LCD_COLOR32
+	#define fbptr_t u32
+#else
+	#define fbptr_t uchar
+#endif
 
-struct console_t {
-	short curr_col, curr_row;
-	short cols, rows;
-	void *lcd_address;
-};
 static struct console_t cons;
 
-void lcd_init_console(void *address, int rows, int cols)
-{
-	memset(&cons, 0, sizeof(cons));
-	cons.cols = cols;
-	cons.rows = rows;
-	cons.lcd_address = address;
-
-}
-
 void lcd_set_col(short col)
 {
 	cons.curr_col = col;
@@ -56,61 +47,50 @@  int lcd_get_screen_columns(void)
 	return cons.cols;
 }
 
-static void lcd_putc_xy(ushort x, ushort y, char c)
+static void lcd_putc_xy0(struct console_t *pcons, ushort x, ushort y, char c)
 {
-	uchar *dest;
-	ushort row;
 	int fg_color = lcd_getfgcolor();
 	int bg_color = lcd_getbgcolor();
-	int i;
-
-	dest = (uchar *)(cons.lcd_address +
-			 y * lcd_line_length + x * NBITS(LCD_BPP) / 8);
-
-	for (row = 0; row < VIDEO_FONT_HEIGHT; ++row, dest += lcd_line_length) {
-#if LCD_BPP == LCD_COLOR16
-		ushort *d = (ushort *)dest;
-#elif LCD_BPP == LCD_COLOR32
-		u32 *d = (u32 *)dest;
-#else
-		uchar *d = dest;
-#endif
-		uchar bits;
-		bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
-
-		for (i = 0; i < 8; ++i) {
-			*d++ = (bits & 0x80) ? fg_color : bg_color;
+	int i, row;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  y * pcons->lcdsizex +
+				  x;
+
+	for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
+		uchar bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
+		for (i = 0; i < VIDEO_FONT_WIDTH; ++i) {
+			*dst++ = (bits & 0x80) ? fg_color : bg_color;
 			bits <<= 1;
 		}
+		dst += (pcons->lcdsizex - VIDEO_FONT_WIDTH);
 	}
 }
 
-static void console_scrollup(void)
+static inline void console_setrow0(struct console_t *pcons, u32 row, int clr)
 {
-	const int rows = CONFIG_CONSOLE_SCROLL_LINES;
-	int bg_color = lcd_getbgcolor();
+	int i;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  row * VIDEO_FONT_HEIGHT *
+				  pcons->lcdsizex;
 
-	/* Copy up rows ignoring those that will be overwritten */
-	memcpy(CONSOLE_ROW_FIRST,
-	       cons.lcd_address + CONSOLE_ROW_SIZE * rows,
-	       CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows);
+	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
+		*dst++ = clr;
+}
 
-	/* Clear the last rows */
-#if (LCD_BPP != LCD_COLOR32)
-	memset(lcd_console_address + CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows,
-	       bg_color, CONSOLE_ROW_SIZE * rows);
-#else
-	u32 *ppix = cons.lcd_address +
-		    CONSOLE_SIZE - CONSOLE_ROW_SIZE * rows;
-	u32 i;
-	for (i = 0;
-	    i < (CONSOLE_ROW_SIZE * rows) / NBYTES(panel_info.vl_bpix);
-	    i++) {
-		*ppix++ = bg_color;
-	}
-#endif
-	lcd_sync();
-	cons.curr_row -= rows;
+static inline void console_moverow0(struct console_t *pcons,
+				    u32 rowdst, u32 rowsrc)
+{
+	int i;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  rowdst * VIDEO_FONT_HEIGHT *
+				  pcons->lcdsizex;
+
+	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
+				  rowsrc * VIDEO_FONT_HEIGHT *
+				  pcons->lcdsizex;
+
+	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
+		*dst++ = *src++;
 }
 
 static inline void console_back(void)
@@ -121,19 +101,65 @@  static inline void console_back(void)
 			cons.curr_row = 0;
 	}
 
-	lcd_putc_xy(cons.curr_col * VIDEO_FONT_WIDTH,
-		    cons.curr_row * VIDEO_FONT_HEIGHT, ' ');
+	cons.fp_putc_xy(&cons,
+			cons.curr_col * VIDEO_FONT_WIDTH,
+			cons.curr_row * VIDEO_FONT_HEIGHT, ' ');
 }
 
 static inline void console_newline(void)
 {
+	const int rows = CONFIG_CONSOLE_SCROLL_LINES;
+	int bg_color = lcd_getbgcolor();
+	int i;
+
 	cons.curr_col = 0;
 
 	/* Check if we need to scroll the terminal */
-	if (++cons.curr_row >= cons.rows)
-		console_scrollup();
-	else
-		lcd_sync();
+	if (++cons.curr_row >= cons.rows) {
+		for (i = 0; i < cons.rows-rows; i++)
+			cons.fp_console_moverow(&cons, i, i+rows);
+		for (i = 0; i < rows; i++)
+			cons.fp_console_setrow(&cons, cons.rows-i-1, bg_color);
+		cons.curr_row -= rows;
+	}
+	lcd_sync();
+}
+
+static void console_calc_rowcol(struct console_t *pcons)
+{
+	pcons->cols = pcons->lcdsizex / VIDEO_FONT_WIDTH;
+#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
+	pcons->rows = (pcons->lcdsizey - BMP_LOGO_HEIGHT);
+	pcons->rows /= VIDEO_FONT_HEIGHT;
+#else
+	pcons->rows = pcons->lcdsizey / VIDEO_FONT_HEIGHT;
+#endif
+}
+
+void __weak lcd_init_console_rot(struct console_t *pcons)
+{
+	return;
+}
+
+void lcd_init_console(void *address, int vl_cols, int vl_rows, int vl_rot)
+{
+	memset(&cons, 0, sizeof(cons));
+	cons.lcd_address = address;
+
+	cons.lcdsizex = vl_cols;
+	cons.lcdsizey = vl_rows;
+	cons.lcdrot = vl_rot;
+
+	cons.fp_putc_xy = &lcd_putc_xy0;
+	cons.fp_console_moverow = &console_moverow0;
+	cons.fp_console_setrow = &console_setrow0;
+	console_calc_rowcol(&cons);
+
+	lcd_init_console_rot(&cons);
+
+	debug("lcd_console: have %d/%d col/rws on scr %dx%d (%d deg rotated)\n",
+	      cons.cols, cons.rows, cons.lcdsizex, cons.lcdsizey, vl_rot);
+
 }
 
 void lcd_putc(const char c)
@@ -165,8 +191,9 @@  void lcd_putc(const char c)
 
 		return;
 	default:
-		lcd_putc_xy(cons.curr_col * VIDEO_FONT_WIDTH,
-			    cons.curr_row * VIDEO_FONT_HEIGHT, c);
+		cons.fp_putc_xy(&cons,
+				cons.curr_col * VIDEO_FONT_WIDTH,
+				cons.curr_row * VIDEO_FONT_HEIGHT, c);
 		if (++cons.curr_col >= cons.cols)
 			console_newline();
 	}
@@ -235,4 +262,3 @@  U_BOOT_CMD(
 	"print string on lcd-framebuffer",
 	"    <string>"
 );
-
diff --git a/common/lcd_console_rotation.c b/common/lcd_console_rotation.c
new file mode 100644
index 0000000..3b6bb4f
--- /dev/null
+++ b/common/lcd_console_rotation.c
@@ -0,0 +1,216 @@ 
+/*
+ * (C) Copyright 2015
+ * Bernecker & Rainer Industrieelektronik GmbH - http://www.br-automation.com
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <lcd.h>
+#include <video_font.h>		/* Get font data, width and height */
+
+#if LCD_BPP == LCD_COLOR16
+	#define fbptr_t ushort
+#elif LCD_BPP == LCD_COLOR32
+	#define fbptr_t u32
+#else
+	#define fbptr_t uchar
+#endif
+
+static void lcd_putc_xy90(struct console_t *pcons, ushort x, ushort y, char c)
+{
+	int fg_color = lcd_getfgcolor();
+	int bg_color = lcd_getbgcolor();
+	int col, i;
+
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  (x+1) * pcons->lcdsizex -
+				  y;
+
+	uchar msk = 0x80;
+	uchar *pfont = video_fontdata + c * VIDEO_FONT_HEIGHT;
+	for (col = 0; col < VIDEO_FONT_WIDTH; ++col) {
+		for (i = 0; i < VIDEO_FONT_HEIGHT; ++i)
+			*dst-- = (*(pfont + i) & msk) ? fg_color : bg_color;
+		msk >>= 1;
+		dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
+	}
+}
+
+static inline void console_setrow90(struct console_t *pcons, u32 row, int clr)
+{
+	int i, j;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  pcons->lcdsizex -
+				  row*VIDEO_FONT_HEIGHT+1;
+
+	for (j = 0; j < pcons->lcdsizey; j++) {
+		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+			*dst-- = clr;
+		dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
+	}
+}
+
+static inline void console_moverow90(struct console_t *pcons,
+				      u32 rowdst, u32 rowsrc)
+{
+	int i, j;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  pcons->lcdsizex -
+				  (rowdst*VIDEO_FONT_HEIGHT+1);
+
+	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
+				  pcons->lcdsizex -
+				  (rowsrc*VIDEO_FONT_HEIGHT+1);
+
+	for (j = 0; j < pcons->lcdsizey; j++) {
+		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+			*dst-- = *src--;
+		src += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
+		dst += (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
+	}
+}
+static void lcd_putc_xy180(struct console_t *pcons, ushort x, ushort y, char c)
+{
+	int fg_color = lcd_getfgcolor();
+	int bg_color = lcd_getbgcolor();
+	int i, row;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  pcons->lcdsizex +
+				  pcons->lcdsizey * pcons->lcdsizex -
+				  y * pcons->lcdsizex -
+				  (x+1);
+
+	for (row = 0; row < VIDEO_FONT_HEIGHT; row++) {
+		uchar bits = video_fontdata[c * VIDEO_FONT_HEIGHT + row];
+
+		for (i = 0; i < VIDEO_FONT_WIDTH; ++i) {
+			*dst-- = (bits & 0x80) ? fg_color : bg_color;
+			bits <<= 1;
+		}
+		dst -= (pcons->lcdsizex - VIDEO_FONT_WIDTH);
+	}
+}
+
+static inline void console_setrow180(struct console_t *pcons, u32 row, int clr)
+{
+	int i;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  (pcons->rows-row-1) * VIDEO_FONT_HEIGHT *
+				  pcons->lcdsizex;
+
+	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
+		*dst++ = clr;
+}
+
+static inline void console_moverow180(struct console_t *pcons,
+				      u32 rowdst, u32 rowsrc)
+{
+	int i;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  (pcons->rows-rowdst-1) * VIDEO_FONT_HEIGHT *
+				  pcons->lcdsizex;
+
+	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
+				  (pcons->rows-rowsrc-1) * VIDEO_FONT_HEIGHT *
+				  pcons->lcdsizex;
+
+	for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
+		*dst++ = *src++;
+}
+
+static void lcd_putc_xy270(struct console_t *pcons, ushort x, ushort y, char c)
+{
+	int fg_color = lcd_getfgcolor();
+	int bg_color = lcd_getbgcolor();
+	int i, col;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  pcons->lcdsizey * pcons->lcdsizex -
+				  (x+1) * pcons->lcdsizex +
+				  y;
+
+	uchar msk = 0x80;
+	uchar *pfont = video_fontdata + c * VIDEO_FONT_HEIGHT;
+	for (col = 0; col < VIDEO_FONT_WIDTH; ++col) {
+		for (i = 0; i < VIDEO_FONT_HEIGHT; ++i)
+			*dst++ = (*(pfont + i) & msk) ? fg_color : bg_color;
+		msk >>= 1;
+		dst -= (pcons->lcdsizex + VIDEO_FONT_HEIGHT);
+	}
+}
+
+static inline void console_setrow270(struct console_t *pcons, u32 row, int clr)
+{
+	int i, j;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  row*VIDEO_FONT_HEIGHT;
+
+	for (j = 0; j < pcons->lcdsizey; j++) {
+		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+			*dst++ = clr;
+		dst += (pcons->lcdsizex - VIDEO_FONT_HEIGHT);
+	}
+}
+
+static inline void console_moverow270(struct console_t *pcons,
+				     u32 rowdst, u32 rowsrc)
+{
+	int i, j;
+	fbptr_t *dst = (fbptr_t *)pcons->lcd_address +
+				  rowdst*VIDEO_FONT_HEIGHT;
+
+	fbptr_t *src = (fbptr_t *)pcons->lcd_address +
+				  rowsrc*VIDEO_FONT_HEIGHT;
+
+	for (j = 0; j < pcons->lcdsizey; j++) {
+		for (i = 0; i < VIDEO_FONT_HEIGHT; i++)
+			*dst++ = *src++;
+		src += (pcons->lcdsizex - VIDEO_FONT_HEIGHT);
+		dst += (pcons->lcdsizex - VIDEO_FONT_HEIGHT);
+	}
+}
+
+static void console_calc_rowcol_rot(struct console_t *pcons)
+{
+	u32 cols, rows;
+
+	if (pcons->lcdrot == 1 || pcons->lcdrot == 3) {
+		cols = pcons->lcdsizey;
+		rows = pcons->lcdsizex;
+	} else {
+		cols = pcons->lcdsizex;
+		rows = pcons->lcdsizey;
+	}
+
+	pcons->cols = cols / VIDEO_FONT_WIDTH;
+#if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
+	pcons->rows = (rows - BMP_LOGO_HEIGHT);
+	pcons->rows /= VIDEO_FONT_HEIGHT;
+#else
+	pcons->rows = rows / VIDEO_FONT_HEIGHT;
+#endif
+}
+
+void lcd_init_console_rot(struct console_t *pcons)
+{
+	if (pcons->lcdrot == 0) {
+		return;
+	} else if (pcons->lcdrot == 1) {
+		pcons->fp_putc_xy = &lcd_putc_xy90;
+		pcons->fp_console_moverow = &console_moverow90;
+		pcons->fp_console_setrow = &console_setrow90;
+	} else if (pcons->lcdrot == 2) {
+		pcons->fp_putc_xy = &lcd_putc_xy180;
+		pcons->fp_console_moverow = &console_moverow180;
+		pcons->fp_console_setrow = &console_setrow180;
+	} else if (pcons->lcdrot == 3) {
+		pcons->fp_putc_xy = &lcd_putc_xy270;
+		pcons->fp_console_moverow = &console_moverow270;
+		pcons->fp_console_setrow = &console_setrow270;
+	} else {
+		puts("lcd_init_console_rot: invalid framebuffer rotation!\n");
+		puts("                      using def. 0 degree rotation.\n");
+		return;
+	}
+	console_calc_rowcol_rot(pcons);
+}
diff --git a/include/lcd.h b/include/lcd.h
index f049fd3..1329280 100644
--- a/include/lcd.h
+++ b/include/lcd.h
@@ -51,6 +51,7 @@  void lcd_set_flush_dcache(int flush);
 typedef struct vidinfo {
 	ushort	vl_col;		/* Number of columns (i.e. 160) */
 	ushort	vl_row;		/* Number of rows (i.e. 100) */
+	ushort	vl_rot;		/* Rotation of Display (0, 1, 2, 3) */
 	u_char	vl_bpix;	/* Bits per pixel, 0 = 1 */
 	ushort	*cmap;		/* Pointer to the colormap */
 	void	*priv;		/* Pointer to driver-specific data */
diff --git a/include/lcd_console.h b/include/lcd_console.h
index 429214d..6612a38 100644
--- a/include/lcd_console.h
+++ b/include/lcd_console.h
@@ -9,6 +9,16 @@ 
 #define CONFIG_CONSOLE_SCROLL_LINES 1
 #endif
 
+struct console_t {
+	short curr_col, curr_row;
+	short cols, rows;
+	void *lcd_address;
+	u32 lcdsizex, lcdsizey, lcdrot;
+	void (*fp_putc_xy)(struct console_t *pcons, ushort x, ushort y, char c);
+	void (*fp_console_moverow)(struct console_t *pcons,
+				   u32 rowdst, u32 rowsrc);
+	void (*fp_console_setrow)(struct console_t *pcons, u32 row, int clr);
+};
 /**
  * lcd_init_console() - Initialize lcd console parameters
  *
@@ -16,11 +26,11 @@ 
  * console has.
  *
  * @address: Console base address
- * @rows: Number of rows in the console
- * @cols: Number of columns in the console
+ * @vl_rows: Number of rows in the console
+ * @vl_cols: Number of columns in the console
+ * @vl_rot: Rotation of display in degree (0 - 90 - 180 - 270) counterlockwise
  */
-void lcd_init_console(void *address, int rows, int cols);
-
+void lcd_init_console(void *address, int vl_cols, int vl_rows, int vl_rot);
 /**
  * lcd_set_col() - Set the number of the current lcd console column
  *