*  linux/drivers/video/console/sticon.c - console driver using HP's STI firmware
  *
  *     Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
- *     Copyright (C) 2002 Helge Deller <deller@gmx.de>
+ *     Copyright (C) 2002-2020 Helge Deller <deller@gmx.de>
  *
  *  Based on linux/drivers/video/vgacon.c and linux/drivers/video/fbcon.c,
  *  which were
 #include <linux/kd.h>
 #include <linux/selection.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/font.h>
+#include <linux/crc32.h>
 
 #include <asm/io.h>
 
 #define BLANK 0
 static int vga_is_gfx;
 
-/* this is the sti_struct used for this console */
-static struct sti_struct *sticon_sti;
-
-/* Software scrollback */
-static unsigned long softback_buf, softback_curr;
-static unsigned long softback_in;
-static unsigned long /* softback_top, */ softback_end;
-static int softback_lines;
-
-/* software cursor */
-static int cursor_drawn;
-#define CURSOR_DRAW_DELAY              (1)
-#define DEFAULT_CURSOR_BLINK_RATE      (20)
+#define STI_DEF_FONT   sticon_sti->font
 
-static int vbl_cursor_cnt;
+/* borrowed from fbcon.c */
+#define FNTREFCOUNT(fd)        (fd->refcount)
+#define FNTCRC(fd)     (fd->crc)
+static struct sti_cooked_font *font_data[MAX_NR_CONSOLES];
 
-static inline void cursor_undrawn(void)
-{
-    vbl_cursor_cnt = 0;
-    cursor_drawn = 0;
-}
+/* this is the sti_struct used for this console */
+static struct sti_struct *sticon_sti;
 
 static const char *sticon_startup(void)
 {
 
 static void sticon_putc(struct vc_data *conp, int c, int ypos, int xpos)
 {
-    int redraw_cursor = 0;
-
     if (vga_is_gfx || console_blanked)
            return;
 
     if (conp->vc_mode != KD_TEXT)
            return;
-#if 0
-    if ((p->cursor_x == xpos) && (p->cursor_y == ypos)) {
-           cursor_undrawn();
-           redraw_cursor = 1;
-    }
-#endif
-
-    sti_putc(sticon_sti, c, ypos, xpos);
 
-    if (redraw_cursor)
-           vbl_cursor_cnt = CURSOR_DRAW_DELAY;
+    sti_putc(sticon_sti, c, ypos, xpos, font_data[conp->vc_num]);
 }
 
 static void sticon_putcs(struct vc_data *conp, const unsigned short *s,
                         int count, int ypos, int xpos)
 {
-    int redraw_cursor = 0;
-
     if (vga_is_gfx || console_blanked)
            return;
 
     if (conp->vc_mode != KD_TEXT)
            return;
 
-#if 0
-    if ((p->cursor_y == ypos) && (xpos <= p->cursor_x) &&
-       (p->cursor_x < (xpos + count))) {
-           cursor_undrawn();
-           redraw_cursor = 1;
-    }
-#endif
-
     while (count--) {
-       sti_putc(sticon_sti, scr_readw(s++), ypos, xpos++);
+       sti_putc(sticon_sti, scr_readw(s++), ypos, xpos++,
+                font_data[conp->vc_num]);
     }
-
-    if (redraw_cursor)
-           vbl_cursor_cnt = CURSOR_DRAW_DELAY;
 }
 
 static void sticon_cursor(struct vc_data *conp, int mode)
 {
     unsigned short car1;
 
+    /* no cursor update if screen is blanked */
+    if (vga_is_gfx || console_blanked)
+       return;
+
     car1 = conp->vc_screenbuf[conp->state.x + conp->state.y * conp->vc_cols];
     switch (mode) {
     case CM_ERASE:
-       sti_putc(sticon_sti, car1, conp->state.y, conp->state.x);
+       sti_putc(sticon_sti, car1, conp->state.y, conp->state.x,
+                font_data[conp->vc_num]);
        break;
     case CM_MOVE:
     case CM_DRAW:
        case CUR_TWO_THIRDS:
        case CUR_BLOCK:
            sti_putc(sticon_sti, (car1 & 255) + (0 << 8) + (7 << 11),
-                    conp->state.y, conp->state.x);
+                    conp->state.y, conp->state.x, font_data[conp->vc_num]);
            break;
        }
        break;
 
     switch (dir) {
     case SM_UP:
-       sti_bmove(sti, t + count, 0, t, 0, b - t - count, conp->vc_cols);
-       sti_clear(sti, b - count, 0, count, conp->vc_cols, conp->vc_video_erase_char);
+       sti_bmove(sti, t + count, 0, t, 0, b - t - count, conp->vc_cols,
+                 font_data[conp->vc_num]);
+       sti_clear(sti, b - count, 0, count, conp->vc_cols,
+                 conp->vc_video_erase_char, font_data[conp->vc_num]);
        break;
 
     case SM_DOWN:
-       sti_bmove(sti, t, 0, t + count, 0, b - t - count, conp->vc_cols);
-       sti_clear(sti, t, 0, count, conp->vc_cols, conp->vc_video_erase_char);
+       sti_bmove(sti, t, 0, t + count, 0, b - t - count, conp->vc_cols,
+                 font_data[conp->vc_num]);
+       sti_clear(sti, t, 0, count, conp->vc_cols,
+                 conp->vc_video_erase_char, font_data[conp->vc_num]);
        break;
     }
 
     return false;
 }
 
+static int sticon_set_def_font(int unit, struct console_font *op)
+{
+       if (font_data[unit] != STI_DEF_FONT) {
+               if (--FNTREFCOUNT(font_data[unit]) == 0) {
+                       kfree(font_data[unit]->raw_ptr);
+                       kfree(font_data[unit]);
+               }
+               font_data[unit] = STI_DEF_FONT;
+       }
+
+       return 0;
+}
+
+static int sticon_set_font(struct vc_data *vc, struct console_font *op)
+{
+       struct sti_struct *sti = sticon_sti;
+       int vc_cols, vc_rows, vc_old_cols, vc_old_rows;
+       int unit = vc->vc_num;
+       int w = op->width;
+       int h = op->height;
+       int size, i, bpc, pitch;
+       struct sti_rom_font *new_font;
+       struct sti_cooked_font *cooked_font;
+       unsigned char *data = op->data, *p;
+
+       if ((w < 6) || (h < 6) || (w > 32) || (h > 32)
+           || (op->charcount != 256 && op->charcount != 512))
+               return -EINVAL;
+       pitch = ALIGN(w, 8) / 8;
+       bpc = pitch * h;
+       size = bpc * op->charcount;
+
+       new_font = kmalloc(sizeof(*new_font) + size, STI_LOWMEM);
+       if (!new_font)
+               return -ENOMEM;
+
+       new_font->first_char = 0;
+       new_font->last_char = op->charcount - 1;
+       new_font->width = w;
+       new_font->height = h;
+       new_font->font_type = STI_FONT_HPROMAN8;
+       new_font->bytes_per_char = bpc;
+       new_font->underline_height = 0;
+       new_font->underline_pos = 0;
+
+       cooked_font = kzalloc(sizeof(*cooked_font), GFP_KERNEL);
+       if (!cooked_font) {
+               kfree(new_font);
+               return -ENOMEM;
+       }
+       cooked_font->raw = new_font;
+       cooked_font->raw_ptr = new_font;
+       cooked_font->width = w;
+       cooked_font->height = h;
+       FNTREFCOUNT(cooked_font) = 0;   /* usage counter */
+
+       p = (unsigned char *) new_font;
+       p += sizeof(*new_font);
+       for (i = 0; i < op->charcount; i++) {
+               memcpy(p, data, bpc);
+               data += pitch*32;
+               p += bpc;
+       }
+       FNTCRC(cooked_font) = crc32(0, new_font, size + sizeof(*new_font));
+       sti_font_convert_bytemode(sti, cooked_font);
+       new_font = cooked_font->raw_ptr;
+
+       /* check if font is already used by other console */
+       for (i = 0; i < MAX_NR_CONSOLES; i++) {
+               if (font_data[i] != STI_DEF_FONT
+                   && (FNTCRC(font_data[i]) == FNTCRC(cooked_font))) {
+                       kfree(new_font);
+                       kfree(cooked_font);
+                       /* current font is the same as the new one */
+                       if (i == unit)
+                               return 0;
+                       cooked_font = font_data[i];
+                       new_font = cooked_font->raw_ptr;
+                       break;
+               }
+       }
+
+       /* clear screen with old font: we now may have less rows */
+       vc_old_rows = vc->vc_rows;
+       vc_old_cols = vc->vc_cols;
+       sti_clear(sticon_sti, 0, 0, vc_old_rows, vc_old_cols,
+                 vc->vc_video_erase_char, font_data[vc->vc_num]);
+
+       /* delete old font in case it is a user font */
+       sticon_set_def_font(unit, NULL);
+
+       FNTREFCOUNT(cooked_font)++;
+       font_data[unit] = cooked_font;
+
+       vc_cols = sti_onscreen_x(sti) / cooked_font->width;
+       vc_rows = sti_onscreen_y(sti) / cooked_font->height;
+       vc_resize(vc, vc_cols, vc_rows);
+
+       /* need to repaint screen if cols & rows are same as old font */
+       if (vc_cols == vc_old_cols && vc_rows == vc_old_rows)
+               update_screen(vc);
+
+       return 0;
+}
+
+static int sticon_font_default(struct vc_data *vc, struct console_font *op, char *name)
+{
+       return sticon_set_def_font(vc->vc_num, op);
+}
+
+static int sticon_font_set(struct vc_data *vc, struct console_font *font,
+                          unsigned int flags)
+{
+       return sticon_set_font(vc, font);
+}
+
 static void sticon_init(struct vc_data *c, int init)
 {
     struct sti_struct *sti = sticon_sti;
     int vc_cols, vc_rows;
 
     sti_set(sti, 0, 0, sti_onscreen_y(sti), sti_onscreen_x(sti), 0);
-    vc_cols = sti_onscreen_x(sti) / sti->font_width;
-    vc_rows = sti_onscreen_y(sti) / sti->font_height;
+    vc_cols = sti_onscreen_x(sti) / sti->font->width;
+    vc_rows = sti_onscreen_y(sti) / sti->font->height;
     c->vc_can_do_color = 1;
     
     if (init) {
        c->vc_cols = vc_cols;
        c->vc_rows = vc_rows;
     } else {
-       /* vc_rows = (c->vc_rows > vc_rows) ? vc_rows : c->vc_rows; */
-       /* vc_cols = (c->vc_cols > vc_cols) ? vc_cols : c->vc_cols; */
        vc_resize(c, vc_cols, vc_rows);
-/*     vc_resize_con(vc_rows, vc_cols, c->vc_num); */
     }
 }
 
 static void sticon_deinit(struct vc_data *c)
 {
+    int i;
+
+    /* free memory used by user font */
+    for (i = 0; i < MAX_NR_CONSOLES; i++)
+       sticon_set_def_font(i, NULL);
 }
 
 static void sticon_clear(struct vc_data *conp, int sy, int sx, int height,
     if (!height || !width)
        return;
 
-    sti_clear(sticon_sti, sy, sx, height, width, conp->vc_video_erase_char);
+    sti_clear(sticon_sti, sy, sx, height, width,
+             conp->vc_video_erase_char, font_data[conp->vc_num]);
 }
 
 static int sticon_switch(struct vc_data *conp)
            vga_is_gfx = 0;
        return 1;
     }
-    sti_clear(sticon_sti, 0,0, c->vc_rows, c->vc_cols, BLANK);
+    sti_clear(sticon_sti, 0, 0, c->vc_rows, c->vc_cols, BLANK,
+             font_data[c->vc_num]);
     if (mode_switch)
        vga_is_gfx = 1;
     return 1;
 }
 
-static u16 *sticon_screen_pos(const struct vc_data *conp, int offset)
-{
-    int line;
-    unsigned long p;
-
-    if (conp->vc_num != fg_console || !softback_lines)
-       return (u16 *)(conp->vc_origin + offset);
-    line = offset / conp->vc_size_row;
-    if (line >= softback_lines)
-       return (u16 *)(conp->vc_origin + offset - softback_lines * conp->vc_size_row);
-    p = softback_curr + offset;
-    if (p >= softback_end)
-       p += softback_buf - softback_end;
-    return (u16 *)p;
-}
-
-static unsigned long sticon_getxy(struct vc_data *conp, unsigned long pos,
-                                 int *px, int *py)
-{
-    int x, y;
-    unsigned long ret;
-    if (pos >= conp->vc_origin && pos < conp->vc_scr_end) {
-       unsigned long offset = (pos - conp->vc_origin) / 2;
-       
-       x = offset % conp->vc_cols;
-       y = offset / conp->vc_cols;
-       if (conp->vc_num == fg_console)
-           y += softback_lines;
-       ret = pos + (conp->vc_cols - x) * 2;
-    } else if (conp->vc_num == fg_console && softback_lines) {
-       unsigned long offset = pos - softback_curr;
-       
-       if (pos < softback_curr)
-           offset += softback_end - softback_buf;
-       offset /= 2;
-       x = offset % conp->vc_cols;
-       y = offset / conp->vc_cols;
-       ret = pos + (conp->vc_cols - x) * 2;
-       if (ret == softback_end)
-           ret = softback_buf;
-       if (ret == softback_in)
-           ret = conp->vc_origin;
-    } else {
-       /* Should not happen */
-       x = y = 0;
-       ret = conp->vc_origin;
-    }
-    if (px) *px = x;
-    if (py) *py = y;
-    return ret;
-}
-
 static u8 sticon_build_attr(struct vc_data *conp, u8 color,
                            enum vc_intensity intens,
                            bool blink, bool underline, bool reverse,
     }
 }
 
-static void sticon_save_screen(struct vc_data *conp)
-{
-}
-
 static const struct consw sti_con = {
        .owner                  = THIS_MODULE,
        .con_startup            = sticon_startup,
        .con_scroll             = sticon_scroll,
        .con_switch             = sticon_switch,
        .con_blank              = sticon_blank,
-       .con_save_screen        = sticon_save_screen, 
+       .con_font_set           = sticon_font_set,
+       .con_font_default       = sticon_font_default,
        .con_build_attr         = sticon_build_attr,
        .con_invert_region      = sticon_invert_region, 
-       .con_screen_pos         = sticon_screen_pos,
-       .con_getxy              = sticon_getxy,
 };
 
 
 
 static int __init sticonsole_init(void)
 {
-    int err;
+    int err, i;
+
     /* already initialized ? */
     if (sticon_sti)
         return 0;
     if (!sticon_sti)
        return -ENODEV;
 
+    for (i = 0; i < MAX_NR_CONSOLES; i++)
+       font_data[i] = STI_DEF_FONT;
+
     pr_info("sticon: Initializing STI text console.\n");
     console_lock();
     err = do_take_over_console(&sti_con, 0, MAX_NR_CONSOLES - 1,
 
  *     core code for console driver using HP's STI firmware
  *
  *     Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
- *     Copyright (C) 2001-2013 Helge Deller <deller@gmx.de>
+ *     Copyright (C) 2001-2020 Helge Deller <deller@gmx.de>
  *     Copyright (C) 2001-2002 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
  * 
  * TODO:
  * 
  */
 
+#define pr_fmt(fmt) "%s: " fmt, KBUILD_MODNAME
+
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 };
 
 void
-sti_putc(struct sti_struct *sti, int c, int y, int x)
+sti_putc(struct sti_struct *sti, int c, int y, int x,
+        struct sti_cooked_font *font)
 {
        struct sti_font_inptr *inptr = &sti->sti_data->font_inptr;
        struct sti_font_inptr inptr_default = {
-               .font_start_addr= STI_PTR(sti->font->raw),
+               .font_start_addr = STI_PTR(font->raw),
                .index          = c_index(sti, c),
                .fg_color       = c_fg(sti, c),
                .bg_color       = c_bg(sti, c),
-               .dest_x         = x * sti->font_width,
-               .dest_y         = y * sti->font_height,
+               .dest_x         = x * font->width,
+               .dest_y         = y * font->height,
        };
        struct sti_font_outptr *outptr = &sti->sti_data->font_outptr;
        s32 ret;
 
 void
 sti_clear(struct sti_struct *sti, int src_y, int src_x,
-         int height, int width, int c)
+         int height, int width, int c, struct sti_cooked_font *font)
 {
        struct sti_blkmv_inptr *inptr = &sti->sti_data->blkmv_inptr;
        struct sti_blkmv_inptr inptr_default = {
                .fg_color       = c_fg(sti, c),
                .bg_color       = c_bg(sti, c),
-               .src_x          = src_x * sti->font_width,
-               .src_y          = src_y * sti->font_height,
-               .dest_x         = src_x * sti->font_width,
-               .dest_y         = src_y * sti->font_height,
-               .width          = width * sti->font_width,
-               .height         = height* sti->font_height,
+               .src_x          = src_x * font->width,
+               .src_y          = src_y * font->height,
+               .dest_x         = src_x * font->width,
+               .dest_y         = src_y * font->height,
+               .width          = width * font->width,
+               .height         = height * font->height,
        };
        struct sti_blkmv_outptr *outptr = &sti->sti_data->blkmv_outptr;
        s32 ret;
 
 void
 sti_bmove(struct sti_struct *sti, int src_y, int src_x,
-         int dst_y, int dst_x, int height, int width)
+         int dst_y, int dst_x, int height, int width,
+         struct sti_cooked_font *font)
 {
        struct sti_blkmv_inptr *inptr = &sti->sti_data->blkmv_inptr;
        struct sti_blkmv_inptr inptr_default = {
-               .src_x          = src_x * sti->font_width,
-               .src_y          = src_y * sti->font_height,
-               .dest_x         = dst_x * sti->font_width,
-               .dest_y         = dst_y * sti->font_height,
-               .width          = width * sti->font_width,
-               .height         = height* sti->font_height,
+               .src_x          = src_x * font->width,
+               .src_y          = src_y * font->height,
+               .dest_x         = dst_x * font->width,
+               .dest_y         = dst_y * font->height,
+               .width          = width * font->width,
+               .height         = height * font->height,
        };
        struct sti_blkmv_outptr *outptr = &sti->sti_data->blkmv_outptr;
        s32 ret;
 
 
 
-static char *font_name[MAX_STI_ROMS];
-static int font_index[MAX_STI_ROMS],
-          font_height[MAX_STI_ROMS],
-          font_width[MAX_STI_ROMS];
+static char *font_name;
+static int font_index,
+          font_height,
+          font_width;
 #ifndef MODULE
 static int sti_font_setup(char *str)
 {
-       char *x;
-       int i = 0;
+       /*
+        * The default font can be selected in various ways.
+        * a) sti_font=VGA8x16, sti_font=10x20, sti_font=10*20 selects
+        *    an built-in Linux framebuffer font.
+        * b) sti_font=<index>, where index is (1..x) with 1 selecting
+        *    the first HP STI ROM built-in font..
+        */
 
-       /* we accept sti_font=VGA8x16, sti_font=10x20, sti_font=10*20 
-        * or sti_font=7 style command lines. */
+       if (*str >= '0' && *str <= '9') {
+               char *x;
 
-       while (i<MAX_STI_ROMS && str && *str) {
-               if (*str>='0' && *str<='9') {
-                       if ((x = strchr(str, 'x')) || (x = strchr(str, '*'))) {
-                               font_height[i] = simple_strtoul(str, NULL, 0);
-                               font_width[i] = simple_strtoul(x+1, NULL, 0);
-                       } else {
-                               font_index[i] = simple_strtoul(str, NULL, 0);
-                       }
+               if ((x = strchr(str, 'x')) || (x = strchr(str, '*'))) {
+                       font_height = simple_strtoul(str, NULL, 0);
+                       font_width = simple_strtoul(x+1, NULL, 0);
                } else {
-                       font_name[i] = str;     /* fb font name */
+                       font_index = simple_strtoul(str, NULL, 0);
                }
-
-               if ((x = strchr(str, ',')))
-                       *x++ = 0;
-               str = x;
-
-               i++;
+       } else {
+               font_name = str;        /* fb font name */
        }
 
        return 1;
  *             framebuffer font names (e.g. VGA8x16, SUN22x18). 
  *             This is only available if the fonts have been statically compiled 
  *             in with e.g. the CONFIG_FONT_8x16 or CONFIG_FONT_SUN12x22 options.
- *     - sti_font=<number>
+ *     - sti_font=<number>     (<number> = 1,2,3,...)
  *             most STI ROMs have built-in HP specific fonts, which can be selected
  *             by giving the desired number to the sticon driver. 
  *             NOTE: This number is machine and STI ROM dependend.
 {
        struct sti_glob_cfg_ext *cfg;
        
-       DPRINTK((KERN_INFO
-               "%d text planes\n"
+       pr_debug("%d text planes\n"
                "%4d x %4d screen resolution\n"
                "%4d x %4d offscreen\n"
                "%4d x %4d layout\n"
                glob_cfg->region_ptrs[4], glob_cfg->region_ptrs[5],
                glob_cfg->region_ptrs[6], glob_cfg->region_ptrs[7],
                glob_cfg->reent_lvl,
-               glob_cfg->save_addr));
+               glob_cfg->save_addr);
 
        /* dump extended cfg */ 
        cfg = PTR_STI((unsigned long)glob_cfg->ext_ptr);
-       DPRINTK(( KERN_INFO
-               "monitor %d\n"
+       pr_debug("monitor %d\n"
                "in friendly mode: %d\n"
                "power consumption %d watts\n"
                "freq ref %d\n"
                cfg->friendly_boot,
                cfg->power,
                cfg->freq_ref,
-               cfg->sti_mem_addr, sti_mem_request));
+               cfg->sti_mem_addr, sti_mem_request);
 }
 
 static void sti_dump_outptr(struct sti_struct *sti)
 {
-       DPRINTK((KERN_INFO
-               "%d bits per pixel\n"
+       pr_debug("%d bits per pixel\n"
                "%d used bits\n"
                "%d planes\n"
                "attributes %08x\n",
                 sti->sti_data->inq_outptr.bits_per_pixel,
                 sti->sti_data->inq_outptr.bits_used,
                 sti->sti_data->inq_outptr.planes,
-                sti->sti_data->inq_outptr.attributes));
+                sti->sti_data->inq_outptr.attributes);
 }
 
 static int sti_init_glob_cfg(struct sti_struct *sti, unsigned long rom_address,
                        if (offs != PCI_ROM_ADDRESS &&
                            (offs < PCI_BASE_ADDRESS_0 ||
                             offs > PCI_BASE_ADDRESS_5)) {
-                               printk (KERN_WARNING
-                                       "STI pci region mapping for region %d (%02x) can't be mapped\n",
+                               pr_warn("STI pci region mapping for region %d (%02x) can't be mapped\n",
                                        i,sti->rm_entry[i]);
                                continue;
                        }
                if (len)
                        glob_cfg->region_ptrs[i] = sti->regions_phys[i];
                
-               DPRINTK(("region #%d: phys %08lx, region_ptr %08x, len=%lukB, "
+               pr_debug("region #%d: phys %08lx, region_ptr %08x, len=%lukB, "
                         "btlb=%d, sysonly=%d, cache=%d, last=%d\n",
                        i, sti->regions_phys[i], glob_cfg->region_ptrs[i],
                        len/1024,
                        sti->regions[i].region_desc.btlb,
                        sti->regions[i].region_desc.sys_only,
                        sti->regions[i].region_desc.cache, 
-                       sti->regions[i].region_desc.last));
+                       sti->regions[i].region_desc.last);
 
                /* last entry reached ? */
                if (sti->regions[i].region_desc.last)
        }
 
        if (++i<8 && sti->regions[i].region)
-               printk(KERN_WARNING "%s: *future ptr (0x%8x) not yet supported !\n",
-                               __FILE__, sti->regions[i].region);
+               pr_warn("future ptr (0x%8x) not yet supported !\n",
+                       sti->regions[i].region);
 
        glob_cfg_ext->sti_mem_addr = STI_PTR(sti_mem_addr);
 
        }
        
        cooked_font->raw = nf;
+       cooked_font->raw_ptr = nf;
        cooked_font->next_font = NULL;
 
        cooked_rom->font_start = cooked_font;
 }
 #endif
 
-static struct sti_cooked_font *sti_select_font(struct sti_cooked_rom *rom,
-               int (*search_font_fnc)(struct sti_cooked_rom *, int, int))
+static int sti_search_font(struct sti_cooked_rom *rom, int height, int width)
+{
+       struct sti_cooked_font *font;
+       int i = 0;
+
+       for (font = rom->font_start; font; font = font->next_font, i++) {
+               if ((font->raw->width == width) &&
+                   (font->raw->height == height))
+                       return i;
+       }
+       return 0;
+}
+
+static struct sti_cooked_font *sti_select_font(struct sti_cooked_rom *rom)
 {
        struct sti_cooked_font *font;
        int i;
-       int index = num_sti_roms;
 
        /* check for framebuffer-font first */
-       if ((font = sti_select_fbfont(rom, font_name[index])))
-               return font;
+       if (!font_index) {
+               font = sti_select_fbfont(rom, font_name);
+               if (font)
+                       return font;
+       }
 
-       if (font_width[index] && font_height[index])
-               font_index[index] = search_font_fnc(rom, 
-                               font_height[index], font_width[index]);
+       if (font_width && font_height)
+               font_index = sti_search_font(rom,
+                               font_height, font_width);
 
-       for (font = rom->font_start, i = font_index[index];
-           font && (i > 0);
-           font = font->next_font, i--);
+       for (font = rom->font_start, i = font_index - 1;
+               font && (i > 0);
+               font = font->next_font, i--);
 
        if (font)
                return font;
 }
 
 
-static void sti_dump_rom(struct sti_rom *rom)
+static void sti_dump_rom(struct sti_struct *sti)
 {
-       printk(KERN_INFO "    id %04x-%04x, conforms to spec rev. %d.%02x\n",
+       struct sti_rom *rom = sti->rom->raw;
+       struct sti_cooked_font *font_start;
+       int nr;
+
+       pr_info("  id %04x-%04x, conforms to spec rev. %d.%02x\n",
                rom->graphics_id[0], 
                rom->graphics_id[1],
                rom->revno[0] >> 4, 
                rom->revno[0] & 0x0f);
-       DPRINTK(("      supports %d monitors\n", rom->num_mons));
-       DPRINTK(("      font start %08x\n", rom->font_start));
-       DPRINTK(("      region list %08x\n", rom->region_list));
-       DPRINTK(("      init_graph %08x\n", rom->init_graph));
-       DPRINTK(("      bus support %02x\n", rom->bus_support));
-       DPRINTK(("      ext bus support %02x\n", rom->ext_bus_support));
-       DPRINTK(("      alternate code type %d\n", rom->alt_code_type));
+       pr_debug("  supports %d monitors\n", rom->num_mons);
+       pr_debug("  font start %08x\n", rom->font_start);
+       pr_debug("  region list %08x\n", rom->region_list);
+       pr_debug("  init_graph %08x\n", rom->init_graph);
+       pr_debug("  bus support %02x\n", rom->bus_support);
+       pr_debug("  ext bus support %02x\n", rom->ext_bus_support);
+       pr_debug("  alternate code type %d\n", rom->alt_code_type);
+
+       font_start = sti->rom->font_start;
+       nr = 0;
+       while (font_start) {
+               struct sti_rom_font *f = font_start->raw;
+
+               pr_info("    built-in font #%d: size %dx%d, chars %d-%d, bpc %d\n", ++nr,
+                       f->width, f->height,
+                       f->first_char, f->last_char, f->bytes_per_char);
+               font_start = font_start->next_font;
+       }
 }
 
 
        return 1;
 }
 
-
-static int sti_search_font(struct sti_cooked_rom *rom, int height, int width)
-{
-       struct sti_cooked_font *font;
-       int i = 0;
-       
-       for (font = rom->font_start; font; font = font->next_font, i++) {
-               if ((font->raw->width == width) &&
-                   (font->raw->height == height))
-                       return i;
-       }
-       return 0;
-}
-
 #define BMODE_RELOCATE(offset)         offset = (offset) / 4;
 #define BMODE_LAST_ADDR_OFFS           0x50
 
-static void *sti_bmode_font_raw(struct sti_cooked_font *f)
+void sti_font_convert_bytemode(struct sti_struct *sti, struct sti_cooked_font *f)
 {
        unsigned char *n, *p, *q;
-       int size = f->raw->bytes_per_char*256+sizeof(struct sti_rom_font);
-       
+       int size = f->raw->bytes_per_char * 256 + sizeof(struct sti_rom_font);
+       struct sti_rom_font *old_font;
+
+       if (sti->wordmode)
+               return;
+
+       old_font = f->raw_ptr;
        n = kcalloc(4, size, STI_LOWMEM);
+       f->raw_ptr = n;
        if (!n)
-               return NULL;
+               return;
        p = n + 3;
-       q = (unsigned char *)f->raw;
+       q = (unsigned char *) f->raw;
        while (size--) {
                *p = *q++;
-               p+=4;
+               p += 4;
        }
-       return n + 3;
+       /* store new ptr to byte-mode font and delete old font */
+       f->raw = (struct sti_rom_font *) (n + 3);
+       kfree(old_font);
 }
+EXPORT_SYMBOL(sti_font_convert_bytemode);
 
 static void sti_bmode_rom_copy(unsigned long base, unsigned long count,
                               void *dest)
                goto out_err;
 
        if (!sti_cook_fonts(cooked, raw)) {
-               printk(KERN_ERR "No font found for STI at %08lx\n", address);
+               pr_warn("No font found for STI at %08lx\n", address);
                goto out_err;
        }
 
 
        address = (unsigned long) STI_PTR(raw);
 
-       pr_info("STI ROM supports 32 %sbit firmware functions.\n",
+       pr_info("STI %s ROM supports 32 %sbit firmware functions.\n",
+               wordmode ? "word mode" : "byte mode",
                raw->alt_code_type == ALT_CODE_TYPE_PA_RISC_64
                ? "and 64 " : "");
 
 
        sti->rom = cooked;
        sti->rom->raw = raw;
-       
-       sti->font = sti_select_font(sti->rom, sti_search_font);
-       sti->font_width = sti->font->raw->width;
-       sti->font_height = sti->font->raw->height;
-       if (!wordmode)
-               sti->font->raw = sti_bmode_font_raw(sti->font);
+       sti_dump_rom(sti);
+
+       sti->wordmode = wordmode;
+       sti->font = sti_select_font(sti->rom);
+       sti->font->width = sti->font->raw->width;
+       sti->font->height = sti->font->raw->height;
+       sti_font_convert_bytemode(sti, sti->font);
 
        sti->sti_mem_request = raw->sti_mem_req;
        sti->graphics_id[0] = raw->graphics_id[0];
        sti->graphics_id[1] = raw->graphics_id[1];
-       
-       sti_dump_rom(raw);
 
        /* check if the ROM routines in this card are compatible */
        if (wordmode || sti->graphics_id[1] != 0x09A02587)
        return 1;
 
 msg_not_supported:
-       printk(KERN_ERR "Sorry, this GSC/STI card is not yet supported.\n");
-       printk(KERN_ERR "Please see http://parisc-linux.org/faq/"
-                       "graphics-howto.html for more info.\n");
+       pr_warn("Sorry, this GSC/STI card is not yet supported.\n");
+       pr_warn("Please see https://parisc.wiki.kernel.org/"
+               "index.php/Graphics_howto for more info.\n");
        /* fall through */
 out_err:
        kfree(raw);
        u32 sig;
 
        if (num_sti_roms >= MAX_STI_ROMS) {
-               printk(KERN_WARNING "maximum number of STI ROMS reached !\n");
+               pr_warn("maximum number of STI ROMS reached !\n");
                return NULL;
        }
        
                if (i != 1) {
                        /* The ROM could have multiple architecture 
                         * dependent images (e.g. i386, parisc,...) */
-                       printk(KERN_WARNING 
-                               "PCI ROM is not a STI ROM type image (0x%8x)\n", i);
+                       pr_warn("PCI ROM is not a STI ROM type image (0x%8x)\n", i);
                        goto out_err;
                }
                
                sti->pd = pd;
 
                i = gsc_readl(address+0x0c);
-               DPRINTK(("PCI ROM size (from header) = %d kB\n",
-                       le16_to_cpu(i>>16)*512/1024));
+               pr_debug("PCI ROM size (from header) = %d kB\n",
+                       le16_to_cpu(i>>16)*512/1024);
                rm_offset = le16_to_cpu(i & 0xffff);
                if (rm_offset) { 
                        /* read 16 bytes from the pci region mapper array */
                        *rm++ = gsc_readl(address+rm_offset+0x04);
                        *rm++ = gsc_readl(address+rm_offset+0x08);
                        *rm++ = gsc_readl(address+rm_offset+0x0c);
-                       DPRINTK(("PCI region Mapper offset = %08x: ",
-                               rm_offset));
-                       for (i=0; i<16; i++)
-                               DPRINTK(("%02x ", sti->rm_entry[i]));
-                       DPRINTK(("\n"));
                }
 
                address += le32_to_cpu(gsc_readl(address+8));
-               DPRINTK(("sig %04x, PCI STI ROM at %08lx\n", sig, address));
+               pr_debug("sig %04x, PCI STI ROM at %08lx\n", sig, address);
                goto test_rom;
        }
        
        ok = 0;
        
        if ((sig & 0xff) == 0x01) {
-               DPRINTK(("    byte mode ROM at %08lx, hpa at %08lx\n",
-                      address, hpa));
+               pr_debug("    byte mode ROM at %08lx, hpa at %08lx\n",
+                      address, hpa);
                ok = sti_read_rom(0, sti, address);
        }
 
        if ((sig & 0xffff) == 0x0303) {
-               DPRINTK(("    word mode ROM at %08lx, hpa at %08lx\n",
-                      address, hpa));
+               pr_debug("    word mode ROM at %08lx, hpa at %08lx\n",
+                      address, hpa);
                ok = sti_read_rom(1, sti, address);
        }
 
                unsigned long rom_base;
                rom_base = pci_resource_start(sti->pd, PCI_ROM_RESOURCE);       
                pci_write_config_dword(sti->pd, PCI_ROM_ADDRESS, rom_base & ~PCI_ROM_ADDRESS_ENABLE);
-               DPRINTK((KERN_DEBUG "STI PCI ROM disabled\n"));
+               pr_debug("STI PCI ROM disabled\n");
        }
 
        if (sti_init_graph(sti))
        rom_len = pci_resource_len(pd, PCI_ROM_RESOURCE);
        if (rom_base) {
                pci_write_config_dword(pd, PCI_ROM_ADDRESS, rom_base | PCI_ROM_ADDRESS_ENABLE);
-               DPRINTK((KERN_DEBUG "STI PCI ROM enabled at 0x%08lx\n", rom_base));
+               pr_debug("STI PCI ROM enabled at 0x%08lx\n", rom_base);
        }
 
-       printk(KERN_INFO "STI PCI graphic ROM found at %08lx (%u kB), fb at %08lx (%u MB)\n",
+       pr_info("STI PCI graphic ROM found at %08lx (%u kB), fb at %08lx (%u MB)\n",
                rom_base, rom_len/1024, fb_base, fb_len/1024/1024);
 
-       DPRINTK((KERN_DEBUG "Trying PCI STI ROM at %08lx, PCI hpa at %08lx\n",
-                   rom_base, fb_base));
+       pr_debug("Trying PCI STI ROM at %08lx, PCI hpa at %08lx\n",
+                   rom_base, fb_base);
 
        sti = sti_try_rom_generic(rom_base, fb_base, pd);
        if (sti) {
        }
        
        if (!sti) {
-               printk(KERN_WARNING "Unable to handle STI device '%s'\n",
-                       pci_name(pd));
+               pr_warn("Unable to handle STI device '%s'\n", pci_name(pd));
                return -ENODEV;
        }
 #endif /* CONFIG_PCI */
 
        sticore_initialized = 1;
 
-       printk(KERN_INFO "STI GSC/PCI core graphics driver "
+       pr_info("STI GSC/PCI core graphics driver "
                        STI_DRIVERVERSION "\n");
 
        /* Register drivers for native & PCI cards */