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's mounted landscape but rotated by 180degree,
+               we need to rotate our content of the display relative to the
+               framebuffer, so that user can read the messages which are
+               printed out.
+               Once CONFIG_LCD_ROTATION is defined, 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.
 
 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
 
 
 void lcd_clear(void)
 {
-       short console_rows, console_cols;
        int bg_color;
        char *s;
        ulong addr;
        }
 #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) {
        lcd_logo();
 #if defined(CONFIG_LCD_LOGO) && !defined(CONFIG_LCD_INFO_BELOW_LOGO)
        addr = (ulong)lcd_base + BMP_LOGO_HEIGHT * lcd_line_length;
-       lcd_init_console((void *)addr, console_rows, console_cols);
+       lcd_init_console((void *)addr, panel_info.vl_row,
+                        panel_info.vl_col, panel_info.vl_rot);
 #endif
        lcd_sync();
 }
 
 /*
- * (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+
  */
 #include <common.h>
 #include <lcd.h>
 #include <video_font.h>                /* Get font data, width and height */
+#if defined(CONFIG_LCD_LOGO)
+#include <bmp_logo.h>
+#endif
 
-#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)
-
-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;
        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->fbbase +
+                                 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->fbbase +
+                                 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->fbbase +
+                                 rowdst * VIDEO_FONT_HEIGHT *
+                                 pcons->lcdsizex;
+
+       fbptr_t *src = (fbptr_t *)pcons->fbbase +
+                                 rowsrc * VIDEO_FONT_HEIGHT *
+                                 pcons->lcdsizex;
+
+       for (i = 0; i < (VIDEO_FONT_HEIGHT * pcons->lcdsizex); i++)
+               *dst++ = *src++;
 }
 
 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();
+}
+
+void console_calc_rowcol(struct console_t *pcons, u32 sizex, u32 sizey)
+{
+       pcons->cols = sizex / 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 = sizey / 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.fbbase = 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, cons.lcdsizex, cons.lcdsizey);
+
+       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)
 
                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();
        }
 
--- /dev/null
+/*
+ * (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 */
+
+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->fbbase +
+                                 (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->fbbase +
+                                 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->fbbase +
+                                 pcons->lcdsizex -
+                                 (rowdst*VIDEO_FONT_HEIGHT+1);
+
+       fbptr_t *src = (fbptr_t *)pcons->fbbase +
+                                 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->fbbase +
+                                 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->fbbase +
+                                 (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->fbbase +
+                                 (pcons->rows-rowdst-1) * VIDEO_FONT_HEIGHT *
+                                 pcons->lcdsizex;
+
+       fbptr_t *src = (fbptr_t *)pcons->fbbase +
+                                 (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->fbbase +
+                                 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->fbbase +
+                                 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->fbbase +
+                                 rowdst*VIDEO_FONT_HEIGHT;
+
+       fbptr_t *src = (fbptr_t *)pcons->fbbase +
+                                 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)
+{
+       if (pcons->lcdrot == 1 || pcons->lcdrot == 3)
+               console_calc_rowcol(pcons, pcons->lcdsizey, pcons->lcdsizex);
+       else
+               console_calc_rowcol(pcons, pcons->lcdsizex, pcons->lcdsizey);
+}
+
+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 {
+               printf("%s: invalid framebuffer rotation (%d)!\n",
+                      __func__, pcons->lcdrot);
+               return;
+       }
+       console_calc_rowcol_rot(pcons);
+}
 
 typedef struct vidinfo {
        ushort vl_col;          /* Number of columns (i.e. 640) */
        ushort vl_row;          /* Number of rows (i.e. 480) */
-       u_long vl_clk;  /* pixel clock in ps    */
+       ushort vl_rot;          /* Rotation of Display (0, 1, 2, 3) */
+       u_long vl_clk;          /* pixel clock in ps    */
 
        /* LCD configuration register */
        u_long vl_sync;         /* Horizontal / vertical sync */
 
 typedef struct vidinfo {
        ushort vl_col;          /* Number of columns (i.e. 640) */
        ushort vl_row;          /* Number of rows (i.e. 480) */
+       ushort vl_rot;          /* Rotation of Display (0, 1, 2, 3) */
        ushort vl_width;        /* Width of display area in millimeters */
        ushort vl_height;       /* Height of display area in millimeters */
 
 
 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 */
 #define CONSOLE_COLOR_WHITE    0xffff          /* Must remain last / highest */
 #endif /* color definitions */
 
+#if LCD_BPP == LCD_COLOR16
+#define fbptr_t ushort
+#elif LCD_BPP == LCD_COLOR32
+#define fbptr_t u32
+#else
+#define fbptr_t uchar
+#endif
+
 #ifndef PAGE_SIZE
 #define PAGE_SIZE      4096
 #endif
 
 #define CONFIG_CONSOLE_SCROLL_LINES 1
 #endif
 
+struct console_t {
+       short curr_col, curr_row;
+       short cols, rows;
+       void *fbbase;
+       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);
+};
+
+/**
+ * console_calc_rowcol() - calculate available rows / columns wihtin a given
+ * screen-size based on used VIDEO_FONT.
+ *
+ * @pcons: Pointer to struct console_t
+ * @sizex: size X of the screen in pixel
+ * @sizey: size Y of the screen in pixel
+ */
+void console_calc_rowcol(struct console_t *pcons, u32 sizex, u32 sizey);
 /**
  * lcd_init_console() - Initialize lcd console parameters
  *
  * 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
  *
 
 typedef struct vidinfo {
        ushort  vl_col;         /* Number of columns (i.e. 640) */
        ushort  vl_row;         /* Number of rows (i.e. 480) */
+       ushort  vl_rot;         /* Rotation of Display (0, 1, 2, 3) */
        ushort  vl_width;       /* Width of display area in millimeters */
        ushort  vl_height;      /* Height of display area in millimeters */
 
 
 typedef struct vidinfo {
        ushort  vl_col;         /* Number of columns (i.e. 640) */
        ushort  vl_row;         /* Number of rows (i.e. 480) */
+       ushort  vl_rot;         /* Rotation of Display (0, 1, 2, 3) */
        ushort  vl_width;       /* Width of display area in millimeters */
        ushort  vl_height;      /* Height of display area in millimeters */