Blob Blame History Raw
From: Egbert Eich <eich@suse.de>
Subject: Combined and consolidated bootsplash patch.
Patch-mainline: Never
References: bnc#669777, bnc#570082, bnc#595657, bnc#594209, bnc#544645

  * Combine all existing patches for the bootsplash code.
  * Add support for sharing bootsplash content between consoles
    (needed to support seamless switching from boot vt to vt used
    by the X Window System server.
  * Fix formatting according to kernel standards.
  * Fix a number of minor bugs discovered while reformatting the
    code.
  * Fix race where the struct fb_info may be missing when
    proc_read() is called: bnc#669777.

  * Fix bootsplash code for 15bpp framebuffers.
    These framebuffers are rare these days, the legendary and still
    popular server chipset Radeon ES1000 supports it, though.

  * Fix crash when updating boxes:
    Don't reallocate picture.

  * Add a workaround for Xen fb

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Egbert Eich <eich@suse.de>
---
 drivers/tty/n_tty.c                   |   10 
 drivers/tty/vt/keyboard.c             |    9 
 drivers/tty/vt/vt.c                   |   29 
 drivers/video/Kconfig                 |    4 
 drivers/video/Makefile                |    1 
 drivers/video/bootsplash/Kconfig      |   17 
 drivers/video/bootsplash/Makefile     |    5 
 drivers/video/bootsplash/bootsplash.c | 2492 ++++++++++++++++++++++++++++++++++
 drivers/video/bootsplash/decode-jpg.c | 1045 ++++++++++++++
 drivers/video/bootsplash/decode-jpg.h |   37 
 drivers/video/bootsplash/render.c     |  518 +++++++
 drivers/video/console/bitblit.c       |   31 
 drivers/video/console/fbcon.c         |   58 
 drivers/video/console/fbcon.h         |   48 
 include/linux/bootsplash.h            |   87 +
 include/linux/console_struct.h        |    3 
 include/linux/fb.h                    |    4 
 17 files changed, 4398 insertions(+)
 create mode 100644 drivers/video/bootsplash/Kconfig
 create mode 100644 drivers/video/bootsplash/Makefile
 create mode 100644 drivers/video/bootsplash/bootsplash.c
 create mode 100644 drivers/video/bootsplash/decode-jpg.c
 create mode 100644 drivers/video/bootsplash/decode-jpg.h
 create mode 100644 drivers/video/bootsplash/render.c
 create mode 100644 include/linux/bootsplash.h

--- a/drivers/tty/n_tty.c
+++ b/drivers/tty/n_tty.c
@@ -50,6 +50,7 @@
 #include <linux/uaccess.h>
 #include <linux/module.h>
 
+#include <linux/bootsplash.h>
 
 /* number of characters left in xmit buffer before select has we have room */
 #define WAKEUP_CHARS 256
@@ -1795,6 +1796,15 @@ do_it_again:
 			tty->minimum_to_wake = (minimum - (b - buf));
 
 		if (!input_available_p(tty, 0)) {
+			dev_t i_rdev = file->f_dentry->d_inode->i_rdev;
+
+			if (i_rdev == MKDEV(TTY_MAJOR, 0) ||
+			    i_rdev == MKDEV(TTY_MAJOR, 1) ||
+			    i_rdev == MKDEV(TTYAUX_MAJOR, 0) ||
+			    i_rdev == MKDEV(TTYAUX_MAJOR, 1)) {
+				SPLASH_VERBOSE();
+			}
+
 			if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
 				retval = -EIO;
 				break;
--- a/drivers/tty/vt/keyboard.c
+++ b/drivers/tty/vt/keyboard.c
@@ -45,6 +45,8 @@
 
 #include <asm/irq_regs.h>
 
+#include <linux/bootsplash.h>
+
 extern void ctrl_alt_del(void);
 
 /*
@@ -1280,6 +1282,13 @@ static void kbd_keycode(unsigned int key
 				pr_warning("can't emulate rawmode for keycode %d\n",
 					   keycode);
 
+	/* This code has to be redone for some non-x86 platforms */
+	if (down == 1 && (keycode == 0x3c || keycode == 0x01)) {
+		/* F2 and ESC on PC keyboard */
+		if (splash_verbose())
+			return;
+	}
+
 #ifdef CONFIG_SPARC
 	if (keycode == KEY_A && sparc_l1_a_state) {
 		sparc_l1_a_state = false;
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -4209,6 +4209,35 @@ void vcs_scr_updated(struct vc_data *vc)
 	notify_update(vc);
 }
 
+#ifdef CONFIG_BOOTSPLASH
+void con_remap_def_color(struct vc_data *vc, int new_color)
+{
+	unsigned short *sbuf = screenpos(vc, 0, 1);
+	unsigned c, len = vc->vc_screenbuf_size >> 1;
+	unsigned int bits, old_color;
+
+	if (sbuf) {
+		old_color = vc->vc_def_color << 8;
+		new_color <<= 8;
+		while (len--) {
+			c = scr_readw(sbuf);
+			bits = (old_color ^ new_color) & 0xf000;
+			if (((c ^ old_color) & 0xf000) == 0)
+				scr_writew((c ^ bits), sbuf);
+			*sbuf ^= bits;
+			bits = (old_color ^ new_color) & 0x0f00;
+			if (((c ^ old_color) & 0x0f00) == 0)
+				scr_writew((c ^ bits), sbuf);
+			*sbuf ^= bits;
+			sbuf++;
+		}
+		new_color >>= 8;
+	}
+	vc->vc_def_color = vc->vc_color = new_color;
+	update_attr(vc);
+}
+#endif
+
 /*
  *	Visible symbols for modules
  */
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2424,6 +2424,10 @@ if FB || SGI_NEWPORT_CONSOLE
 	source "drivers/video/logo/Kconfig"
 endif
 
+if FB
+	source "drivers/video/bootsplash/Kconfig"
+endif
+
 config FB_SH_MOBILE_MERAM
 	tristate "SuperH Mobile MERAM read ahead support"
 	depends on (SUPERH || ARCH_SHMOBILE)
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -14,6 +14,7 @@ fb-objs                           := $(f
 obj-$(CONFIG_VT)		  += console/
 obj-$(CONFIG_LOGO)		  += logo/
 obj-y				  += backlight/
+obj-$(CONFIG_BOOTSPLASH)	  += bootsplash/
 
 obj-$(CONFIG_EXYNOS_VIDEO)     += exynos/
 
--- /dev/null
+++ b/drivers/video/bootsplash/Kconfig
@@ -0,0 +1,17 @@
+#
+# Bootsplash configuration
+#
+
+menu "Bootsplash configuration"
+
+config BOOTSPLASH
+	bool "Bootup splash screen"
+	depends on FRAMEBUFFER_CONSOLE && FB_VESA
+	default n
+        ---help---
+          This option enables the Linux bootsplash screen. For more
+          information on the bootsplash screen have a look at
+          http://www.bootsplash.org/.
+          If you are unsure, say N
+endmenu
+
--- /dev/null
+++ b/drivers/video/bootsplash/Makefile
@@ -0,0 +1,5 @@
+# Makefile for the Linux bootsplash
+
+obj-$(CONFIG_BOOTSPLASH)		+= bootsplash.o
+obj-$(CONFIG_BOOTSPLASH)		+= decode-jpg.o
+obj-$(CONFIG_BOOTSPLASH)		+= render.o
--- /dev/null
+++ b/drivers/video/bootsplash/bootsplash.c
@@ -0,0 +1,2492 @@
+/*
+ *           linux/drivers/video/bootsplash/bootsplash.c -
+ *                 splash screen handling functions.
+ *
+ *	(w) 2001-2004 by Volker Poplawski, <volker@poplawski.de>,
+ *			Stefan Reinauer, <stepan@suse.de>,
+ *			Steffen Winterfeldt, <snwint@suse.de>,
+ *			Michael Schroeder <mls@suse.de>
+ *	    2009-2011 Egbert Eich <eich@suse.de>
+ *
+ *        Ideas & SuSE screen work by Ken Wimer, <wimer@suse.de>
+ *
+ *  For more information on this code check http://www.bootsplash.org/
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/vmalloc.h>
+#include <linux/unistd.h>
+#include <linux/syscalls.h>
+#include <linux/console.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+#include <asm/irq.h>
+
+#include "../console/fbcon.h"
+#include <linux/bootsplash.h>
+#include "decode-jpg.h"
+
+#ifndef DEBUG
+# define SPLASH_DEBUG(fmt, args...)
+#else
+# define SPLASH_DEBUG(fmt, args...) \
+	printk(KERN_WARNING "%s: " fmt "\n", __func__, ##args)
+#endif
+extern signed char con2fb_map[MAX_NR_CONSOLES];
+
+#define SPLASH_VERSION "3.2.0-2010/03/31"
+
+/* These errors have to match fbcon-jpegdec.h */
+static unsigned char *jpg_errors[] = {
+	"no SOI found",
+	"not 8 bit",
+	"height mismatch",
+	"width mismatch",
+	"bad width or height",
+	"too many COMPPs",
+	"illegal HV",
+	"quant table selector",
+	"picture is not YCBCR 221111",
+	"unknow CID in scan",
+	"dct not sequential",
+	"wrong marker",
+	"no EOI",
+	"bad tables",
+	"depth mismatch",
+	"scale error",
+	"out of memory"
+};
+
+static int splash_usesilent;
+static unsigned long splash_default = 0xf01;
+
+static int jpeg_get(unsigned char *buf, unsigned char *pic,
+		    int width, int height, enum splash_color_format cf,
+		    struct jpeg_decdata *decdata);
+static int splash_look_for_jpeg(struct vc_data *vc, int width, int height);
+
+static int __init splash_setup(char *options)
+{
+	splash_usesilent = 0;
+
+	if (!strncmp("silent", options, 6)) {
+		printk(KERN_INFO "bootsplash: silent mode.\n");
+		splash_usesilent = 1;
+		/* skip "silent," */
+		if (strlen(options) == 6)
+			return 0;
+		options += 7;
+	}
+	if (!strncmp("verbose", options, 7)) {
+		printk(KERN_INFO "bootsplash: verbose mode.\n");
+		splash_usesilent = 0;
+		if (strlen(options) == 7)
+			return 0;
+		options += 8;
+	}
+	if (strict_strtoul(options, 0, &splash_default) == -EINVAL)
+		splash_default = 0;
+
+	return 0;
+}
+
+__setup("splash=", splash_setup);
+
+
+static int splash_hasinter(unsigned char *buf, int num)
+{
+	unsigned char *bufend = buf + num * 12;
+	while (buf < bufend) {
+		if (buf[1] > 127)		/* inter? */
+			return 1;
+		buf += buf[3] > 127 ? 24 : 12;	/* blend? */
+	}
+	return 0;
+}
+
+static int boxextract(unsigned char *buf, unsigned short *dp,
+		      unsigned char *cols, int *blendp)
+{
+	dp[0] = buf[0] | buf[1] << 8;
+	dp[1] = buf[2] | buf[3] << 8;
+	dp[2] = buf[4] | buf[5] << 8;
+	dp[3] = buf[6] | buf[7] << 8;
+	*(unsigned int *)(cols + 0) =
+		*(unsigned int *)(cols + 4) =
+		*(unsigned int *)(cols + 8) =
+		*(unsigned int *)(cols + 12) = *(unsigned int *)(buf + 8);
+	if (dp[1] > 32767) {
+		dp[1] = ~dp[1];
+		*(unsigned int *)(cols + 4) = *(unsigned int *)(buf + 12);
+		*(unsigned int *)(cols + 8) = *(unsigned int *)(buf + 16);
+		*(unsigned int *)(cols + 12) = *(unsigned int *)(buf + 20);
+		*blendp = 1;
+		return 24;
+	}
+	return 12;
+}
+
+static void boxit(unsigned char *pic, int bytes, unsigned char *buf, int num,
+		  int percent, int xoff, int yoff, int overpaint,
+		  enum splash_color_format cf)
+{
+	int x, y, p, doblend, r, g, b, a, add;
+	unsigned int i = 0;
+	unsigned short data1[4];
+	unsigned char cols1[16];
+	unsigned short data2[4];
+	unsigned char cols2[16];
+	unsigned char *bufend;
+	union pt picp;
+	unsigned int stipple[32], sti, stin, stinn, stixs, stixe, stiys, stiye;
+	int xs, xe, ys, ye, xo, yo;
+	int octpp = splash_octpp(cf);
+
+	SPLASH_DEBUG();
+	if (num == 0 || percent < -1)
+		return;
+	bufend = buf + num * 12;
+	stipple[0] = 0xffffffff;
+	stin = 1;
+	stinn = 0;
+	stixs = stixe = 0;
+	stiys = stiye = 0;
+	while (buf < bufend) {
+		doblend = 0;
+		buf += boxextract(buf, data1, cols1, &doblend);
+		if (data1[0] == 32767 && data1[1] == 32767) {
+			/* box stipple */
+			if (stinn == 32)
+				continue;
+			if (stinn == 0) {
+				stixs = data1[2];
+				stixe = data1[3];
+				stiys = stiye = 0;
+			} else if (stinn == 4) {
+				stiys = data1[2];
+				stiye = data1[3];
+			}
+			stipple[stinn++] = (cols1[0]  << 24) |
+				(cols1[1]  << 16) |
+				(cols1[2]  << 8)  |
+				cols1[3] ;
+			stipple[stinn++] = (cols1[4]  << 24) |
+				(cols1[5]  << 16) |
+				(cols1[6]  << 8)  |
+				cols1[7] ;
+			stipple[stinn++] = (cols1[8]  << 24) |
+				(cols1[9]  << 16) |
+				(cols1[10] << 8)  |
+				cols1[11] ;
+			stipple[stinn++] = (cols1[12] << 24) |
+				(cols1[13] << 16) |
+				(cols1[14] << 8)  |
+				cols1[15] ;
+			stin = stinn;
+			continue;
+		}
+		stinn = 0;
+		if (data1[0] > 32767)
+			buf += boxextract(buf, data2, cols2, &doblend);
+		if (data1[0] == 32767 && data1[1] == 32766) {
+			/* box copy */
+			i = 12 * (short)data1[3];
+			doblend = 0;
+			i += boxextract(buf + i, data1, cols1, &doblend);
+			if (data1[0] > 32767)
+				boxextract(buf + i, data2, cols2, &doblend);
+		}
+		if (data1[0] == 32767)
+			continue;
+		if (data1[2] > 32767) {
+			if (overpaint)
+				continue;
+			data1[2] = ~data1[2];
+		}
+		if (data1[3] > 32767) {
+			if (percent == 65536)
+				continue;
+			data1[3] = ~data1[3];
+		}
+		if (data1[0] > 32767) {
+			if (percent < 0)
+				continue;
+			data1[0] = ~data1[0];
+			for (i = 0; i < 4; i++)
+				data1[i] = (data1[i] * (65536 - percent)
+					    + data2[i] * percent) >> 16;
+			for (i = 0; i < 16; i++)
+				cols1[i] = (cols1[i] * (65536 - percent)
+					    + cols2[i] * percent) >> 16;
+		}
+		*(unsigned int *)cols2 = *(unsigned int *)cols1;
+		a = cols2[3];
+		if (a == 0 && !doblend)
+			continue;
+
+		if (stixs >= 32768) {
+			xo = xs = (stixs ^ 65535) + data1[0];
+			xe = stixe ? stixe + data1[0] : data1[2];
+		} else if (stixe >= 32768) {
+			xs = stixs ? data1[2] - stixs : data1[0];
+			xe = data1[2] - (stixe ^ 65535);
+			xo = xe + 1;
+		} else {
+			xo = xs = stixs;
+			xe = stixe ? stixe : data1[2];
+		}
+		if (stiys >= 32768) {
+			yo = ys = (stiys ^ 65535) + data1[1];
+			ye = stiye ? stiye + data1[1] : data1[3];
+		} else if (stiye >= 32768) {
+			ys = stiys ? data1[3] - stiys : data1[1];
+			ye = data1[3] - (stiye ^ 65535);
+			yo = ye + 1;
+		} else {
+			yo = ys = stiys;
+			ye = stiye ? stiye : data1[3];
+		}
+		xo = 32 - (xo & 31);
+		yo = stin - (yo % stin);
+		if (xs < data1[0])
+			xs = data1[0];
+		if (xe > data1[2])
+			xe = data1[2];
+		if (ys < data1[1])
+			ys = data1[1];
+		if (ye > data1[3])
+			ye = data1[3];
+
+		for (y = ys; y <= ye; y++) {
+			sti = stipple[(y + yo) % stin];
+			x = (xs + xo) & 31;
+			if (x)
+				sti = (sti << x) | (sti >> (32 - x));
+			if (doblend) {
+				p = data1[3] - data1[1];
+				if (p != 0)
+					p = ((y - data1[1]) << 16) / p;
+				for (i = 0; i < 8; i++)
+					cols2[i + 8] = (cols1[i] * (65536 - p)
+							+ cols1[i + 8] * p)
+						>> 16;
+			}
+			add = (xs & 1);
+			add ^= (add ^ y) & 1 ? 1 : 3; /*2x2 ordered dithering*/
+			picp.ub = (pic + (xs + xoff) * octpp
+				   + (y + yoff) * bytes);
+			for (x = xs; x <= xe; x++) {
+				if (!(sti & 0x80000000)) {
+					sti <<= 1;
+					switch (octpp) {
+					case 2:
+						picp.us++;
+						break;
+					case 3:
+						picp.ub += 3;
+						break;
+					case 4:
+						picp.ul++;
+						break;
+					}
+					add ^= 3;
+					continue;
+				}
+				sti = (sti << 1) | 1;
+				if (doblend) {
+					p = data1[2] - data1[0];
+					if (p != 0)
+						p = ((x - data1[0]) << 16) / p;
+					for (i = 0; i < 4; i++)
+						cols2[i] = (cols2[i + 8] * (65536 - p)
+							    + cols2[i + 12] * p)
+							>> 16;
+					a = cols2[3];
+				}
+				r = cols2[0];
+				g = cols2[1];
+				b = cols2[2];
+#define CLAMP(x) ((x) >= 256 ? 255 : (x))
+#define BLEND(x, v, a) ((x * (255 - a) + v * a) / 255)
+				switch (cf) {
+				case SPLASH_DEPTH_15:
+					if (a != 255) {
+						i = *picp.us;
+						r = BLEND((i>>7 & 0xf8), r, a);
+						g = BLEND((i>>2 & 0xf8), g, a);
+						b = BLEND((i<<3 & 0xf8), b, a);
+					}
+					r += add * 2 + 1;
+					g += add;
+					b += add * 2 + 1;
+					i =     ((CLAMP(r) & 0xf8) <<  7) |
+						((CLAMP(g) & 0xf8) <<  2) |
+						((CLAMP(b))        >>  3);
+					*(picp.us++) = i;
+					break;
+				case SPLASH_DEPTH_16:
+					if (a != 255) {
+						i = *picp.us;
+						r = BLEND((i>>8 & 0xf8), r, a);
+						g = BLEND((i>>3 & 0xfc), g, a);
+						b = BLEND((i<<3 & 0xf8), b, a);
+					}
+					r += add * 2 + 1;
+					g += add;
+					b += add * 2 + 1;
+						i = ((CLAMP(r) & 0xf8) <<  8) |
+						    ((CLAMP(g) & 0xfc) <<  3) |
+						    ((CLAMP(b))        >>  3);
+					*(picp.us++) = i;
+					break;
+				case SPLASH_DEPTH_24_PACKED:
+					if (a != 255) {
+						i = *picp.ub;
+						r = BLEND((i & 0xff), r, a);
+						i = *(picp.ub + 1);
+						g = BLEND((i & 0xff), g, a);
+						i = *(picp.ub + 2);
+						b = BLEND((i & 0xff), b, a);
+					}
+					*(picp.ub++) = CLAMP(r);
+					*(picp.ub++) = CLAMP(g);
+					*(picp.ub++) = CLAMP(b);
+					break;
+				case SPLASH_DEPTH_24:
+					if (a != 255) {
+						i = *picp.ul;
+						r = BLEND((i>>16 & 0xff), r, a);
+						g = BLEND((i>>8  & 0xff), g, a);
+						b = BLEND((i     & 0xff), b, a);
+					}
+					i = ((CLAMP(r) << 16)
+					     | (CLAMP(g) << 8)
+					     | (CLAMP(b)));
+					*(picp.ul++) = i;
+					break;
+				default:
+					break;
+				}
+				add ^= 3;
+			}
+		}
+	}
+}
+
+static void box_offsets(unsigned char *buf, int num,
+			int screen_w, int screen_h, int pic_w, int pic_h,
+			int *x_off, int *y_off)
+{
+	int a, doblend;
+	int x_min = pic_w, x_max = 0;
+	int y_min = pic_h, y_max = 0;
+	unsigned int i = 0;
+	unsigned short data1[4];
+	unsigned char cols1[16];
+	unsigned short data2[4];
+	unsigned char cols2[16];
+	unsigned char *bufend;
+	unsigned int stin, stinn, stixs, stixe, stiys, stiye;
+	int xs, xe, ys, ye;
+
+	SPLASH_DEBUG();
+
+	if ((screen_w == pic_w && screen_h == pic_h) || num == 0)
+		*x_off = *y_off = 0;
+
+	bufend = buf + num * 12;
+	stin = 1;
+	stinn = 0;
+	stixs = stixe = 0;
+	stiys = stiye = 0;
+
+	while (buf < bufend) {
+		doblend = 0;
+		buf += boxextract(buf, data1, cols1, &doblend);
+		if (data1[0] == 32767 && data1[1] == 32767) {
+			/* box stipple */
+			if (stinn == 32)
+				continue;
+			if (stinn == 0) {
+				stixs = data1[2];
+				stixe = data1[3];
+				stiys = stiye = 0;
+			} else if (stinn == 4) {
+				stiys = data1[2];
+				stiye = data1[3];
+			}
+			stin = stinn;
+			continue;
+		}
+		stinn = 0;
+		if (data1[0] > 32767)
+			buf += boxextract(buf, data2, cols2, &doblend);
+		if (data1[0] == 32767 && data1[1] == 32766) {
+			/* box copy */
+			i = 12 * (short)data1[3];
+			doblend = 0;
+			i += boxextract(buf + i, data1, cols1, &doblend);
+			if (data1[0] > 32767)
+				boxextract(buf + i, data2, cols2, &doblend);
+		}
+		if (data1[0] == 32767)
+			continue;
+		if (data1[2] > 32767)
+			data1[2] = ~data1[2];
+		if (data1[3] > 32767)
+			data1[3] = ~data1[3];
+		if (data1[0] > 32767) {
+			data1[0] = ~data1[0];
+			for (i = 0; i < 4; i++)
+				data1[i] = (data1[i] * (65536 - 1)
+					    + data2[i] * 1) >> 16;
+		}
+		*(unsigned int *)cols2 = *(unsigned int *)cols1;
+		a = cols2[3];
+		if (a == 0 && !doblend)
+			continue;
+
+		if (stixs >= 32768) {
+			xs = (stixs ^ 65535) + data1[0];
+			xe = stixe ? stixe + data1[0] : data1[2];
+		} else if (stixe >= 32768) {
+			xs = stixs ? data1[2] - stixs : data1[0];
+			xe = data1[2] - (stixe ^ 65535);
+		} else {
+			xs = stixs;
+			xe = stixe ? stixe : data1[2];
+		}
+		if (stiys >= 32768) {
+			ys = (stiys ^ 65535) + data1[1];
+			ye = stiye ? stiye + data1[1] : data1[3];
+		} else if (stiye >= 32768) {
+			ys = stiys ? data1[3] - stiys : data1[1];
+			ye = data1[3] - (stiye ^ 65535);
+		} else {
+			ys = stiys;
+			ye = stiye ? stiye : data1[3];
+		}
+		if (xs < data1[0])
+			xs = data1[0];
+		if (xe > data1[2])
+			xe = data1[2];
+		if (ys < data1[1])
+			ys = data1[1];
+		if (ye > data1[3])
+			ye = data1[3];
+
+		if (xs < x_min)
+			x_min = xs;
+		if (xe > x_max)
+			x_max = xe;
+		if (ys < y_min)
+			y_min = ys;
+		if (ye > y_max)
+			y_max = ye;
+	}
+	{
+		int x_center = (x_min + x_max) / 2;
+		int y_center = (y_min + y_max) / 2;
+
+		if (screen_w == pic_w)
+			*x_off = 0;
+		else {
+			if (x_center < (pic_w + pic_w / 5) >> 1 &&
+			    x_center > (pic_w - pic_w / 5) >> 1) {
+				*x_off = (screen_w - pic_w) >> 1;
+			} else {
+				int x = x_center * screen_w / pic_w;
+				*x_off = x - x_center;
+				if (x_min + *x_off < 0)
+					*x_off = 0;
+				if (x_max + *x_off > screen_w)
+					*x_off = screen_w - pic_w;
+			}
+		}
+		if (screen_h == pic_h)
+			*y_off = 0;
+		else {
+			if (y_center < (pic_h + pic_h / 5) >> 1 &&
+			    y_center > (pic_h - pic_h / 5) >> 1)
+				*y_off = (screen_h - pic_h) >> 1;
+			else {
+				int x = y_center * screen_h / pic_h;
+				*y_off = x - y_center;
+				if (y_min + *y_off < 0)
+					*y_off = 0;
+				if (y_max + *x_off > screen_h)
+					*y_off = screen_h - pic_h;
+			}
+		}
+	}
+}
+
+static int splash_check_jpeg(unsigned char *jpeg,
+			     int width, int height)
+{
+	int size, err;
+	unsigned char *mem;
+	struct jpeg_decdata *decdata; /* private decoder data */
+
+
+	size = ((width + 15) & ~15) * ((height + 15) & ~15) * 2;
+	mem = vmalloc(size);
+	if (!mem) {
+		printk(KERN_INFO "bootsplash: no memory for decoded picture.\n");
+		return -1;
+	}
+	decdata = vmalloc(sizeof(*decdata));
+	if (!decdata) {
+		printk(KERN_INFO "bootsplash: not enough memory.\n");
+		vfree(mem);
+		return -1;
+	}
+	/* test decode: use fixed depth of 16 */
+	err = jpeg_decode(jpeg, mem,
+			  ((width + 15) & ~15), ((height + 15) & ~15),
+			  SPLASH_DEPTH_16,
+			  decdata);
+	if (err)
+		printk(KERN_INFO "bootsplash: "
+		       "error while decompressing picture: %s (%d)\n",
+		       jpg_errors[err - 1], err);
+	vfree(decdata);
+	vfree(mem);
+	return err ? -1 : 0;
+}
+
+static void splash_free(struct vc_data *vc, struct fb_info *info)
+{
+	struct splash_data *sd;
+	struct splash_data *next;
+	SPLASH_DEBUG();
+	for (sd = vc->vc_splash_data; sd; sd = next) {
+		next = sd->next;
+		sd->pic->ref_cnt--;
+		if (!sd->pic->ref_cnt) {
+			vfree(sd->pic->splash_pic);
+			vfree(sd->pic);
+		}
+		sd->imgd->ref_cnt--;
+		if (!sd->imgd->ref_cnt) {
+			vfree(sd->imgd->splash_sboxes);
+			vfree(sd->imgd);
+		}
+		vfree(sd);
+	}
+	vc->vc_splash_data = 0;
+	if (info)
+		info->splash_data = 0;
+}
+
+static int splash_mkpenguin(struct splash_data *data,
+			    int pxo, int pyo, int pwi, int phe,
+			    int pr, int pg, int pb)
+{
+	unsigned char *buf;
+	int i;
+
+	if (pwi == 0 || phe == 0)
+		return 0;
+
+	buf = (unsigned char *)data + sizeof(*data);
+
+	pwi += pxo - 1;
+	phe += pyo - 1;
+
+	*buf++ = pxo;
+	*buf++ = pxo >> 8;
+	*buf++ = pyo;
+	*buf++ = pyo >> 8;
+	*buf++ = pwi;
+	*buf++ = pwi >> 8;
+	*buf++ = phe;
+	*buf++ = phe >> 8;
+	*buf++ = pr;
+	*buf++ = pg;
+	*buf++ = pb;
+	*buf++ = 0;
+
+	for (i = 0; i < 12; i++, buf++)
+		*buf = buf[-12];
+
+	buf[-24] ^= 0xff;
+	buf[-23] ^= 0xff;
+	buf[-1] = 0xff;
+
+	return 2;
+}
+
+static const int splash_offsets[3][16] = {
+    /* len, unit, size, state, fgcol, col, xo, yo, wi, he
+       boxcnt, ssize, sboxcnt, percent, overok, palcnt */
+    /* V1 */
+	{   20,   -1,   16,    -1,    -1,  -1,  8, 10, 12, 14,
+	    -1,    -1,      -1,      -1,     -1,     -1 },
+	/* V2 */
+	{   35,    8,   12,     9,    10,  11, 16, 18, 20, 22,
+	    -1,    -1,      -1,      -1,     -1,     -1 },
+	/* V3 */
+	{   38,    8,   12,     9,    10,  11, 16, 18, 20, 22,
+	    24,    28,      32,      34,     36,     37 },
+};
+
+#define SPLASH_OFF_LEN     offsets[0]
+#define SPLASH_OFF_UNIT    offsets[1]
+#define SPLASH_OFF_SIZE    offsets[2]
+#define SPLASH_OFF_STATE   offsets[3]
+#define SPLASH_OFF_FGCOL   offsets[4]
+#define SPLASH_OFF_COL     offsets[5]
+#define SPLASH_OFF_XO      offsets[6]
+#define SPLASH_OFF_YO      offsets[7]
+#define SPLASH_OFF_WI      offsets[8]
+#define SPLASH_OFF_HE      offsets[9]
+#define SPLASH_OFF_BOXCNT  offsets[10]
+#define SPLASH_OFF_SSIZE   offsets[11]
+#define SPLASH_OFF_SBOXCNT offsets[12]
+#define SPLASH_OFF_PERCENT offsets[13]
+#define SPLASH_OFF_OVEROK  offsets[14]
+#define SPLASH_OFF_PALCNT  offsets[15]
+
+static inline int splash_getb(unsigned char *pos, int off)
+{
+	return off == -1 ? 0 : pos[off];
+}
+
+static inline int splash_gets(unsigned char *pos, int off)
+{
+	return off == -1 ? 0 : pos[off] | pos[off + 1] << 8;
+}
+
+static inline int splash_geti(unsigned char *pos, int off)
+{
+	return off == -1 ? 0 : (pos[off] |
+				pos[off + 1] << 8 |
+				pos[off + 2] << 16 |
+				pos[off + 3] << 24);
+}
+
+/* move the given splash_data to the current one */
+static void splash_pivot_current(struct vc_data *vc, struct splash_data *new)
+{
+	struct splash_data *sd;
+	struct splash_pic_data *pic;
+	int state, percent, silent;
+
+	sd = vc->vc_splash_data;
+	if (!sd || sd == new)
+		return;
+
+	state = sd->splash_state;
+	percent = sd->splash_percent;
+	silent = sd->splash_dosilent;
+	if (sd->pic->ref_cnt > 1) {
+		pic = kzalloc(sizeof(struct splash_pic_data), GFP_KERNEL);
+		if (!pic)
+			return;
+		sd->pic = pic;
+	}
+	sd->pic->ref_cnt = 1;
+	sd->pic->splash_pic_size = 0;
+	sd->pic->splash_pic = NULL;
+	sd->splash_vc_text_wi = sd->imgd->splash_text_wi;
+	sd->splash_vc_text_he = sd->imgd->splash_text_he;
+	for (; sd->next; sd = sd->next) {
+		if (sd->next == new) {
+			sd->next = new->next;
+			new->next = vc->vc_splash_data;
+			vc->vc_splash_data = new;
+			/* copy the current states */
+			new->splash_state = state;
+			new->splash_percent = percent;
+			new->splash_dosilent = silent;
+			new->splash_vc_text_wi = new->imgd->splash_text_wi;
+			new->splash_vc_text_he = new->imgd->splash_text_he;
+
+			new->splash_boxes_xoff = 0;
+			new->splash_boxes_yoff = 0;
+			new->splash_sboxes_xoff = 0;
+			new->splash_sboxes_yoff = 0;
+
+			if (new->pic->ref_cnt > 1) {
+				struct splash_pic_data *pic;
+				pic = kzalloc(sizeof(struct splash_pic_data),
+					      GFP_KERNEL);
+				if (!pic)
+					return;
+
+				new->pic = pic;
+			}
+			new->pic->ref_cnt = 1;
+			new->pic->splash_pic_size = 0;
+			new->pic->splash_pic = NULL;
+
+			return;
+		}
+	}
+}
+
+static int update_boxes(struct vc_data *vc,
+	       const int *offsets,
+	       unsigned char *ndata, int len, unsigned char * end,
+	       int *update)
+{
+	int boxcnt;
+	int sboxcnt;
+	struct splash_data *sd;
+	struct splash_img_data *imgd;
+	int i;
+
+	sd = vc->vc_splash_data;
+	if (sd != 0) {
+		int up = 0;
+		imgd = sd->imgd;
+		i = splash_getb(ndata, SPLASH_OFF_STATE);
+		if (i != 255) {
+			sd->splash_state = i; /*@!@*/
+			up = -1;
+		}
+		i = splash_getb(ndata, SPLASH_OFF_FGCOL);
+		if (i != 255) {
+			imgd->splash_fg_color = i;
+			up = -1;
+		}
+		i = splash_getb(ndata, SPLASH_OFF_COL);
+		if (i != 255) {
+			imgd->splash_color = i;
+			up = -1;
+		}
+		boxcnt = sboxcnt = 0;
+		if (ndata + len <= end) {
+			boxcnt = splash_gets(ndata, SPLASH_OFF_BOXCNT);
+			sboxcnt = splash_gets(ndata, SPLASH_OFF_SBOXCNT);
+		}
+		if (boxcnt) {
+			i = splash_gets(ndata, len);
+			if (boxcnt + i
+			    <= imgd->splash_boxcount &&
+			    ndata + len + 2 + boxcnt * 12
+			    <= end) {
+				if (splash_geti(ndata, len + 2)
+				    != 0x7ffd7fff ||
+				    !memcmp(ndata + len + 2,
+					    imgd->splash_boxes + i * 12,
+					    8)) {
+					memcpy(imgd->splash_boxes + i * 12,
+					       ndata + len + 2,
+					       boxcnt * 12);
+					up |= 1;
+				}
+			}
+			len += boxcnt * 12 + 2;
+		}
+		if (sboxcnt) {
+			i = splash_gets(ndata, len);
+			if ((sboxcnt + i <= imgd->splash_sboxcount) &&
+			    (ndata + len + 2 + sboxcnt * 12 <= end)) {
+				if ((splash_geti(ndata, len + 2) != 0x7ffd7fff)
+				    || !memcmp(ndata + len + 2,
+					       imgd->splash_sboxes + i * 12,
+					       8)) {
+					memcpy(imgd->splash_sboxes + i * 12,
+					       ndata + len + 2,
+					       sboxcnt * 12);
+					up |= 2;
+				}
+			}
+		}
+		if (update)
+			*update = up;
+	}
+	return 0;
+}
+
+static int splash_getraw(unsigned char *start, unsigned char *end, int *update)
+{
+	unsigned char *ndata;
+	int version;
+	int splash_size;
+	int unit;
+	int width, height;
+	int silentsize;
+	int boxcnt;
+	int sboxcnt;
+	int palcnt;
+	int len;
+	const int *offsets;
+	struct vc_data *vc = NULL;
+	struct fb_info *info = NULL;
+	struct splash_data *sd;
+	struct splash_img_data *imgd;
+	struct splash_pic_data *pic;
+	struct splash_data *splash_found = NULL;
+	int unit_found = -1;
+	int oldpercent, oldsilent;
+
+	if (update)
+		*update = -1;
+
+	if (!update ||
+	    start[7] < '2' ||
+	    start[7] > '3' ||
+	    splash_geti(start, 12) != (int)0xffffffff)
+		printk(KERN_INFO "bootsplash %s: looking for picture...\n",
+		       SPLASH_VERSION);
+
+	oldpercent = -3;
+	oldsilent = -1;
+	for (ndata = start; ndata < end; ndata++) {
+		if (ndata[0] != 'B' ||
+		    ndata[1] != 'O' ||
+		    ndata[2] != 'O' ||
+		    ndata[3] != 'T')
+			continue;
+		if (ndata[4] != 'S' ||
+		    ndata[5] != 'P' ||
+		    ndata[6] != 'L' ||
+		    ndata[7]  < '1' ||
+		    ndata[7]  > '3')
+			continue;
+
+		version = ndata[7] - '0';
+		offsets = splash_offsets[version - 1];
+		len = SPLASH_OFF_LEN;
+
+		unit = splash_getb(ndata, SPLASH_OFF_UNIT);
+		if (unit >= MAX_NR_CONSOLES)
+			continue;
+
+		if (unit)
+			vc_allocate(unit);
+
+		vc = vc_cons[unit].d;
+		if (!vc)
+			continue;
+
+		info = registered_fb[(int)con2fb_map[unit]];
+
+		splash_size = splash_geti(ndata, SPLASH_OFF_SIZE);
+
+		/*
+		 * Update. Wonder what should happen here now
+		 * since we can have multiple splash_data records
+		 */
+		if (splash_size == (int)0xffffffff && version > 1) {
+			if (update_boxes(vc, offsets, ndata, len, end, update) < 0)
+				return -1;
+
+			return unit;
+		}
+
+		if (splash_size == 0) {
+			printk(KERN_INFO
+			       "bootsplash: ...found, freeing memory.\n");
+			if (vc->vc_splash_data)
+				splash_free(vc, info);
+			return unit;
+		}
+		boxcnt = splash_gets(ndata, SPLASH_OFF_BOXCNT);
+		palcnt = 3 * splash_getb(ndata, SPLASH_OFF_PALCNT);
+		if (ndata + len + splash_size > end) {
+			printk(KERN_ERR
+			       "bootsplash: ...found, but truncated!\n");
+			return -1;
+		}
+		silentsize = splash_geti(ndata, SPLASH_OFF_SSIZE);
+		if (silentsize)
+			printk(KERN_INFO
+			       "bootsplash: silentjpeg size %d bytes\n",
+			       silentsize);
+		if (silentsize >= splash_size) {
+			printk(KERN_ERR "bootsplash: bigger than splashsize!\n");
+			return -1;
+		}
+		splash_size -= silentsize;
+		if (!splash_usesilent)
+			silentsize = 0;
+
+		sboxcnt = splash_gets(ndata, SPLASH_OFF_SBOXCNT);
+		if (vc->vc_splash_data) {
+			oldpercent = vc->vc_splash_data->splash_percent;/*@!@*/
+			oldsilent = vc->vc_splash_data->splash_dosilent;/*@!@*/
+		}
+		sd = kzalloc(sizeof(*sd), GFP_KERNEL);
+		if (!sd)
+			break;
+		imgd = vmalloc(sizeof(*imgd)
+			       + splash_size + (version < 3 ? 2 * 12 : 0));
+		if (!imgd) {
+			vfree(sd);
+			break;
+		}
+		pic = kzalloc(sizeof(*pic), GFP_KERNEL);
+		if (!pic) {
+			vfree(sd);
+			vfree(pic);
+			break;
+		}
+		memset(imgd, 0, sizeof(*imgd));
+		sd->imgd = imgd;
+		sd->pic = pic;
+		imgd->ref_cnt = 1;
+		pic->ref_cnt = 1;
+		jpeg_get_size(ndata + len + boxcnt * 12 + palcnt,
+			      &imgd->splash_width, &imgd->splash_height);
+		if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt,
+				      imgd->splash_width,
+				      imgd->splash_height)) {
+			ndata += len + splash_size - 1;
+			vfree(imgd);
+			vfree(sd);
+			continue;
+		}
+		if (silentsize) {
+			imgd->splash_silentjpeg = vmalloc(silentsize);
+			if (imgd->splash_silentjpeg) {
+				memcpy(imgd->splash_silentjpeg,
+				       ndata + len + splash_size, silentsize);
+				imgd->splash_sboxes = imgd->splash_silentjpeg;
+				imgd->splash_silentjpeg += 12 * sboxcnt;
+				imgd->splash_sboxcount = sboxcnt;
+			}
+		}
+		imgd->splash_fg_color = splash_getb(ndata, SPLASH_OFF_FGCOL);
+		imgd->splash_color = splash_getb(ndata, SPLASH_OFF_COL);
+		imgd->splash_overpaintok = splash_getb(ndata, SPLASH_OFF_OVEROK);
+		imgd->splash_text_xo = splash_gets(ndata, SPLASH_OFF_XO);
+		imgd->splash_text_yo = splash_gets(ndata, SPLASH_OFF_YO);
+		imgd->splash_text_wi = splash_gets(ndata, SPLASH_OFF_WI);
+		imgd->splash_text_he = splash_gets(ndata, SPLASH_OFF_HE);
+		if (version == 1) {
+			imgd->splash_text_xo *= 8;
+			imgd->splash_text_wi *= 8;
+			imgd->splash_text_yo *= 16;
+			imgd->splash_text_he *= 16;
+			imgd->splash_color    = (splash_default >> 8) & 0x0f;
+			imgd->splash_fg_color = (splash_default >> 4) & 0x0f;
+		}
+
+		/* fake penguin box for older formats */
+		if (version == 1)
+			boxcnt = splash_mkpenguin(sd, imgd->splash_text_xo + 10,
+						  imgd->splash_text_yo + 10,
+						  imgd->splash_text_wi - 20,
+						  imgd->splash_text_he - 20,
+						  0xf0, 0xf0, 0xf0);
+		else if (version == 2)
+			boxcnt = splash_mkpenguin(sd,
+						  splash_gets(ndata, 24),
+						  splash_gets(ndata, 26),
+						  splash_gets(ndata, 28),
+						  splash_gets(ndata, 30),
+						  splash_getb(ndata, 32),
+						  splash_getb(ndata, 33),
+						  splash_getb(ndata, 34));
+
+		memcpy((char *)imgd
+		       + sizeof(*imgd) + (version < 3 ? boxcnt * 12 : 0),
+		       ndata + len,
+		       splash_size);
+		imgd->splash_boxcount = boxcnt;
+		imgd->splash_boxes = (unsigned char *)imgd + sizeof(*imgd);
+		imgd->splash_palette = imgd->splash_boxes + boxcnt * 12;
+		imgd->splash_jpeg = imgd->splash_palette + palcnt;
+
+		sd->splash_state = splash_getb(ndata, SPLASH_OFF_STATE);/*@!@*/
+		sd->splash_percent = oldpercent == -3 ?
+			splash_gets(ndata, SPLASH_OFF_PERCENT) :
+			oldpercent; /*@!@*/
+		sd->pic->splash_pic = NULL;
+		sd->pic->splash_pic_size = 0;
+
+		sd->splash_dosilent = imgd->splash_silentjpeg != 0 ?
+			(oldsilent == -1 ? 1 : oldsilent) :
+			0; /* @!@ */
+
+		sd->splash_vc_text_wi = imgd->splash_text_wi;
+		sd->splash_vc_text_he = imgd->splash_text_he;
+
+		sd->next = vc->vc_splash_data;
+		vc->vc_splash_data = sd;
+
+		if (info) {
+			width = info->var.xres;
+			height = info->var.yres;
+			if (imgd->splash_width != width ||
+			    imgd->splash_height != height) {
+				ndata += len + splash_size - 1;
+				continue;
+			}
+		}
+		printk(KERN_INFO
+		       "bootsplash: ...found (%dx%d, %d bytes, v%d).\n",
+		       imgd->splash_width, imgd->splash_height,
+		       splash_size, version);
+		if (version == 1) {
+			printk(KERN_WARNING
+			       "bootsplash: Using deprecated v1 header. "
+			       "Updating your splash utility recommended.\n");
+			printk(KERN_INFO
+			       "bootsplash: Find the latest version at "
+		       "http://www.bootsplash.org/\n");
+		}
+
+		splash_found = sd;
+		unit_found = unit;
+	}
+
+	if (splash_found) {
+		splash_pivot_current(vc, splash_found);
+		return unit_found;
+	} else {
+		vc = vc_cons[0].d;
+		if (vc) {
+			info = registered_fb[(int)con2fb_map[0]];
+			if (info) {
+				width = info->var.xres;
+				height = info->var.yres;
+			} else
+				width = height = 0;
+			if (!splash_look_for_jpeg(vc, width, height))
+				return -1;
+			return 0;
+		}
+	}
+
+	printk(KERN_ERR "bootsplash: ...no good signature found.\n");
+	return -1;
+}
+
+static void splash_update_redraw(struct vc_data *vc, struct fb_info *info)
+{
+	update_region(vc,
+		      vc->vc_origin + vc->vc_size_row * vc->vc_top,
+		      vc->vc_size_row * (vc->vc_bottom - vc->vc_top) / 2);
+	splash_clear_margins(vc, info, 0);
+}
+
+int splash_do_verbose(void)
+{
+	struct vc_data *vc;
+	struct fb_info *info;
+	int ret = 0;
+
+	SPLASH_DEBUG();
+	if (!oops_in_progress)
+		console_lock();
+
+	if (!splash_usesilent)
+		goto done;
+
+	vc = vc_cons[0].d;
+
+	if (!vc || !vc->vc_splash_data || !vc->vc_splash_data->splash_state)
+		goto done;
+	if (!vc->vc_splash_data->imgd->splash_silentjpeg)
+		goto done;
+
+	if (!vc->vc_splash_data->splash_dosilent)
+		goto done;
+	vc->vc_splash_data->splash_dosilent = 0;
+	if (fg_console != vc->vc_num)
+		goto done;
+
+	info = registered_fb[(int)con2fb_map[0]];
+
+	if (!info || !info->splash_data)
+		goto done;
+
+	splash_update_redraw(vc, info);
+	ret = 0;
+
+ done:
+	if (!oops_in_progress)
+		console_unlock();
+
+	return ret;
+}
+
+static void splash_verbose_callback(struct work_struct *ignored)
+{
+	splash_do_verbose();
+}
+
+static DECLARE_WORK(splash_work, splash_verbose_callback);
+
+int splash_verbose(void)
+{
+	if (!oops_in_progress)
+		schedule_work(&splash_work);
+	else
+		return splash_do_verbose();
+	return 0;
+}
+
+static void splash_off(struct vc_data *vc, struct fb_info *info)
+{
+	int rows = info->var.xres / vc->vc_font.width;
+	int cols = info->var.yres / vc->vc_font.height;
+	SPLASH_DEBUG();
+
+	info->splash_data = 0;
+	if (rows != vc->vc_rows || cols != vc->vc_cols)
+		vc_resize(vc, rows, cols);
+}
+
+/* look for the splash with the matching size and set it as the current */
+static int splash_look_for_jpeg(struct vc_data *vc, int width, int height)
+{
+	struct splash_data *sd, *found = NULL;
+	int found_delta_x = INT_MAX, found_delta_y = INT_MAX;
+
+	for (sd = vc->vc_splash_data; sd; sd = sd->next) {
+		int delta_x = abs(sd->imgd->splash_width - width) * height;
+		int delta_y = abs(sd->imgd->splash_height - height) * width;
+		if (!found ||
+		    (found_delta_x + found_delta_y > delta_x + delta_y)) {
+			found = sd;
+			found_delta_x = delta_x;
+			found_delta_y = delta_y;
+		}
+	}
+
+	if (found) {
+		SPLASH_DEBUG("bootsplash: "
+			     "scalable image found (%dx%d scaled to %dx%d).",
+			     found->imgd->splash_width,
+			     found->imgd->splash_height,
+			     width, height);
+
+		splash_pivot_current(vc, found);
+
+		/* textarea margins are constant independent from image size */
+		if (found->imgd->splash_height != height)
+			found->splash_vc_text_he = height
+				- (found->imgd->splash_height
+				   - found->imgd->splash_text_he);
+		else
+			found->splash_vc_text_he = found->imgd->splash_text_he;
+		if (found->imgd->splash_width != width)
+			found->splash_vc_text_wi =
+				width
+				- (found->imgd->splash_width
+				   - found->imgd->splash_text_wi);
+		else
+			found->splash_vc_text_wi = found->imgd->splash_text_wi;
+
+		if (found->imgd->splash_width != width
+		    || found->imgd->splash_height != height) {
+			box_offsets(found->imgd->splash_boxes,
+				    found->imgd->splash_boxcount,
+				    width, height,
+				    found->imgd->splash_width,
+				    found->imgd->splash_height,
+				    &found->splash_boxes_xoff,
+				    &found->splash_boxes_yoff);
+			SPLASH_DEBUG("bootsplash: offsets for boxes: x=%d y=%d",
+				     found->splash_boxes_xoff,
+				     found->splash_boxes_yoff);
+
+			if (found->imgd->splash_sboxes) {
+				box_offsets(found->imgd->splash_sboxes,
+					    found->imgd->splash_sboxcount,
+					    width, height,
+					    found->imgd->splash_width,
+					    found->imgd->splash_height,
+					    &found->splash_sboxes_xoff,
+					    &found->splash_sboxes_yoff);
+				SPLASH_DEBUG("bootsplash: "
+					     "offsets sboxes: x=%d y=%d",
+					     found->splash_sboxes_xoff,
+					     found->splash_sboxes_yoff);
+			}
+		} else {
+			found->splash_sboxes_xoff = 0;
+			found->splash_sboxes_yoff = 0;
+		}
+		return 0;
+	}
+	return -1;
+}
+
+static int splash_recolor(struct vc_data *vc, struct fb_info *info)
+{
+	int color;
+
+	SPLASH_DEBUG();
+	if (!vc->vc_splash_data)
+		return -1;
+	if (!vc->vc_splash_data->splash_state)
+		return 0;
+	color = vc->vc_splash_data->imgd->splash_color << 4 |
+		vc->vc_splash_data->imgd->splash_fg_color;
+	if (vc->vc_def_color != color)
+		con_remap_def_color(vc, color);
+	if (info && info->splash_data && fg_console == vc->vc_num)
+		splash_update_redraw(vc, info);
+	vc->vc_splash_data->color_set = 1;
+	return 0;
+}
+
+int splash_prepare(struct vc_data *vc, struct fb_info *info)
+{
+	int err;
+	int width, height, octpp, size, sbytes;
+	enum splash_color_format cf = SPLASH_DEPTH_UNKNOWN;
+	int pic_update = 0;
+	struct jpeg_decdata *decdata; /* private decoder data */
+
+	SPLASH_DEBUG("vc_num: %i", vc->vc_num);
+
+#if 0 /* Nouveau fb sets a different ops, so we can't use the condition */
+	if (info->fbops->fb_imageblit != cfb_imageblit) {
+		printk(KERN_ERR "bootsplash: "
+		       "found, but framebuffer can't "
+		       "handle it!\n");
+		return -1;
+	}
+#endif
+
+	if (!vc->vc_splash_data || !vc->vc_splash_data->splash_state) {
+		splash_off(vc, info);
+		return -1;
+	}
+
+	width = info->var.xres;
+	height = info->var.yres;
+	switch (info->var.bits_per_pixel) {
+	case 16:
+		if ((info->var.red.length +
+		     info->var.green.length +
+		     info->var.blue.length) == 15)
+			cf = SPLASH_DEPTH_15;
+		else
+			cf = SPLASH_DEPTH_16;
+		break;
+	case 24:
+		cf = SPLASH_DEPTH_24_PACKED;
+		break;
+	case 32:
+		cf = SPLASH_DEPTH_24;
+		break;
+	}
+	if (cf == SPLASH_DEPTH_UNKNOWN) {
+	        printk(KERN_INFO "bootsplash: unsupported pixel format: %i\n",
+		       info->var.bits_per_pixel);
+		splash_off(vc, info);
+		return -2;
+	}
+	octpp = splash_octpp(cf);
+
+	if (splash_look_for_jpeg(vc, width, height) < 0) {
+		printk(KERN_INFO "bootsplash: no matching splash %dx%d\n",
+		       width, height);
+		splash_off(vc, info);
+		return -2;
+	}
+
+	sbytes = ((width + 15) & ~15) * octpp;
+	size = sbytes * ((height + 15) & ~15);
+
+	if (size != vc->vc_splash_data->pic->splash_pic_size) {
+		if (vc->vc_splash_data->pic->ref_cnt > 1) {
+			struct splash_pic_data *pic;
+			pic = kzalloc(sizeof(struct splash_pic_data),
+				      GFP_KERNEL);
+			if (!pic)
+				return -2;
+			vc->vc_splash_data->pic = pic;
+		}
+		vc->vc_splash_data->pic->ref_cnt = 1;
+		vc->vc_splash_data->pic->splash_pic = NULL;
+		vc->vc_splash_data->pic->splash_pic_size = 0;
+	}
+	if (!vc->vc_splash_data->pic->splash_pic) {
+		vc->vc_splash_data->pic->splash_pic = vmalloc(size);
+		pic_update = 1;
+	}
+	if (!vc->vc_splash_data->pic->splash_pic) {
+		printk(KERN_INFO "bootsplash: not enough memory.\n");
+		splash_off(vc, info);
+		return -3;
+	}
+
+	decdata = vmalloc(sizeof(*decdata));
+	if (!decdata) {
+		printk(KERN_INFO "bootsplash: not enough memory.\n");
+		splash_off(vc, info);
+		return -3;
+	}
+
+	if (vc->vc_splash_data->imgd->splash_silentjpeg &&
+	    vc->vc_splash_data->splash_dosilent) {
+		pic_update = 1;
+		err = jpeg_get(vc->vc_splash_data->imgd->splash_silentjpeg,
+			       vc->vc_splash_data->pic->splash_pic,
+			       width, height, cf, decdata);
+		if (err) {
+			printk(KERN_INFO "bootsplash: "
+			       "error while decompressing silent picture: "
+			       "%s (%d)\n",
+			       jpg_errors[err - 1], err);
+			vc->vc_splash_data->splash_dosilent = 0;
+		} else {
+			if (vc->vc_splash_data->imgd->splash_sboxcount)
+				boxit(vc->vc_splash_data->pic->splash_pic,
+				      sbytes,
+				      vc->vc_splash_data->imgd->splash_sboxes,
+				      vc->vc_splash_data->imgd->splash_sboxcount,
+				      vc->vc_splash_data->splash_percent,
+				      vc->vc_splash_data->splash_sboxes_xoff,
+				      vc->vc_splash_data->splash_sboxes_yoff,
+				      vc->vc_splash_data->splash_percent < 0 ?
+				      1 : 0,
+				      cf);
+			splashcopy(info->screen_base,
+				   vc->vc_splash_data->pic->splash_pic,
+				   info->var.yres,
+				   info->var.xres,
+				   info->fix.line_length, sbytes,
+				   octpp);
+		}
+	} else
+		vc->vc_splash_data->splash_dosilent = 0;
+
+	if (pic_update) {
+		err = jpeg_get(vc->vc_splash_data->imgd->splash_jpeg,
+			       vc->vc_splash_data->pic->splash_pic,
+			       width, height, cf, decdata);
+		if (err) {
+			printk(KERN_INFO "bootsplash: "
+			       "error while decompressing picture: %s (%d) .\n",
+			       jpg_errors[err - 1], err);
+			splash_off(vc, info);
+			return -4;
+		}
+	}
+
+	vfree(decdata);
+
+	vc->vc_splash_data->pic->splash_pic_size = size;
+	vc->vc_splash_data->pic->splash_pic_stride = sbytes;
+
+	if (vc->vc_splash_data->imgd->splash_boxcount)
+		boxit(vc->vc_splash_data->pic->splash_pic,
+		      sbytes,
+		      vc->vc_splash_data->imgd->splash_boxes,
+		      vc->vc_splash_data->imgd->splash_boxcount,
+		      vc->vc_splash_data->splash_percent,
+		      vc->vc_splash_data->splash_boxes_xoff,
+		      vc->vc_splash_data->splash_boxes_yoff,
+		      0,
+		      cf);
+	if (vc->vc_splash_data->splash_state) {
+		int cols = vc->vc_splash_data->splash_vc_text_wi
+			/ vc->vc_font.width;
+		int rows = vc->vc_splash_data->splash_vc_text_he
+			/ vc->vc_font.height;
+
+		info->splash_data = vc->vc_splash_data;
+
+		info->splash_data->need_sync = 0;
+		/* XEN fb needs some sync after the direct modification of
+		 * fb area; maybe other FBs would need similar hack, but
+		 * so far I don't care.
+		 */
+		if (!strcmp(info->fix.id, "xen")) {
+			info->splash_data->need_sync = 1;
+			/* sync the whole splash once */
+			splash_sync_region(info, 0, 0,
+					   info->var.xres, info->var.yres);
+		}
+
+		/* vc_resize also calls con_switch which resets yscroll */
+		if (rows != vc->vc_rows || cols != vc->vc_cols)
+			vc_resize(vc, cols, rows);
+		if (!vc->vc_splash_data->color_set)
+			splash_recolor(vc, NULL);
+	} else {
+	        SPLASH_DEBUG("Splash Status is off\n");
+		splash_off(vc, info);
+		return -5;
+	}
+	return 0;
+}
+
+
+#ifdef CONFIG_PROC_FS
+
+#include <linux/proc_fs.h>
+
+static int splash_read_proc(char *buffer, char **start, off_t offset, int size,
+			    int *eof, void *data);
+static int splash_write_proc(struct file *file, const char *buffer,
+			     unsigned long count, void *data);
+static int splash_status(struct vc_data *vc);
+static int splash_proc_register(void);
+
+static struct proc_dir_entry *proc_splash;
+
+static int splash_status(struct vc_data *vc)
+{
+	struct fb_info *info;
+
+	printk(KERN_INFO "bootsplash: status on console %d changed to %s\n",
+	       vc->vc_num,
+	       vc->vc_splash_data &&
+	       vc->vc_splash_data->splash_state ? "on" : "off");
+
+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
+	if (!info)
+		return 0;
+
+	if (fg_console == vc->vc_num)
+		splash_prepare(vc, info);
+	if (vc->vc_splash_data && vc->vc_splash_data->splash_state)
+		splash_recolor(vc, info);
+	else {
+		splash_off(vc, info);
+		if (vc->vc_def_color != 0x07)
+			con_remap_def_color(vc, 0x07);
+	}
+
+	return 0;
+}
+
+int splash_copy_current_img(int unit_s, int unit_t)
+{
+	struct fb_info *info;
+	struct vc_data *vc_s;
+	struct vc_data *vc_t;
+	struct splash_data *sd_s;
+	struct splash_data *sd_t;
+	int size;
+
+	if (unit_s >= MAX_NR_CONSOLES || unit_t >= MAX_NR_CONSOLES)
+		return -1;
+
+	vc_s = vc_cons[unit_s].d;
+	if (!vc_s) {
+		printk(KERN_WARNING "bootsplash: "
+		       "copy: source (%i) is invalid.\n", unit_s);
+		return -1;
+	}
+	sd_s = vc_s->vc_splash_data;
+	if (!sd_s || !sd_s->imgd) {
+		printk(KERN_INFO "bootsplash: "
+		       "copy: source_vc (%i) doesn't have valid splash data.\n",
+		       unit_s);
+		return -1;
+	}
+	vc_allocate(unit_t);
+	vc_t = vc_cons[unit_t].d;
+	if (!vc_t) {
+		printk(KERN_WARNING "bootsplash: copy: dest (%i) is invalid.\n",
+		       unit_t);
+		return -1;
+	}
+	sd_t = kzalloc(sizeof(*sd_t), GFP_KERNEL);
+	if (!sd_t)
+		return -1;
+	vc_t->vc_splash_data = sd_t;
+
+	sd_t->imgd = sd_s->imgd;
+	sd_t->imgd->ref_cnt++;
+
+	/* now recreate all the rest */
+	sd_t->splash_state = sd_s->splash_state;
+	sd_t->splash_percent = sd_s->splash_percent;
+	sd_t->splash_dosilent = sd_s->splash_dosilent;
+	sd_t->splash_vc_text_wi = sd_s->imgd->splash_text_wi;
+	sd_t->splash_vc_text_he = sd_s->imgd->splash_text_he;
+
+	sd_t->splash_boxes_xoff = 0;
+	sd_t->splash_boxes_yoff = 0;
+	sd_t->splash_sboxes_xoff = 0;
+	sd_t->splash_sboxes_yoff = 0;
+
+	info = registered_fb[(int) con2fb_map[vc_t->vc_num]];
+	size = (((info->var.xres + 15) & ~15)
+		* ((info->var.bits_per_pixel + 1) >> 3))
+		* ((info->var.yres + 15) & ~15);
+	if (size != vc_s->vc_splash_data->pic->splash_pic_size) {
+		sd_t->pic = kzalloc(sizeof(struct splash_pic_data), GFP_KERNEL);
+		if (!sd_t->pic)
+			return -1;
+		sd_t->pic->ref_cnt = 1;
+	} else {
+		sd_t->pic = sd_s->pic;
+		sd_t->pic->ref_cnt++;
+	}
+
+	splash_status(vc_t);
+
+	return 0;
+}
+
+static int splash_read_proc(char *buffer, char **start, off_t offset, int size,
+			int *eof, void *data)
+{
+	int len;
+	int xres, yres;
+	struct vc_data *vc = vc_cons[0].d;
+	struct fb_info *info = registered_fb[(int)con2fb_map[0]];
+	int color = vc->vc_splash_data ?
+		vc->vc_splash_data->imgd->splash_color << 4 |
+		vc->vc_splash_data->imgd->splash_fg_color : splash_default >> 4;
+	int status = vc->vc_splash_data ?
+		vc->vc_splash_data->splash_state & 1 : 0;
+
+	if (info) {
+		xres = info->var.xres;
+		yres = info->var.yres;
+	} else
+		xres = yres = 0;
+
+	len = sprintf(buffer, "Splash screen v%s (0x%02x, %dx%d%s): %s\n",
+		      SPLASH_VERSION, color, xres, yres,
+		      (vc->vc_splash_data ?
+		       vc->vc_splash_data->splash_dosilent : 0) ? ", silent" :
+		      "",
+		      status ? "on" : "off");
+	if (offset >= len)
+		return 0;
+
+	*start = buffer - offset;
+
+	return (size < len - offset ? size : len - offset);
+}
+
+void splash_set_percent(struct vc_data *vc, int pe)
+{
+	struct fb_info *info;
+	struct fbcon_ops *ops;
+	struct splash_data *vc_splash_data;
+	int oldpe;
+
+	SPLASH_DEBUG(" console: %d val: %d\n", vc->vc_num, pe);
+
+	if (pe < -2)
+		pe = 0;
+	if (pe > 65535)
+		pe = 65535;
+	pe += pe > 32767;
+
+	vc_splash_data = vc->vc_splash_data;
+	if (!vc_splash_data || vc_splash_data->splash_percent == pe)
+		return;
+
+	oldpe = vc_splash_data->splash_percent;
+	vc_splash_data->splash_percent = pe;
+	if (fg_console != vc->vc_num ||
+	    !vc_splash_data->splash_state) {
+		return;
+	}
+	info = registered_fb[(int) con2fb_map[vc->vc_num]];
+	if (!info)
+		return;
+
+	ops = info->fbcon_par;
+	if (ops->blank_state)
+		return;
+	if (!vc_splash_data->imgd->splash_overpaintok
+	    || pe == 65536
+	    || pe < oldpe) {
+		if (splash_hasinter(vc_splash_data->imgd->splash_boxes,
+				    vc_splash_data->imgd->splash_boxcount)) {
+			splash_status(vc);
+		} else
+			splash_prepare(vc, info);
+	} else {
+		struct splash_data *splash_data = info->splash_data;
+		enum splash_color_format cf = SPLASH_DEPTH_UNKNOWN;
+		switch (info->var.bits_per_pixel) {
+		case 16:
+			if ((info->var.red.length +
+			     info->var.green.length +
+			     info->var.blue.length) == 15)
+				cf = SPLASH_DEPTH_15;
+			else
+				cf = SPLASH_DEPTH_16;
+			break;
+		case 24:
+			cf = SPLASH_DEPTH_24_PACKED;
+			break;
+		case 32:
+			cf = SPLASH_DEPTH_24;
+			break;
+		}
+		if (cf == SPLASH_DEPTH_UNKNOWN)
+			return;
+		if (splash_data) {
+			if (splash_data->imgd->splash_silentjpeg
+			    && splash_data->splash_dosilent) {
+				boxit(info->screen_base,
+				      info->fix.line_length,
+				      splash_data->imgd->splash_sboxes,
+				      splash_data->imgd->splash_sboxcount,
+				      splash_data->splash_percent,
+				      splash_data->splash_sboxes_xoff,
+				      splash_data->splash_sboxes_yoff,
+				      1,
+				      cf);
+				/* FIXME: get a proper width/height */
+				splash_sync_region(info,
+					splash_data->splash_sboxes_xoff,
+					splash_data->splash_sboxes_yoff,
+					info->var.xres -
+					splash_data->splash_sboxes_xoff,
+					8);
+			}
+		}
+	}
+}
+
+static const char *get_unit(const char *buffer, int *unit)
+{
+
+	*unit = -1;
+	if (buffer[0] >= '0' && buffer[0] <= '9') {
+		*unit = buffer[0] - '0';
+		buffer++;
+		if (buffer[0] >= '0' && buffer[0] <= '9') {
+			*unit = *unit * 10 + buffer[0] - '0';
+			buffer++;
+		}
+		if (*buffer == ' ')
+			buffer++;
+	}
+	return buffer;
+}
+
+static int splash_write_proc(struct file *file, const char *buffer,
+			     unsigned long count, void *data)
+{
+	int new, unit;
+	unsigned long uval;
+	struct vc_data *vc;
+	struct splash_data *vc_splash_data;
+
+	SPLASH_DEBUG();
+
+	if (!buffer || !splash_default)
+		return count;
+
+	console_lock();
+	unit = 0;
+	if (buffer[0] == '@') {
+		buffer++;
+		buffer = get_unit(buffer, &unit);
+		if (unit < 0 || unit >= MAX_NR_CONSOLES || !vc_cons[unit].d) {
+			console_unlock();
+			return count;
+		}
+	}
+	SPLASH_DEBUG(" unit: %i", unit);
+	vc = vc_cons[unit].d;
+	vc_splash_data = vc->vc_splash_data;
+
+	if (!strncmp(buffer, "redraw", 6)) {
+		SPLASH_DEBUG(" redraw");
+		splash_status(vc);
+		console_unlock();
+		return count;
+	}
+
+	if (!strncmp(buffer, "show", 4) || !strncmp(buffer, "hide", 4)) {
+		long int pe;
+
+		SPLASH_DEBUG("show/hide");
+		if (buffer[4] == ' ' && buffer[5] == 'p')
+			pe = 0;
+		else if (buffer[4] == '\n')
+			pe = 65535;
+		else if (strict_strtol(buffer + 5, 0, &pe) == -EINVAL)
+			pe = 0;
+		if (pe < -2)
+			pe = 0;
+		if (pe > 65535)
+			pe = 65535;
+		if (*buffer == 'h')
+			pe = 65535 - pe;
+		splash_set_percent(vc, pe);
+		console_unlock();
+		return count;
+	}
+
+	if (!strncmp(buffer, "copy", 4)) {
+		buffer += 4;
+		if (buffer[0] == ' ')
+			buffer++;
+		buffer = get_unit(buffer, &unit);
+		if (unit < 0 || unit >= MAX_NR_CONSOLES) {
+			console_unlock();
+			return count;
+		}
+		buffer = get_unit(buffer, &new);
+		if (new < 0 || new >= MAX_NR_CONSOLES) {
+			console_unlock();
+			return count;
+		}
+		splash_copy_current_img(unit, new);
+		console_unlock();
+		return count;
+	}
+
+	if (!strncmp(buffer, "silent\n", 7)
+	    || !strncmp(buffer, "verbose\n", 8)) {
+		SPLASH_DEBUG(" silent/verbose");
+
+		if (vc_splash_data &&
+		    vc_splash_data->imgd->splash_silentjpeg) {
+			if (vc_splash_data->splash_dosilent !=
+			    (buffer[0] == 's')) {
+				vc_splash_data->splash_dosilent =
+					buffer[0] == 's';
+				splash_status(vc);
+			}
+		}
+		console_unlock();
+		return count;
+	}
+
+	if (!strncmp(buffer, "freesilent\n", 11)) {
+		SPLASH_DEBUG(" freesilent");
+
+		if (vc_splash_data &&
+		    vc_splash_data->imgd->splash_silentjpeg) {
+			struct splash_data *sd;
+			printk(KERN_INFO "bootsplash: freeing silent jpeg\n");
+			for (sd = vc_splash_data; sd; sd = sd->next) {
+				sd->imgd->splash_silentjpeg = 0;
+				vfree(sd->imgd->splash_sboxes);
+				sd->imgd->splash_sboxes = 0;
+				sd->imgd->splash_sboxcount = 0;
+			}
+			if (vc_splash_data->splash_dosilent)
+				splash_status(vc);
+
+			vc->vc_splash_data->splash_dosilent = 0;
+		}
+		console_unlock();
+		return count;
+	}
+
+	if (!strncmp(buffer, "BOOTSPL", 7)) {
+		int up = -1;
+
+		SPLASH_DEBUG(" BOOTSPL");
+		unit = splash_getraw((unsigned char *)buffer,
+				     (unsigned char *)buffer + count,
+				     &up);
+		SPLASH_DEBUG(" unit: %i up: %i", unit, up);
+		if (unit >= 0) {
+			struct fb_info *info;
+
+			vc = vc_cons[unit].d;
+			info = registered_fb[(int) con2fb_map[vc->vc_num]];
+			if (!info) {
+				console_unlock();
+				return count;
+			}
+
+			if (up == -1) {
+				splash_status(vc);
+			} else {
+				struct splash_data *vc_splash_data
+					= vc->vc_splash_data;
+				struct splash_data *splash_data
+					= info->splash_data;
+				struct fbcon_ops *ops = info->fbcon_par;
+				enum splash_color_format cf = SPLASH_DEPTH_UNKNOWN;
+
+				switch (info->var.bits_per_pixel) {
+				case 16:
+					if ((info->var.red.length +
+					     info->var.green.length +
+					     info->var.blue.length) == 15)
+						cf = SPLASH_DEPTH_15;
+					else
+						cf = SPLASH_DEPTH_16;
+					break;
+				case 24:
+					cf = SPLASH_DEPTH_24_PACKED;
+					break;
+				case 32:
+					cf = SPLASH_DEPTH_24;
+					break;
+				}
+				if (cf == SPLASH_DEPTH_UNKNOWN)
+					up = 0;
+				if (ops->blank_state ||
+				    !vc_splash_data ||
+				    !splash_data)
+					up = 0;
+				if ((up & 2) != 0
+				    && splash_data->imgd->splash_silentjpeg
+				    && splash_data->splash_dosilent) {
+					boxit(info->screen_base,
+					      info->fix.line_length,
+					      splash_data->imgd->splash_sboxes,
+					      splash_data->imgd->splash_sboxcount,
+					      splash_data->splash_percent,
+					      splash_data->splash_sboxes_xoff,
+					      splash_data->splash_sboxes_yoff,
+					      1,
+					      cf);
+				} else if ((up & 1) != 0) {
+					boxit(info->screen_base,
+					      info->fix.line_length,
+					      splash_data->imgd->splash_boxes,
+					      splash_data->imgd->splash_boxcount,
+					      splash_data->splash_percent,
+					      splash_data->splash_boxes_xoff,
+					      splash_data->splash_boxes_yoff,
+					      1,
+					      cf);
+				}
+			}
+		}
+		console_unlock();
+		return count;
+	}
+
+	if (!vc_splash_data) {
+		console_unlock();
+		return count;
+	}
+
+	if (buffer[0] == 't') {
+		vc_splash_data->splash_state ^= 1;
+		SPLASH_DEBUG(" t");
+		splash_status(vc);
+		console_unlock();
+		return count;
+	}
+	if (strict_strtoul(buffer, 0, &uval) == -EINVAL)
+		uval = 1;
+	if (uval > 1) {
+		/* expert user */
+		vc_splash_data->imgd->splash_color    = uval >> 8 & 0xff;
+		vc_splash_data->imgd->splash_fg_color = uval >> 4 & 0x0f;
+	}
+	if ((uval & 1) == vc_splash_data->splash_state)
+		splash_recolor(vc, NULL);
+	else {
+		vc_splash_data->splash_state = uval & 1;
+		splash_status(vc);
+	}
+	console_unlock();
+	return count;
+}
+
+static int splash_proc_register(void)
+{
+	proc_splash = create_proc_entry("splash", 0, 0);
+	if (proc_splash) {
+		proc_splash->read_proc = splash_read_proc;
+		proc_splash->write_proc = splash_write_proc;
+		return 0;
+	}
+	return 1;
+}
+
+#endif	/* CONFIG_PROC_FS */
+
+#define INIT_CONSOLE 0
+
+void splash_init(void)
+{
+	static bool splash_not_initialized = true;
+	struct fb_info *info;
+	struct vc_data *vc;
+	int isramfs = 1;
+	int fd;
+	int len;
+	int max_len = 1024*1024*2;
+	char *mem;
+
+	if (splash_not_initialized == false)
+		return;
+	vc = vc_cons[INIT_CONSOLE].d;
+	info = registered_fb[(int)con2fb_map[INIT_CONSOLE]];
+	if (!vc
+	    || !info
+	    || info->var.bits_per_pixel < 16) /* not supported */
+		return;
+#ifdef CONFIG_PROC_FS
+	splash_proc_register();
+#endif
+	splash_not_initialized = false;
+	if (vc->vc_splash_data)
+		return;
+	fd = sys_open("/bootsplash", O_RDONLY, 0);
+	if (fd < 0) {
+		isramfs = 0;
+		fd = sys_open("/initrd.image", O_RDONLY, 0);
+	}
+	if (fd < 0)
+		return;
+	len = (int)sys_lseek(fd, (off_t)0, 2);
+	if (len <= 0) {
+		sys_close(fd);
+		return;
+	}
+	/* Don't look for more than the last 2MB */
+	if (len > max_len) {
+		printk(KERN_INFO "bootsplash: "
+		       "scanning last %dMB of initrd for signature\n",
+		       max_len>>20);
+		sys_lseek(fd, (off_t)(len - max_len), 0);
+		len = max_len;
+	} else {
+		sys_lseek(fd, (off_t)0, 0);
+	}
+
+	mem = vmalloc(len);
+	if (mem) {
+		console_lock();
+		if ((int)sys_read(fd, mem, len) == len
+		    && (splash_getraw((unsigned char *)mem,
+				      (unsigned char *)mem + len, (int *)0)
+			== INIT_CONSOLE)
+		    && vc->vc_splash_data)
+			vc->vc_splash_data->splash_state = splash_default & 1;
+		console_unlock();
+		vfree(mem);
+	}
+	sys_close(fd);
+	if (isramfs)
+		sys_unlink("/bootsplash");
+	return;
+}
+
+#define SPLASH_ALIGN 15
+
+static u32 *do_coefficients(u32 from, u32 to, u32 *shift)
+{
+	u32 *coefficients;
+	u32 left = to;
+	int n = 1;
+	u32 upper = 31;
+	int col_cnt = 0;
+	int row_cnt = 0;
+	int m;
+	u32 rnd = from >> 1;
+
+	if (from > to) {
+		left = to;
+		rnd = from >> 1;
+
+		while (upper > 0) {
+			if ((1 << upper) & from)
+				break;
+			upper--;
+		}
+		upper++;
+
+		*shift = 32 - 8 - 1 - upper;
+
+		coefficients = vmalloc(sizeof(u32) * (from / to + 2) * from + 1);
+		if (!coefficients)
+			return NULL;
+
+		n = 1;
+		while (1) {
+			u32 sum = left;
+			col_cnt = 0;
+			m = n++;
+			while (sum < from) {
+				coefficients[n++] =
+					((left << *shift) + rnd) / from;
+				col_cnt++;
+				left = to;
+				sum += left;
+			}
+			left = sum - from;
+			coefficients[n++] =
+				(((to - left) << *shift) + rnd) / from;
+			col_cnt++;
+			coefficients[m] = col_cnt;
+			row_cnt++;
+			if (!left) {
+				coefficients[0] = row_cnt;
+				return coefficients;
+			}
+		}
+	} else {
+		left = 0;
+		rnd = to >> 1;
+
+		while (upper > 0) {
+			if ((1 << upper) & to)
+				break;
+			upper--;
+		}
+		upper++;
+
+		*shift = 32 - 8 - 1 - upper;
+
+		coefficients = vmalloc(sizeof(u32) * 3 * from + 1);
+		if (!coefficients)
+			return NULL;
+
+		while (1) {
+			u32 diff;
+			u32 sum = left;
+			col_cnt = 0;
+			row_cnt++;
+			while (sum < to) {
+				col_cnt++;
+				sum += from;
+			}
+			left = sum - to;
+			diff = from - left;
+			if (!left) {
+				coefficients[n] = col_cnt;
+				coefficients[0] = row_cnt;
+				return coefficients;
+			}
+			coefficients[n++] = col_cnt - 1;
+			coefficients[n++] = ((diff << *shift) + rnd) / from;
+			coefficients[n++] = ((left << *shift) + rnd) / from;
+		}
+	}
+}
+
+
+struct pixel {
+	u32 red;
+	u32 green;
+	u32 blue;
+};
+
+#define put_pixel(pix, buf, cf)						\
+	switch (cf) {							\
+	case SPLASH_DEPTH_15:						\
+		*(u16 *)(buf) = (u16)((pix).red << 10 |                 \
+				      (pix).green << 5 | (pix).blue);	\
+	(buf) += 2;							\
+	break;								\
+	case SPLASH_DEPTH_16:						\
+		*(u16 *)(buf) = (u16)((pix).red << 11 |			\
+				      (pix).green << 5 | (pix).blue);	\
+		(buf) += 2;						\
+		break;							\
+	case SPLASH_DEPTH_24_PACKED:					\
+		*(u16 *)(buf) = (u16)((pix).red << 8 | (pix).green);	\
+		buf += 2;						\
+		*((buf)++) = (pix).blue;				\
+		break;							\
+	case SPLASH_DEPTH_24:						\
+		*(u32 *)(buf) = (u32)((pix).red << 16 |			\
+				      (pix).green << 8 | (pix).blue);	\
+		(buf) += 4;						\
+		break;							\
+        case SPLASH_DEPTH_UNKNOWN:					\
+		break;							\
+	}
+
+#define get_pixel(pix, buf, depth)				       \
+	switch (depth) {					       \
+	case SPLASH_DEPTH_15:					       \
+		(pix).red = ((*(u16 *)(buf)) >> 10) & 0x1f;	       \
+		(pix).green = ((*(u16 *)(buf)) >> 5) & 0x1f;	       \
+		(pix).blue = (*(u16 *)(buf)) & 0x1f;		       \
+		(buf) += 2;					       \
+		break;						       \
+	case SPLASH_DEPTH_16:					       \
+		(pix).red = ((*(u16 *)(buf)) >> 11) & 0x1f;	       \
+		(pix).green = ((*(u16 *)(buf)) >> 5) & 0x3f;	       \
+		(pix).blue = (*(u16 *)(buf)) & 0x1f;		       \
+		(buf) += 2;					       \
+		break;						       \
+	case SPLASH_DEPTH_24_PACKED:				       \
+		(pix).blue = *(((buf))++);			       \
+		(pix).green = *(((buf))++);			       \
+		(pix).red = *(((buf))++);			       \
+		break;						       \
+	case SPLASH_DEPTH_24:					       \
+		(pix).blue = *(((buf))++);			       \
+		(pix).green = *(((buf))++);			       \
+		(pix).red = *(((buf))++);			       \
+		(buf)++;					       \
+		break;						       \
+	 case SPLASH_DEPTH_UNKNOWN:				       \
+		break;						       \
+	}
+
+static inline void
+scale_x_down(enum splash_color_format cf, int src_w,
+	     unsigned char **src_p, u32 *x_coeff,
+	     u32 x_shift,  u32 y_coeff, struct pixel *row_buffer)
+{
+	u32 curr_x_coeff = 1;
+	struct pixel curr_pixel, tmp_pixel;
+	u32 x_array_size = x_coeff[0];
+	int x_column_num;
+	int i;
+	int l, m;
+	int k = 0;
+	u32 rnd = (1 << (x_shift - 1));
+
+	for (i = 0; i < src_w; ) {
+		curr_x_coeff = 1;
+		get_pixel(tmp_pixel, *src_p, cf);
+		i++;
+		for (l = 0; l < x_array_size; l++) {
+			x_column_num = x_coeff[curr_x_coeff++];
+			curr_pixel.red = 0;
+			curr_pixel.green = 0;
+			curr_pixel.blue = 0;
+			for (m = 0; m < x_column_num - 1; m++) {
+				curr_pixel.red += tmp_pixel.red
+					* x_coeff[curr_x_coeff];
+				curr_pixel.green += tmp_pixel.green
+					* x_coeff[curr_x_coeff];
+				curr_pixel.blue += tmp_pixel.blue
+					* x_coeff[curr_x_coeff];
+				curr_x_coeff++;
+				get_pixel(tmp_pixel, *src_p, cf);
+				i++;
+			}
+			curr_pixel.red += tmp_pixel.red * x_coeff[curr_x_coeff];
+			curr_pixel.green += tmp_pixel.green
+				* x_coeff[curr_x_coeff];
+			curr_pixel.blue += tmp_pixel.blue
+				* x_coeff[curr_x_coeff];
+			curr_x_coeff++;
+			curr_pixel.red = (curr_pixel.red + rnd) >> x_shift;
+			curr_pixel.green = (curr_pixel.green + rnd) >> x_shift;
+			curr_pixel.blue = (curr_pixel.blue + rnd) >> x_shift;
+			row_buffer[k].red += curr_pixel.red * y_coeff;
+			row_buffer[k].green += curr_pixel.green * y_coeff;
+			row_buffer[k].blue += curr_pixel.blue * y_coeff;
+			k++;
+		}
+	}
+}
+
+static inline void
+scale_x_up(enum splash_color_format cf, int src_w,
+	   unsigned char **src_p, u32 *x_coeff,
+	   u32 x_shift,  u32 y_coeff, struct pixel *row_buffer)
+{
+	u32 curr_x_coeff = 1;
+	struct pixel curr_pixel, tmp_pixel;
+	u32 x_array_size = x_coeff[0];
+	int x_column_num;
+	int i;
+	int l, m;
+	int k = 0;
+	u32 rnd = (1 << (x_shift - 1));
+
+	for (i = 0; i < src_w;) {
+		curr_x_coeff = 1;
+		get_pixel(tmp_pixel, *src_p, cf);
+		i++;
+		for (l = 0; l < x_array_size - 1; l++) {
+			x_column_num = x_coeff[curr_x_coeff++];
+			for (m = 0; m < x_column_num; m++) {
+				row_buffer[k].red += tmp_pixel.red * y_coeff;
+				row_buffer[k].green += tmp_pixel.green * y_coeff;
+				row_buffer[k].blue += tmp_pixel.blue * y_coeff;
+				k++;
+			}
+			curr_pixel.red = tmp_pixel.red * x_coeff[curr_x_coeff];
+			curr_pixel.green = tmp_pixel.green
+				* x_coeff[curr_x_coeff];
+			curr_pixel.blue = tmp_pixel.blue * x_coeff[curr_x_coeff];
+			curr_x_coeff++;
+			get_pixel(tmp_pixel, *src_p, cf);
+			i++;
+			row_buffer[k].red += ((curr_pixel.red
+					       + (tmp_pixel.red
+						  * x_coeff[curr_x_coeff])
+					       + rnd) >> x_shift) * y_coeff;
+			row_buffer[k].green += ((curr_pixel.green
+						 + (tmp_pixel.green
+						    * x_coeff[curr_x_coeff])
+						 + rnd) >> x_shift) * y_coeff;
+			row_buffer[k].blue += ((curr_pixel.blue
+						+ (tmp_pixel.blue
+						   * x_coeff[curr_x_coeff])
+						+ rnd) >> x_shift) * y_coeff;
+			k++;
+			curr_x_coeff++;
+		}
+		for (m = 0; m < x_coeff[curr_x_coeff]; m++) {
+			row_buffer[k].red += tmp_pixel.red * y_coeff;
+			row_buffer[k].green += tmp_pixel.green * y_coeff;
+			row_buffer[k].blue += tmp_pixel.blue * y_coeff;
+			k++;
+		}
+	}
+}
+
+static int scale_y_down(unsigned char *src, unsigned char *dst,
+			enum splash_color_format cf,
+			int src_w, int src_h, int dst_w, int dst_h)
+{
+	int octpp = splash_octpp(cf);
+	int src_x_bytes = octpp * ((src_w + SPLASH_ALIGN) & ~SPLASH_ALIGN);
+	int dst_x_bytes = octpp * ((dst_w + SPLASH_ALIGN) & ~SPLASH_ALIGN);
+	int j;
+	struct pixel *row_buffer;
+	u32 x_shift, y_shift;
+	u32 *x_coeff;
+	u32 *y_coeff;
+	u32 curr_y_coeff = 1;
+	unsigned char *src_p;
+	unsigned char *src_p_line = src;
+	char *dst_p_line;
+	int r, s;
+	int y_array_rows;
+	int y_column_num;
+	int k;
+	u32 rnd;
+	int xup;
+
+	row_buffer = vmalloc(sizeof(struct pixel)
+					     * (dst_w + 1));
+	x_coeff = do_coefficients(src_w, dst_w, &x_shift);
+	y_coeff = do_coefficients(src_h, dst_h, &y_shift);
+	if (!row_buffer || !x_coeff || !y_coeff) {
+		vfree(row_buffer);
+		vfree(x_coeff);
+		vfree(y_coeff);
+		return -ENOMEM;
+	}
+	y_array_rows = y_coeff[0];
+	rnd = (1 << (y_shift - 1));
+	xup = (src_w <= dst_w) ? 1 : 0;
+
+	dst_p_line = dst;
+
+	for (j = 0; j < src_h;) {
+		curr_y_coeff = 1;
+		for (r = 0; r < y_array_rows; r++) {
+			y_column_num = y_coeff[curr_y_coeff++];
+			for (k = 0; k < dst_w + 1; k++) {
+				row_buffer[k].red = 0;
+				row_buffer[k].green = 0;
+				row_buffer[k].blue = 0;
+			}
+			src_p = src_p_line;
+			if (xup)
+				scale_x_up(cf,  src_w, &src_p, x_coeff,
+					   x_shift, y_coeff[curr_y_coeff],
+					   row_buffer);
+			else
+				scale_x_down(cf,  src_w, &src_p, x_coeff,
+					     x_shift, y_coeff[curr_y_coeff],
+					     row_buffer);
+			curr_y_coeff++;
+			for (s = 1; s < y_column_num; s++) {
+				src_p = src_p_line = src_p_line + src_x_bytes;
+				j++;
+				if (xup)
+					scale_x_up(cf,  src_w, &src_p,
+						   x_coeff, x_shift,
+						   y_coeff[curr_y_coeff],
+						   row_buffer);
+				else
+					scale_x_down(cf,  src_w, &src_p,
+						     x_coeff, x_shift,
+						     y_coeff[curr_y_coeff],
+						     row_buffer);
+				curr_y_coeff++;
+			}
+			for (k = 0; k < dst_w; k++) {
+				row_buffer[k].red = (row_buffer[k].red + rnd)
+					>> y_shift;
+				row_buffer[k].green = (row_buffer[k].green
+						       + rnd)
+					>> y_shift;
+				row_buffer[k].blue = (row_buffer[k].blue + rnd)
+					>> y_shift;
+				put_pixel(row_buffer[k], dst, cf);
+			}
+			dst = dst_p_line = dst_p_line + dst_x_bytes;
+		}
+		src_p_line = src_p_line + src_x_bytes;
+		j++;
+	}
+	vfree(row_buffer);
+	vfree(x_coeff);
+	vfree(y_coeff);
+	return 0;
+}
+
+static int scale_y_up(unsigned char *src, unsigned char *dst,
+		      enum splash_color_format cf,
+		      int src_w, int src_h, int dst_w, int dst_h)
+{
+	int octpp = splash_octpp(cf);
+	int src_x_bytes = octpp * ((src_w + SPLASH_ALIGN) & ~SPLASH_ALIGN);
+	int dst_x_bytes = octpp * ((dst_w + SPLASH_ALIGN) & ~SPLASH_ALIGN);
+	int j;
+	u32 x_shift, y_shift;
+	u32 *x_coeff;
+	u32 *y_coeff;
+	struct pixel *row_buf_list[2];
+	struct pixel *row_buffer;
+	u32 curr_y_coeff = 1;
+	unsigned char *src_p;
+	unsigned char *src_p_line = src;
+	char *dst_p_line;
+	int r, s;
+	int y_array_rows;
+	int y_column_num;
+	int k;
+	u32 rnd;
+	int bi;
+	int xup;
+	int writes;
+
+	x_coeff = do_coefficients(src_w, dst_w, &x_shift);
+	y_coeff = do_coefficients(src_h, dst_h, &y_shift);
+	row_buf_list[0] = vmalloc(2 * sizeof(struct pixel)
+						  * (dst_w + 1));
+	if (!row_buf_list[0] || !x_coeff || !y_coeff) {
+		vfree(row_buf_list[0]);
+		vfree(x_coeff);
+		vfree(y_coeff);
+		return -ENOMEM;
+	}
+	row_buf_list[1] = row_buf_list[0] + (dst_w + 1);
+
+	y_array_rows = y_coeff[0];
+	rnd = (1 << (y_shift - 1));
+	bi = 1;
+	xup = (src_w <= dst_w) ? 1 : 0;
+	writes = 0;
+
+	dst_p_line = dst;
+	src_p = src_p_line;
+
+	row_buffer = row_buf_list[0];
+
+	for (j = 0; j < src_h;) {
+		memset(row_buf_list[0], 0, (2 * sizeof(struct pixel)
+					    * (dst_w + 1)));
+		curr_y_coeff = 1;
+		if (xup)
+			scale_x_up(cf,  src_w, &src_p, x_coeff,
+				   x_shift, 1, row_buffer);
+		else
+			scale_x_down(cf,  src_w, &src_p, x_coeff, x_shift, 1,
+				     row_buffer);
+		src_p = src_p_line = src_p_line + src_x_bytes;
+		j++;
+		for (r = 0; r < y_array_rows - 1; r++) {
+			struct pixel *old_row_buffer = row_buffer;
+			u32 prev_y_coeff_val;
+
+			y_column_num = y_coeff[curr_y_coeff];
+			for (s = 0; s < y_column_num; s++) {
+				for (k = 0; k < dst_w; k++)
+					put_pixel(row_buffer[k], dst, cf);
+				dst = dst_p_line = dst_p_line + dst_x_bytes;
+				writes++;
+			}
+			curr_y_coeff++;
+			row_buffer = row_buf_list[(bi++) % 2];
+			prev_y_coeff_val = y_coeff[curr_y_coeff++];
+			if (xup)
+				scale_x_up(cf,  src_w, &src_p, x_coeff,
+					   x_shift, 1, row_buffer);
+			else
+				scale_x_down(cf,  src_w, &src_p, x_coeff,
+					     x_shift, 1, row_buffer);
+			src_p = src_p_line = src_p_line + src_x_bytes;
+			j++;
+			for (k = 0; k < dst_w; k++) {
+				struct pixel pix;
+				pix.red = ((old_row_buffer[k].red
+					    * prev_y_coeff_val)
+					   + (row_buffer[k].red
+					      * y_coeff[curr_y_coeff])
+					   + rnd) >> y_shift;
+				pix.green = ((old_row_buffer[k].green
+					      * prev_y_coeff_val)
+					     + (row_buffer[k].green
+						* y_coeff[curr_y_coeff])
+					     + rnd) >> y_shift;
+				pix.blue = ((old_row_buffer[k].blue
+					     * prev_y_coeff_val)
+					    + (row_buffer[k].blue
+					       * y_coeff[curr_y_coeff])
+					    + rnd) >> y_shift;
+				old_row_buffer[k].red = 0;
+				old_row_buffer[k].green = 0;
+				old_row_buffer[k].blue = 0;
+				put_pixel(pix, dst, cf);
+			}
+			dst = dst_p_line = dst_p_line + dst_x_bytes;
+			writes++;
+			curr_y_coeff++;
+		}
+		for (r = 0; r < y_coeff[curr_y_coeff]; r++) {
+			for (k = 0; k < dst_w; k++)
+				put_pixel(row_buffer[k], dst, cf);
+
+			dst = dst_p_line = dst_p_line + dst_x_bytes;
+			writes++;
+		}
+	}
+	vfree(row_buf_list[0]);
+	vfree(x_coeff);
+	vfree(y_coeff);
+
+	return 0;
+}
+
+static int jpeg_get(unsigned char *buf, unsigned char *pic,
+		    int width, int height, enum splash_color_format cf,
+		    struct jpeg_decdata *decdata)
+{
+	int my_width, my_height;
+	int err;
+	int octpp = splash_octpp(cf);
+
+	jpeg_get_size(buf, &my_width, &my_height);
+
+	if (my_height != height || my_width != width) {
+		int my_size = ((my_width + 15) & ~15)
+		    * ((my_height + 15) & ~15) * octpp;
+		unsigned char *mem = vmalloc(my_size);
+		if (!mem)
+			return 17;
+		err = jpeg_decode(buf, mem, ((my_width + 15) & ~15),
+				  ((my_height + 15) & ~15), cf, decdata);
+		if (err) {
+			vfree(mem);
+			return err;
+		}
+		printk(KERN_INFO
+		       "bootsplash: scaling image from %dx%d to %dx%d\n",
+		       my_width, my_height, width, height);
+		if (my_height <= height)
+			err = scale_y_up(mem, pic, cf, my_width, my_height,
+					 ((width + 15) & ~15),
+					 ((height + 15) & ~15));
+		else
+			err = scale_y_down(mem, pic, cf, my_width, my_height,
+					   ((width + 15) & ~15),
+					   ((height + 15) & ~15));
+		vfree(mem);
+		if (err < 0)
+			return 17;
+	} else {
+		err = jpeg_decode(buf, pic, ((width + 15) & ~15),
+				  ((height + 15) & ~15), cf, decdata);
+		if (err)
+			return err;
+	}
+	return 0;
+}
--- /dev/null
+++ b/drivers/video/bootsplash/decode-jpg.c
@@ -0,0 +1,1045 @@
+/*
+ *    linux/drivers/video/bootsplash/decode-jpg.c - a tiny jpeg decoder.
+ *
+ *      (w) August 2001 by Michael Schroeder, <mls@suse.de>
+ *
+ */
+
+#include <linux/string.h>
+#include <asm/byteorder.h>
+#include <linux/bootsplash.h>
+#include "decode-jpg.h"
+
+#define ISHIFT 11
+
+#define IFIX(a) ((int)((a) * (1 << ISHIFT) + .5))
+#define IMULT(a, b) (((a) * (b)) >> ISHIFT)
+#define ITOINT(a) ((a) >> ISHIFT)
+
+/* special markers */
+#define M_BADHUFF	-1
+#define M_EOF		0x80
+
+struct in {
+	unsigned char *p;
+	unsigned int bits;
+	int left;
+	int marker;
+
+	int (*func)(void *);
+	void *data;
+};
+
+/*********************************/
+struct dec_hufftbl;
+struct enc_hufftbl;
+
+union hufftblp {
+	struct dec_hufftbl *dhuff;
+	struct enc_hufftbl *ehuff;
+};
+
+struct scan {
+	int dc;			/* old dc value */
+
+	union hufftblp hudc;
+	union hufftblp huac;
+	int next;		/* when to switch to next scan */
+
+	int cid;		/* component id */
+	int hv;			/* horiz/vert, copied from comp */
+	int tq;			/* quant tbl, copied from comp */
+};
+
+/*********************************/
+
+#define DECBITS 10		/* seems to be the optimum */
+
+struct dec_hufftbl {
+	int maxcode[17];
+	int valptr[16];
+	unsigned char vals[256];
+	unsigned int llvals[1 << DECBITS];
+};
+
+static void decode_mcus(struct in *, int *, int, struct scan *, int *);
+static int dec_readmarker(struct in *);
+static void dec_makehuff(struct dec_hufftbl *, int *, unsigned char *);
+
+static void setinput(struct in *, unsigned char *);
+/*********************************/
+
+#undef PREC
+#define PREC int
+
+static void idctqtab(unsigned char *, PREC *);
+static void idct(int *, int *, PREC *, PREC, int);
+static void scaleidctqtab(PREC *, PREC);
+
+/*********************************/
+
+static void initcol(PREC[][64]);
+
+static void col221111(int *out, unsigned char *pic, int width);
+static void col221111_15(int *out, unsigned char *pic, int width);
+static void col221111_16(int *out, unsigned char *pic, int width);
+static void col221111_32(int *out, unsigned char *pic, int width);
+
+/*********************************/
+
+#define M_SOI	0xd8
+#define M_APP0	0xe0
+#define M_DQT	0xdb
+#define M_SOF0	0xc0
+#define M_DHT   0xc4
+#define M_DRI	0xdd
+#define M_SOS	0xda
+#define M_RST0	0xd0
+#define M_EOI	0xd9
+#define M_COM	0xfe
+
+static unsigned char *datap;
+
+static int getbyte(void)
+{
+	return *datap++;
+}
+
+static int getword(void)
+{
+	int c1, c2;
+	c1 = *datap++;
+	c2 = *datap++;
+	return c1 << 8 | c2;
+}
+
+struct comp {
+	int cid;
+	int hv;
+	int tq;
+};
+
+#define MAXCOMP 4
+struct jpginfo {
+	int nc;			/* number of components */
+	int ns;			/* number of scans */
+	int dri;		/* restart interval */
+	int nm;			/* mcus til next marker */
+	int rm;			/* next restart marker */
+};
+
+static struct jpginfo info;
+static struct comp comps[MAXCOMP];
+
+static struct scan dscans[MAXCOMP];
+
+static unsigned char quant[4][64];
+
+static struct dec_hufftbl dhuff[4];
+
+#define dec_huffdc (dhuff + 0)
+#define dec_huffac (dhuff + 2)
+
+static struct in in;
+
+static int readtables(int till)
+{
+	int m, l, i, j, lq, pq, tq;
+	int tc, th, tt;
+
+	for (;;) {
+		if (getbyte() != 0xff)
+			return -1;
+		m = getbyte();
+		if (m == till)
+			break;
+
+		switch (m) {
+		case 0xc2:
+			return 0;
+
+		case M_DQT:
+			lq = getword();
+			while (lq > 2) {
+				pq = getbyte();
+				tq = pq & 15;
+				if (tq > 3)
+					return -1;
+				pq >>= 4;
+				if (pq != 0)
+					return -1;
+				for (i = 0; i < 64; i++)
+					quant[tq][i] = getbyte();
+				lq -= 64 + 1;
+			}
+			break;
+
+		case M_DHT:
+			l = getword();
+			while (l > 2) {
+				int hufflen[16], k;
+				unsigned char huffvals[256];
+
+				tc = getbyte();
+				th = tc & 15;
+				tc >>= 4;
+				tt = tc * 2 + th;
+				if (tc > 1 || th > 1)
+					return -1;
+				for (i = 0; i < 16; i++)
+					hufflen[i] = getbyte();
+				l -= 1 + 16;
+				k = 0;
+				for (i = 0; i < 16; i++) {
+					for (j = 0; j < hufflen[i]; j++)
+						huffvals[k++] = getbyte();
+					l -= hufflen[i];
+				}
+				dec_makehuff(dhuff + tt, hufflen,
+					     huffvals);
+			}
+			break;
+
+		case M_DRI:
+			l = getword();
+			info.dri = getword();
+			break;
+
+		default:
+			l = getword();
+			while (l-- > 2)
+				getbyte();
+			break;
+		}
+	}
+	return 0;
+}
+
+static void dec_initscans(void)
+{
+	int i;
+
+	info.nm = info.dri + 1;
+	info.rm = M_RST0;
+	for (i = 0; i < info.ns; i++)
+		dscans[i].dc = 0;
+}
+
+static int dec_checkmarker(void)
+{
+	int i;
+
+	if (dec_readmarker(&in) != info.rm)
+		return -1;
+	info.nm = info.dri;
+	info.rm = (info.rm + 1) & ~0x08;
+	for (i = 0; i < info.ns; i++)
+		dscans[i].dc = 0;
+	return 0;
+}
+
+void jpeg_get_size(unsigned char *buf, int *width, int *height)
+{
+	datap = buf;
+	getbyte();
+	getbyte();
+	readtables(M_SOF0);
+	getword();
+	getbyte();
+	*height = getword();
+	*width = getword();
+}
+
+int jpeg_decode(unsigned char *buf, unsigned char *pic,
+		int width, int height, enum splash_color_format cf,
+		struct jpeg_decdata *decdata)
+{
+	int i, j, m, tac, tdc;
+	int mcusx, mcusy, mx, my;
+	int max[6];
+
+	if (!decdata || !buf || !pic)
+		return -1;
+	datap = buf;
+	if (getbyte() != 0xff)
+		return ERR_NO_SOI;
+	if (getbyte() != M_SOI)
+		return ERR_NO_SOI;
+	if (readtables(M_SOF0))
+		return ERR_BAD_TABLES;
+	getword();
+	i = getbyte();
+	if (i != 8)
+		return ERR_NOT_8BIT;
+	if (((getword() + 15) & ~15) != height)
+		return ERR_HEIGHT_MISMATCH;
+	if (((getword() + 15) & ~15) != width)
+		return ERR_WIDTH_MISMATCH;
+	if ((height & 15) || (width & 15))
+		return ERR_BAD_WIDTH_OR_HEIGHT;
+	info.nc = getbyte();
+	if (info.nc > MAXCOMP)
+		return ERR_TOO_MANY_COMPPS;
+	for (i = 0; i < info.nc; i++) {
+		int h, v;
+		comps[i].cid = getbyte();
+		comps[i].hv = getbyte();
+		v = comps[i].hv & 15;
+		h = comps[i].hv >> 4;
+		comps[i].tq = getbyte();
+		if (h > 3 || v > 3)
+			return ERR_ILLEGAL_HV;
+		if (comps[i].tq > 3)
+			return ERR_QUANT_TABLE_SELECTOR;
+	}
+	if (readtables(M_SOS))
+		return ERR_BAD_TABLES;
+	getword();
+	info.ns = getbyte();
+	if (info.ns != 3)
+		return ERR_NOT_YCBCR_221111;
+	for (i = 0; i < 3; i++) {
+		dscans[i].cid = getbyte();
+		tdc = getbyte();
+		tac = tdc & 15;
+		tdc >>= 4;
+		if (tdc > 1 || tac > 1)
+			return ERR_QUANT_TABLE_SELECTOR;
+		for (j = 0; j < info.nc; j++)
+			if (comps[j].cid == dscans[i].cid)
+				break;
+		if (j == info.nc)
+			return ERR_UNKNOWN_CID_IN_SCAN;
+		dscans[i].hv = comps[j].hv;
+		dscans[i].tq = comps[j].tq;
+		dscans[i].hudc.dhuff = dec_huffdc + tdc;
+		dscans[i].huac.dhuff = dec_huffac + tac;
+	}
+
+	i = getbyte();
+	j = getbyte();
+	m = getbyte();
+
+	if (i != 0 || j != 63 || m != 0)
+		return ERR_NOT_SEQUENTIAL_DCT;
+
+	if (dscans[0].cid != 1 || dscans[1].cid != 2 || dscans[2].cid != 3)
+		return ERR_NOT_YCBCR_221111;
+
+	if (dscans[0].hv != 0x22 ||
+	    dscans[1].hv != 0x11 ||
+	    dscans[2].hv != 0x11)
+		return ERR_NOT_YCBCR_221111;
+
+	mcusx = width >> 4;
+	mcusy = height >> 4;
+
+
+	idctqtab(quant[dscans[0].tq], decdata->dquant[0]);
+	idctqtab(quant[dscans[1].tq], decdata->dquant[1]);
+	idctqtab(quant[dscans[2].tq], decdata->dquant[2]);
+	initcol(decdata->dquant);
+	setinput(&in, datap);
+
+#if 0
+	/* landing zone */
+	img[len] = 0;
+	img[len + 1] = 0xff;
+	img[len + 2] = M_EOF;
+#endif
+
+	dec_initscans();
+
+	dscans[0].next = 6 - 4;
+	dscans[1].next = 6 - 4 - 1;
+	dscans[2].next = 6 - 4 - 1 - 1;	/* 411 encoding */
+	for (my = 0; my < mcusy; my++) {
+		for (mx = 0; mx < mcusx; mx++) {
+			if (info.dri && !--info.nm)
+				if (dec_checkmarker())
+					return ERR_WRONG_MARKER;
+
+			decode_mcus(&in, decdata->dcts, 6, dscans, max);
+			idct(decdata->dcts, decdata->out, decdata->dquant[0],
+			     IFIX(128.5), max[0]);
+			idct(decdata->dcts + 64,
+			     decdata->out + 64,
+			     decdata->dquant[0], IFIX(128.5), max[1]);
+			idct(decdata->dcts + 128,
+			     decdata->out + 128,
+			     decdata->dquant[0], IFIX(128.5), max[2]);
+			idct(decdata->dcts + 192,
+			     decdata->out + 192,
+			     decdata->dquant[0], IFIX(128.5), max[3]);
+			idct(decdata->dcts + 256,
+			     decdata->out + 256,
+			     decdata->dquant[1], IFIX(0.5), max[4]);
+			idct(decdata->dcts + 320,
+			     decdata->out + 320,
+			     decdata->dquant[2], IFIX(0.5), max[5]);
+
+			switch (cf) {
+			case SPLASH_DEPTH_24:
+				col221111_32(decdata->out,
+					     (pic + (my * 16 * mcusx + mx)
+					      * 16 * 4),
+					     mcusx * 16 * 4);
+				break;
+			case SPLASH_DEPTH_24_PACKED:
+				col221111(decdata->out,
+					  (pic + (my * 16 * mcusx + mx)
+					   * 16 * 3),
+					  mcusx * 16 * 3);
+				break;
+			case SPLASH_DEPTH_16:
+				col221111_16(decdata->out,
+					     (pic + (my * 16 * mcusx + mx)
+					      * 16 * 2), mcusx * 16 * 2);
+				break;
+			case SPLASH_DEPTH_15:
+				col221111_15(decdata->out,
+					     (pic + (my * 16 * mcusx + mx)
+					      * 16 * 2), mcusx * 16 * 2);
+				break;
+			default:
+				return ERR_DEPTH_MISMATCH;
+				break;
+			}
+		}
+	}
+
+	m = dec_readmarker(&in);
+	if (m != M_EOI)
+		return ERR_NO_EOI;
+
+	return 0;
+}
+
+/****************************************************************/
+/**************       huffman decoder             ***************/
+/****************************************************************/
+
+static int fillbits(struct in *, int, unsigned int);
+static int dec_rec2(struct in *, struct dec_hufftbl *, int *, int, int);
+
+static void setinput(struct in *in, unsigned char *p)
+{
+	in->p = p;
+	in->left = 0;
+	in->bits = 0;
+	in->marker = 0;
+}
+
+static int fillbits(struct in *in, int le, unsigned int bi)
+{
+	int b, m;
+
+	if (in->marker) {
+		if (le <= 16)
+			in->bits = bi << 16, le += 16;
+		return le;
+	}
+	while (le <= 24) {
+		b = *in->p++;
+		if (b == 0xff) {
+			m = *in->p++;
+			if (m != 0) {
+				if (m == M_EOF) {
+					if (in->func) {
+						m = in->func(in->data);
+						if  (m == 0)
+							continue;
+					}
+				}
+				in->marker = m;
+				if (le <= 16)
+					bi = bi << 16, le += 16;
+				break;
+			}
+		}
+		bi = bi << 8 | b;
+		le += 8;
+	}
+	in->bits = bi;		/* tmp... 2 return values needed */
+	return le;
+}
+
+static int dec_readmarker(struct in *in)
+{
+	int m;
+
+	in->left = fillbits(in, in->left, in->bits);
+	m = in->marker;
+	if (m == 0)
+		return 0;
+	in->left = 0;
+	in->marker = 0;
+	return m;
+}
+
+#define LEBI_DCL	int le, bi
+#define LEBI_GET(in)	(le = in->left, bi = in->bits)
+#define LEBI_PUT(in)	(in->left = le, in->bits = bi)
+
+#define GETBITS(in, n) \
+	(								\
+	 (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0),	\
+	 (le -= (n)),							\
+	 bi >> le & ((1 << (n)) - 1)					\
+									)
+
+#define UNGETBITS(in, n) (			\
+			  le += (n)		\
+						)
+
+
+static int dec_rec2(struct in *in, struct dec_hufftbl *hu, int *runp, int c, int i)
+{
+	LEBI_DCL;
+
+	LEBI_GET(in);
+	if (i) {
+		UNGETBITS(in, i & 127);
+		*runp = i >> 8 & 15;
+		i >>= 16;
+	} else {
+		for (i = DECBITS;
+		     (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]);
+		     i++)
+			;
+		if (i >= 16) {
+			in->marker = M_BADHUFF;
+			return 0;
+		}
+		i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2];
+		*runp = i >> 4;
+		i &= 15;
+	}
+	if (i == 0) {		/* sigh, 0xf0 is 11 bit */
+		LEBI_PUT(in);
+		return 0;
+	}
+	/* receive part */
+	c = GETBITS(in, i);
+	if (c < (1 << (i - 1)))
+		c += (-1 << i) + 1;
+	LEBI_PUT(in);
+	return c;
+}
+
+#define DEC_REC(in, hu, r, i)	 (					\
+				  r = GETBITS(in, DECBITS),		\
+				  i = hu->llvals[r],			\
+				  i & 128 ?				\
+				  (					\
+				   UNGETBITS(in, i & 127),		\
+				   r = i >> 8 & 15,			\
+				   i >> 16				\
+									) \
+				  :					\
+				  (					\
+				   LEBI_PUT(in),			\
+				   i = dec_rec2(in, hu, &r, r, i),	\
+				   LEBI_GET(in),			\
+				   i					\
+									) \
+									)
+
+static void decode_mcus(struct in *in, int *dct, int n, struct scan *sc, int *maxp)
+{
+	struct dec_hufftbl *hu;
+	int i, r, t;
+	LEBI_DCL;
+
+	memset(dct, 0, n * 64 * sizeof(*dct));
+	LEBI_GET(in);
+	while (n-- > 0) {
+		hu = sc->hudc.dhuff;
+		*dct++ = (sc->dc += DEC_REC(in, hu, r, t));
+
+		hu = sc->huac.dhuff;
+		i = 63;
+		while (i > 0) {
+			t = DEC_REC(in, hu, r, t);
+			if (t == 0 && r == 0) {
+				dct += i;
+				break;
+			}
+			dct += r;
+			*dct++ = t;
+			i -= r + 1;
+		}
+		*maxp++ = 64 - i;
+		if (n == sc->next)
+			sc++;
+	}
+	LEBI_PUT(in);
+}
+
+static void dec_makehuff(struct dec_hufftbl *hu, int *hufflen, unsigned char *huffvals)
+{
+	int code, k, i, j, d, x, c, v;
+	for (i = 0; i < (1 << DECBITS); i++)
+		hu->llvals[i] = 0;
+
+/*
+ * llvals layout:
+ *
+ * value v already known, run r, backup u bits:
+ *  vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu
+ * value unknown, size b bits, run r, backup u bits:
+ *  000000000000bbbb 0000 rrrr 0 uuuuuuu
+ * value and size unknown:
+ *  0000000000000000 0000 0000 0 0000000
+ */
+	code = 0;
+	k = 0;
+	for (i = 0; i < 16; i++, code <<= 1) {	/* sizes */
+		hu->valptr[i] = k;
+		for (j = 0; j < hufflen[i]; j++) {
+			hu->vals[k] = *huffvals++;
+			if (i < DECBITS) {
+				c = code << (DECBITS - 1 - i);
+				v = hu->vals[k] & 0x0f;	/* size */
+				for (d = 1 << (DECBITS - 1 - i); --d >= 0;) {
+					if (v + i < DECBITS) {
+						/* both fit in table */
+						x = d >> (DECBITS - 1 - v -
+							  i);
+						if (v && x < (1 << (v - 1)))
+							x += (-1 << v) + 1;
+						x = x << 16 |
+						  (hu->vals[k] & 0xf0) << 4
+						  | (DECBITS - (i + 1 + v))
+						  | 128;
+					} else
+						x = v << 16
+						  | (hu->vals[k] & 0xf0) << 4
+						  | (DECBITS - (i + 1));
+					hu->llvals[c | d] = x;
+				}
+			}
+			code++;
+			k++;
+		}
+		hu->maxcode[i] = code;
+	}
+	hu->maxcode[16] = 0x20000;	/* always terminate decode */
+}
+
+/****************************************************************/
+/**************             idct                  ***************/
+/****************************************************************/
+
+#define ONE ((PREC)IFIX(1.))
+#define S2  ((PREC)IFIX(0.382683432))
+#define C2  ((PREC)IFIX(0.923879532))
+#define C4  ((PREC)IFIX(0.707106781))
+
+#define S22 ((PREC)IFIX(2 * 0.382683432))
+#define C22 ((PREC)IFIX(2 * 0.923879532))
+#define IC4 ((PREC)IFIX(1 / 0.707106781))
+
+#define C3IC1 ((PREC)IFIX(0.847759065))	/* c3/c1 */
+#define C5IC1 ((PREC)IFIX(0.566454497))	/* c5/c1 */
+#define C7IC1 ((PREC)IFIX(0.198912367))	/* c7/c1 */
+
+#define XPP(a, b) (t = a + b, b = a - b, a = t)
+#define XMP(a, b) (t = a - b, b = a + b, a = t)
+#define XPM(a, b) (t = a + b, b = b - a, a = t)
+
+#define ROT(a, b, s, c) (t = IMULT(a + b, s),		\
+			 a = IMULT(a, c - s) + t,	\
+			 b = IMULT(b, c + s) - t)
+
+#define IDCT					\
+	(					\
+	 XPP(t0, t1),				\
+	 XMP(t2, t3),				\
+	 t2 = IMULT(t2, IC4) - t3,		\
+	 XPP(t0, t3),				\
+	 XPP(t1, t2),				\
+	 XMP(t4, t7),				\
+	 XPP(t5, t6),				\
+	 XMP(t5, t7),				\
+	 t5 = IMULT(t5, IC4),			\
+	 ROT(t4, t6, S22, C22),			\
+	 t6 -= t7,				\
+	 t5 -= t6,				\
+	 t4 -= t5,				\
+	 XPP(t0, t7),				\
+	 XPP(t1, t6),				\
+	 XPP(t2, t5),				\
+	 XPP(t3, t4)				\
+			)
+
+static unsigned char zig2[64] = {
+	0, 2, 3, 9, 10, 20, 21, 35,
+	14, 16, 25, 31, 39, 46, 50, 57,
+	5, 7, 12, 18, 23, 33, 37, 48,
+	27, 29, 41, 44, 52, 55, 59, 62,
+	15, 26, 30, 40, 45, 51, 56, 58,
+	1, 4, 8, 11, 19, 22, 34, 36,
+	28, 42, 43, 53, 54, 60, 61, 63,
+	6, 13, 17, 24, 32, 38, 47, 49
+};
+
+void idct(int *in, int *out, PREC *quant, PREC off, int max)
+{
+	PREC t0, t1, t2, t3, t4, t5, t6, t7, t;
+	PREC tmp[64], *tmpp;
+	int i, j;
+	unsigned char *zig2p;
+
+	t0 = off;
+	if (max == 1) {
+		t0 += in[0] * quant[0];
+		for (i = 0; i < 64; i++)
+			out[i] = ITOINT(t0);
+		return;
+	}
+	zig2p = zig2;
+	tmpp = tmp;
+	for (i = 0; i < 8; i++) {
+		j = *zig2p++;
+		t0 += in[j] * quant[j];
+		j = *zig2p++;
+		t5 = in[j] * quant[j];
+		j = *zig2p++;
+		t2 = in[j] * quant[j];
+		j = *zig2p++;
+		t7 = in[j] * quant[j];
+		j = *zig2p++;
+		t1 = in[j] * quant[j];
+		j = *zig2p++;
+		t4 = in[j] * quant[j];
+		j = *zig2p++;
+		t3 = in[j] * quant[j];
+		j = *zig2p++;
+		t6 = in[j] * quant[j];
+		IDCT;
+		tmpp[0 * 8] = t0;
+		tmpp[1 * 8] = t1;
+		tmpp[2 * 8] = t2;
+		tmpp[3 * 8] = t3;
+		tmpp[4 * 8] = t4;
+		tmpp[5 * 8] = t5;
+		tmpp[6 * 8] = t6;
+		tmpp[7 * 8] = t7;
+		tmpp++;
+		t0 = 0;
+	}
+	for (i = 0; i < 8; i++) {
+		t0 = tmp[8 * i + 0];
+		t1 = tmp[8 * i + 1];
+		t2 = tmp[8 * i + 2];
+		t3 = tmp[8 * i + 3];
+		t4 = tmp[8 * i + 4];
+		t5 = tmp[8 * i + 5];
+		t6 = tmp[8 * i + 6];
+		t7 = tmp[8 * i + 7];
+		IDCT;
+		out[8 * i + 0] = ITOINT(t0);
+		out[8 * i + 1] = ITOINT(t1);
+		out[8 * i + 2] = ITOINT(t2);
+		out[8 * i + 3] = ITOINT(t3);
+		out[8 * i + 4] = ITOINT(t4);
+		out[8 * i + 5] = ITOINT(t5);
+		out[8 * i + 6] = ITOINT(t6);
+		out[8 * i + 7] = ITOINT(t7);
+	}
+}
+
+static unsigned char zig[64] = {
+	0, 1, 5, 6, 14, 15, 27, 28,
+	2, 4, 7, 13, 16, 26, 29, 42,
+	3, 8, 12, 17, 25, 30, 41, 43,
+	9, 11, 18, 24, 31, 40, 44, 53,
+	10, 19, 23, 32, 39, 45, 52, 54,
+	20, 22, 33, 38, 46, 51, 55, 60,
+	21, 34, 37, 47, 50, 56, 59, 61,
+	35, 36, 48, 49, 57, 58, 62, 63
+};
+
+static PREC aaidct[8] = {
+	IFIX(0.3535533906), IFIX(0.4903926402),
+	IFIX(0.4619397663), IFIX(0.4157348062),
+	IFIX(0.3535533906), IFIX(0.2777851165),
+	IFIX(0.1913417162), IFIX(0.0975451610)
+};
+
+
+static void idctqtab(unsigned char *qin, PREC *qout)
+{
+	int i, j;
+
+	for (i = 0; i < 8; i++)
+		for (j = 0; j < 8; j++)
+			qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] *
+						IMULT(aaidct[i], aaidct[j]);
+}
+
+static void scaleidctqtab(PREC *q, PREC sc)
+{
+	int i;
+
+	for (i = 0; i < 64; i++)
+		q[i] = IMULT(q[i], sc);
+}
+
+/****************************************************************/
+/**************          color decoder            ***************/
+/****************************************************************/
+
+#define ROUND
+
+/*
+ * YCbCr Color transformation:
+ *
+ * y:0..255   Cb:-128..127   Cr:-128..127
+ *
+ *      R = Y                + 1.40200 * Cr
+ *      G = Y - 0.34414 * Cb - 0.71414 * Cr
+ *      B = Y + 1.77200 * Cb
+ *
+ * =>
+ *      Cr *= 1.40200;
+ *      Cb *= 1.77200;
+ *      Cg = 0.19421 * Cb + .50937 * Cr;
+ *      R = Y + Cr;
+ *      G = Y - Cg;
+ *      B = Y + Cb;
+ *
+ * =>
+ *      Cg = (50 * Cb + 130 * Cr + 128) >> 8;
+ */
+
+static void initcol(q)
+PREC q[][64];
+{
+	scaleidctqtab(q[1], IFIX(1.77200));
+	scaleidctqtab(q[2], IFIX(1.40200));
+}
+
+/* This is optimized for the stupid sun SUNWspro compiler. */
+#define STORECLAMP(a, x)			\
+	(					\
+	 (a) = (x),				\
+	 (unsigned int)(x) >= 256 ?		\
+	 ((a) = (x) < 0 ? 0 : 255)		\
+	 :					\
+	 0					\
+						)
+
+#define CLAMP(x) ((unsigned int)(x) >= 256 ? ((x) < 0 ? 0 : 255) : (x))
+
+#ifdef ROUND
+
+#define CBCRCG(yin, xin)			\
+	(					\
+	 cb = outc[0 + yin * 8 + xin],		\
+	 cr = outc[64 + yin * 8 + xin],		\
+	 cg = (50 * cb + 130 * cr + 128) >> 8	\
+						)
+
+#else
+
+#define CBCRCG(yin, xin)			\
+	(					\
+	 cb = outc[0 + yin * 8 + xin],		\
+	 cr = outc[64 + yin * 8 + xin],		\
+	 cg = (3 * cb + 8 * cr) >> 4		\
+						)
+
+#endif
+
+#define PIC(yin, xin, p, xout)			\
+	(					\
+	 y = outy[(yin) * 8 + xin],		\
+	 STORECLAMP(p[(xout) * 3 + 0], y + cr),	\
+	 STORECLAMP(p[(xout) * 3 + 1], y - cg),	\
+	 STORECLAMP(p[(xout) * 3 + 2], y + cb)	\
+						)
+
+#ifdef __LITTLE_ENDIAN
+#define PIC_15(yin, xin, p, xout, add)				\
+	(							\
+	 y = outy[(yin) * 8 + xin],				\
+	 y = ((CLAMP(y + cr + add * 2 + 1) & 0xf8) <<  7) |	\
+	 ((CLAMP(y - cg + add * 2 + 1) & 0xf8) <<  2) |		\
+	 ((CLAMP(y + cb + add * 2 + 1)) >>  3),			\
+	 p[(xout) * 2 + 0] = y & 0xff,				\
+	 p[(xout) * 2 + 1] = y >> 8				\
+						 )
+
+#define PIC_16(yin, xin, p, xout, add)			    \
+	(						    \
+	 y = outy[(yin) * 8 + xin],			    \
+	 y = ((CLAMP(y + cr + add * 2 + 1) & 0xf8) <<  8) | \
+	 ((CLAMP(y - cg + add) & 0xfc) <<  3) |		    \
+	 ((CLAMP(y + cb + add * 2 + 1)) >>  3),	    \
+	 p[(xout) * 2 + 0] = y & 0xff,			    \
+	 p[(xout) * 2 + 1] = y >> 8			    \
+						 )
+#else
+#define PIC_15(yin, xin, p, xout, add)				\
+	(							\
+	 y = outy[(yin) * 8 + xin],				\
+	 y = ((CLAMP(y + cr + add * 2 + 1) & 0xf8) <<  7) |	\
+	 ((CLAMP(y - cg + add * 2 + 1) & 0xf8) <<  2) |		\
+	 ((CLAMP(y + cb + add * 2 + 1)) >>  3),			\
+	 p[(xout) * 2 + 0] = y >> 8,				\
+	 p[(xout) * 2 + 1] = y & 0xff				\
+						 )
+
+#define PIC_16(yin, xin, p, xout, add)			    \
+	(						    \
+	 y = outy[(yin) * 8 + xin],			    \
+	 y = ((CLAMP(y + cr + add * 2 + 1) & 0xf8) <<  8) | \
+	 ((CLAMP(y - cg + add) & 0xfc) << 3) |		    \
+	 ((CLAMP(y + cb + add * 2 + 1)) >>  3),		    \
+	 p[(xout) * 2 + 0] = y >> 8,			    \
+	 p[(xout) * 2 + 1] = y & 0xff			    \
+						 )
+#endif
+
+#define PIC_32(yin, xin, p, xout)		\
+	(					\
+	 y = outy[(yin) * 8 + xin],		\
+	 STORECLAMP(p[(xout) * 4 + 0], y + cb),	\
+	 STORECLAMP(p[(xout) * 4 + 1], y - cg),	\
+	 STORECLAMP(p[(xout) * 4 + 2], y + cr),	\
+	 p[(xout) * 4 + 3] = 0			\
+						)
+
+#define PIC221111(xin)							\
+	(								\
+	 CBCRCG(0, xin),						\
+	 PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0),	\
+	 PIC(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1),	\
+	 PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0),	\
+	 PIC(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1)	\
+								)
+
+#define PIC221111_15(xin)                                               \
+	(								\
+	 CBCRCG(0, xin),						\
+	 PIC_15(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0, 3), \
+	 PIC_15(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1, 0), \
+	 PIC_15(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0, 1), \
+	 PIC_15(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1, 2) \
+									)
+
+#define PIC221111_16(xin)                                               \
+	(								\
+	 CBCRCG(0, xin),						\
+	 PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0, 3), \
+	 PIC_16(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1, 0), \
+	 PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0, 1), \
+	 PIC_16(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1, 2) \
+									)
+
+#define PIC221111_32(xin)					\
+	(							\
+	 CBCRCG(0, xin),						\
+	 PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 0, pic0, xin * 2 + 0),	\
+	 PIC_32(xin / 4 * 8 + 0, (xin & 3) * 2 + 1, pic0, xin * 2 + 1),	\
+	 PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 0, pic1, xin * 2 + 0),	\
+	 PIC_32(xin / 4 * 8 + 1, (xin & 3) * 2 + 1, pic1, xin * 2 + 1)	\
+								)
+
+static void col221111(int *out, unsigned char *pic, int width)
+{
+	int i, j, k;
+	unsigned char *pic0, *pic1;
+	int *outy, *outc;
+	int cr, cg, cb, y;
+
+	pic0 = pic;
+	pic1 = pic + width;
+	outy = out;
+	outc = out + 64 * 4;
+	for (i = 2; i > 0; i--) {
+		for (j = 4; j > 0; j--) {
+			for (k = 0; k < 8; k++)
+				PIC221111(k);
+			outc += 8;
+			outy += 16;
+			pic0 += 2 * width;
+			pic1 += 2 * width;
+		}
+		outy += 64 * 2 - 16 * 4;
+	}
+}
+
+static void col221111_15(int *out, unsigned char *pic, int width)
+{
+	int i, j, k;
+	unsigned char *pic0, *pic1;
+	int *outy, *outc;
+	int cr, cg, cb, y;
+
+	pic0 = pic;
+	pic1 = pic + width;
+	outy = out;
+	outc = out + 64 * 4;
+	for (i = 2; i > 0; i--) {
+		for (j = 4; j > 0; j--) {
+			for (k = 0; k < 8; k++)
+				PIC221111_15(k);
+			outc += 8;
+			outy += 16;
+			pic0 += 2 * width;
+			pic1 += 2 * width;
+		}
+		outy += 64 * 2 - 16 * 4;
+	}
+}
+
+static void col221111_16(int *out, unsigned char *pic, int width)
+{
+	int i, j, k;
+	unsigned char *pic0, *pic1;
+	int *outy, *outc;
+	int cr, cg, cb, y;
+
+	pic0 = pic;
+	pic1 = pic + width;
+	outy = out;
+	outc = out + 64 * 4;
+	for (i = 2; i > 0; i--) {
+		for (j = 4; j > 0; j--) {
+			for (k = 0; k < 8; k++)
+				PIC221111_16(k);
+			outc += 8;
+			outy += 16;
+			pic0 += 2 * width;
+			pic1 += 2 * width;
+		}
+		outy += 64 * 2 - 16 * 4;
+	}
+}
+
+static void col221111_32(int *out, unsigned char *pic, int width)
+{
+	int i, j, k;
+	unsigned char *pic0, *pic1;
+	int *outy, *outc;
+	int cr, cg, cb, y;
+
+	pic0 = pic;
+	pic1 = pic + width;
+	outy = out;
+	outc = out + 64 * 4;
+	for (i = 2; i > 0; i--) {
+		for (j = 4; j > 0; j--) {
+			for (k = 0; k < 8; k++)
+				PIC221111_32(k);
+			outc += 8;
+			outy += 16;
+			pic0 += 2 * width;
+			pic1 += 2 * width;
+		}
+		outy += 64 * 2 - 16 * 4;
+	}
+}
--- /dev/null
+++ b/drivers/video/bootsplash/decode-jpg.h
@@ -0,0 +1,37 @@
+/*
+ *    linux/drivers/video/bootsplash/decode-jpg.h - a tiny jpeg decoder.
+ *
+ *      (w) August 2001 by Michael Schroeder, <mls@suse.de>
+ */
+
+#ifndef __DECODE_JPG_H
+#define __DECODE_JPG_H
+
+#define ERR_NO_SOI 1
+#define ERR_NOT_8BIT 2
+#define ERR_HEIGHT_MISMATCH 3
+#define ERR_WIDTH_MISMATCH 4
+#define ERR_BAD_WIDTH_OR_HEIGHT 5
+#define ERR_TOO_MANY_COMPPS 6
+#define ERR_ILLEGAL_HV 7
+#define ERR_QUANT_TABLE_SELECTOR 8
+#define ERR_NOT_YCBCR_221111 9
+#define ERR_UNKNOWN_CID_IN_SCAN 10
+#define ERR_NOT_SEQUENTIAL_DCT 11
+#define ERR_WRONG_MARKER 12
+#define ERR_NO_EOI 13
+#define ERR_BAD_TABLES 14
+#define ERR_DEPTH_MISMATCH 15
+
+struct jpeg_decdata {
+	int dcts[6 * 64 + 16];
+	int out[64 * 6];
+	int dquant[3][64];
+};
+
+extern int jpeg_decode(unsigned char *buf, unsigned char *pic,
+		       int width, int height, enum splash_color_format cf,
+		       struct jpeg_decdata *);
+extern void jpeg_get_size(unsigned char *, int *, int *);
+
+#endif
--- /dev/null
+++ b/drivers/video/bootsplash/render.c
@@ -0,0 +1,517 @@
+/*
+ *    linux/drivers/video/bootsplash/render.c - splash screen render functions.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/fb.h>
+#include <linux/vt_kern.h>
+#include <linux/selection.h>
+#include <asm/irq.h>
+
+#include "../console/fbcon.h"
+#include <linux/bootsplash.h>
+
+#ifndef DEBUG
+# define SPLASH_DEBUG(fmt, args...)
+#else
+# define SPLASH_DEBUG(fmt, args...) \
+	printk(KERN_WARNING "%s: " fmt "\n", __func__, ##args)
+#endif
+
+/* fake a region sync */
+void splash_sync_region(struct fb_info *info, int x, int y,
+			int width, int height)
+{
+	struct splash_data *sd = info->splash_data;
+	if (sd && sd->need_sync) {
+		/* issue a fake copyarea (copy to the very same position)
+		 * for marking the dirty region; this is required for Xen fb
+		 * (bnc#739020)
+		 */
+		struct fb_copyarea area;
+		area.sx = area.dx = x;
+		area.sy = area.dy = y;
+		area.width = width;
+		area.height = height;
+		info->fbops->fb_copyarea(info, &area);
+	}
+}
+
+void splash_putcs(struct vc_data *vc, struct fb_info *info,
+		   const unsigned short *s, int count, int ypos, int xpos)
+{
+	struct splash_data *sd;
+	unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff;
+	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+	int fgshift = (vc->vc_hi_font_mask) ? 9 : 8;
+	union pt src;
+	union pt dst, splashsrc;
+	unsigned int d, x, y;
+	u32 dd, fgx, bgx;
+	u16 c = scr_readw(s);
+	int fg_color, bg_color, transparent;
+	int n;
+	int octpp = (info->var.bits_per_pixel + 1) >> 3;
+	int drawn_width;
+
+	if (!oops_in_progress
+	    && (console_blanked || info->splash_data->splash_dosilent))
+		return;
+	sd = info->splash_data;
+
+	fg_color = attr_fgcol(fgshift, c);
+	bg_color = attr_bgcol(bgshift, c);
+	transparent = sd->imgd->splash_color == bg_color;
+	xpos = xpos * vc->vc_font.width + sd->imgd->splash_text_xo;
+	ypos = ypos * vc->vc_font.height + sd->imgd->splash_text_yo;
+	splashsrc.ub = (u8 *)(sd->pic->splash_pic
+			      + ypos * sd->pic->splash_pic_stride
+			      + xpos * octpp);
+	dst.ub = (u8 *)(info->screen_base
+			+ ypos * info->fix.line_length
+			+ xpos * octpp);
+	fgx = ((u32 *)info->pseudo_palette)[fg_color];
+	if (transparent && sd->imgd->splash_color == 15) {
+		if (fgx == 0xffea)
+			fgx = 0xfe4a;
+		else if (fgx == 0x57ea)
+			fgx = 0x0540;
+		else if (fgx == 0xffff)
+			fgx = 0x52aa;
+	}
+	bgx = ((u32 *)info->pseudo_palette)[bg_color];
+	d = 0;
+	drawn_width = 0;
+	while (count--) {
+		c = scr_readw(s++);
+		src.ub = vc->vc_font.data
+			+ ((c & charmask)
+			   * vc->vc_font.height
+			   * ((vc->vc_font.width + 7) >> 3));
+		for (y = 0; y < vc->vc_font.height; y++) {
+			for (x = 0; x < vc->vc_font.width; ) {
+				if ((x & 7) == 0)
+					d = *src.ub++;
+				switch (octpp) {
+				case 2:
+					if (d & 0x80)
+						dd = fgx;
+					else
+						dd = (transparent ?
+						      *splashsrc.us : bgx);
+					splashsrc.us += 1;
+					if (d & 0x40)
+						dd |= fgx << 16;
+					else
+						dd |= (transparent ? *splashsrc.us : bgx) << 16;
+					splashsrc.us += 1;
+					d <<= 2;
+					x += 2;
+					fb_writel(dd, dst.ul);
+					dst.ul += 1;
+					break;
+				case 3:
+					for (n = 0; n <= 16; n += 8) {
+						if (d & 0x80)
+							dd = (fgx >> n) & 0xff;
+						else
+							dd = (transparent ? *splashsrc.ul : ((bgx >> n) & 0xff));
+						splashsrc.ub += 1;
+						fb_writeb(dd, dst.ub);
+						dst.ub += 1;
+					}
+					d <<= 1;
+					x += 1;
+					break;
+				case 4:
+					if (d & 0x80)
+						dd = fgx;
+					else
+						dd = (transparent ? *splashsrc.ul : bgx);
+					splashsrc.ul += 1;
+					d <<= 1;
+					x += 1;
+					fb_writel(dd, dst.ul);
+					dst.ul += 1;
+					break;
+				}
+			}
+			dst.ub += info->fix.line_length
+				- vc->vc_font.width * octpp;
+			splashsrc.ub += sd->pic->splash_pic_stride
+				- vc->vc_font.width * octpp;
+		}
+		dst.ub -= info->fix.line_length * vc->vc_font.height
+			- vc->vc_font.width * octpp;
+		splashsrc.ub -= sd->pic->splash_pic_stride * vc->vc_font.height
+			- vc->vc_font.width * octpp;
+		drawn_width += vc->vc_font.width;
+	}
+	splash_sync_region(info, xpos, ypos, drawn_width, vc->vc_font.height);
+}
+
+static void splash_renderc(struct fb_info *info,
+			   int fg_color, int bg_color,
+			   u8 *src,
+			   int ypos, int xpos,
+			   int height, int width)
+{
+	struct splash_data *sd;
+	int transparent;
+	u32 dd, fgx, bgx;
+	union pt dst, splashsrc;
+	unsigned int d, x, y;
+	int n;
+	int octpp = (info->var.bits_per_pixel + 1) >> 3;
+
+	if (!oops_in_progress
+	    && (console_blanked || info->splash_data->splash_dosilent))
+		return;
+
+	sd = info->splash_data;
+
+	transparent = sd->imgd->splash_color == bg_color;
+	splashsrc.ub = (u8 *)(sd->pic->splash_pic
+			     + ypos * sd->pic->splash_pic_stride
+			     + xpos * octpp);
+	dst.ub = (u8 *)(info->screen_base
+		       + ypos * info->fix.line_length
+		       + xpos * octpp);
+	fgx = ((u32 *)info->pseudo_palette)[fg_color];
+	if (transparent && (sd->imgd->splash_color == 15)) {
+		if (fgx == 0xffea)
+			fgx = 0xfe4a;
+		else if (fgx == 0x57ea)
+			fgx = 0x0540;
+		else if (fgx == 0xffff)
+			fgx = 0x52aa;
+	}
+	bgx = ((u32 *)info->pseudo_palette)[bg_color];
+	d = 0;
+	for (y = 0; y < height; y++) {
+		for (x = 0; x < width; ) {
+			if ((x & 7) == 0)
+				d = *src++;
+			switch (octpp) {
+			case 2:
+				if (d & 0x80)
+					dd = fgx;
+				else
+					dd = (transparent ? *splashsrc.us : bgx);
+				splashsrc.us += 1;
+				if (d & 0x40)
+					dd |= fgx << 16;
+				else
+					dd |= (transparent ? *splashsrc.us : bgx) << 16;
+				splashsrc.us += 1;
+				d <<= 2;
+				x += 2;
+				fb_writel(dd, dst.ul);
+				dst.ul += 1;
+				break;
+			case 3:
+				for (n = 0; n <= 16; n += 8) {
+					if (d & 0x80)
+						dd = (fgx >> n) & 0xff;
+					else
+						dd = (transparent ? *splashsrc.ub : bgx);
+					splashsrc.ub += 1;
+					fb_writeb(dd, dst.ub);
+					dst.ub += 1;
+				}
+				d <<= 1;
+				x += 1;
+				break;
+			case 4:
+				if (d & 0x80)
+					dd = fgx;
+				else
+					dd = (transparent ? *splashsrc.ul : bgx);
+				splashsrc.ul += 1;
+				d <<= 1;
+				x += 1;
+				fb_writel(dd, dst.ul);
+				dst.ul += 1;
+				break;
+			}
+		}
+		dst.ub += info->fix.line_length - width * octpp;
+		splashsrc.ub += sd->pic->splash_pic_stride - width * octpp;
+	}
+	splash_sync_region(info, xpos, ypos, width, height);
+}
+
+void splashcopy(u8 *dst, u8 *src, int height, int width,
+		int dstbytes, int srcbytes, int octpp)
+{
+	int i;
+
+	width *= octpp;
+	while (height-- > 0) {
+		union pt p, q;
+		p.ul = (u32 *)dst;
+		q.ul = (u32 *)src;
+		for (i = 0; i < width / 4; i++)
+			fb_writel(*q.ul++, p.ul++);
+		if (width & 2)
+			fb_writew(*q.us++, p.us++);
+		if (width & 1)
+			fb_writeb(*q.ub, p.ub);
+		dst += dstbytes;
+		src += srcbytes;
+	}
+}
+
+static void splashset(u8 *dst, int height, int width,
+		      int dstbytes, u32 bgx, int octpp) {
+	int i;
+
+	width *= octpp;
+	if (octpp == 2)
+		bgx |= bgx << 16;
+	while (height-- > 0) {
+		union pt p;
+		p.ul = (u32 *)dst;
+		if (!(octpp & 1)) {
+			for (i = 0; i < width / 4; i++)
+				fb_writel(bgx, p.ul++);
+			if (width & 2)
+				fb_writew(bgx, p.us++);
+			if (width & 1)
+				fb_writeb(bgx, p.ub);
+			dst += dstbytes;
+		} else { /* slow! */
+			for (i = 0; i < width; i++)
+				fb_writeb((bgx >> ((i % 3) * 8)) & 0xff,
+					  p.ub++);
+		}
+	}
+}
+
+static void splashfill(struct fb_info *info, int sy, int sx,
+		       int height, int width) {
+	int octpp = (info->var.bits_per_pixel + 1) >> 3;
+	struct splash_data *sd = info->splash_data;
+
+	splashcopy((u8 *)(info->screen_base
+			  + sy * info->fix.line_length + sx * octpp),
+		   (u8 *)(sd->pic->splash_pic
+			  + sy * sd->pic->splash_pic_stride
+			  + sx * octpp),
+		   height, width, info->fix.line_length,
+		   sd->pic->splash_pic_stride,
+		   octpp);
+	splash_sync_region(info, sx, sy, width, height);
+}
+
+void splash_clear(struct vc_data *vc, struct fb_info *info, int sy,
+			int sx, int height, int width)
+{
+	struct splash_data *sd;
+	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
+	int bg_color = attr_bgcol_ec(bgshift, vc, info);
+	int transparent;
+	int octpp = (info->var.bits_per_pixel + 1) >> 3;
+	u32 bgx;
+	u8 *dst;
+
+	if (!oops_in_progress
+	    && (console_blanked || info->splash_data->splash_dosilent))
+		return;
+
+	sd = info->splash_data;
+
+	transparent = sd->imgd->splash_color == bg_color;
+
+	sy = sy * vc->vc_font.height + sd->imgd->splash_text_yo;
+	sx = sx * vc->vc_font.width + sd->imgd->splash_text_xo;
+	height *= vc->vc_font.height;
+	width *= vc->vc_font.width;
+	if (transparent) {
+		splashfill(info, sy, sx, height, width);
+		return;
+	}
+	dst = (u8 *)(info->screen_base
+		     + sy * info->fix.line_length
+		     + sx * octpp);
+	bgx = ((u32 *)info->pseudo_palette)[bg_color];
+	splashset(dst,
+		  height, width,
+		  info->fix.line_length,
+		  bgx,
+		  (info->var.bits_per_pixel + 1) >> 3);
+	splash_sync_region(info, sx, sy, width, height);
+}
+
+void splash_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+		int sx, int dy, int dx, int height, int width)
+{
+	struct splash_data *sd;
+	struct fb_copyarea area;
+
+	if (!oops_in_progress
+	    && (console_blanked || info->splash_data->splash_dosilent))
+		return;
+
+	sd = info->splash_data;
+
+	area.sx = sx * vc->vc_font.width;
+	area.sy = sy * vc->vc_font.height;
+	area.dx = dx * vc->vc_font.width;
+	area.dy = dy * vc->vc_font.height;
+	area.sx += sd->imgd->splash_text_xo;
+	area.sy += sd->imgd->splash_text_yo;
+	area.dx += sd->imgd->splash_text_xo;
+	area.dy += sd->imgd->splash_text_yo;
+	area.height = height * vc->vc_font.height;
+	area.width = width * vc->vc_font.width;
+
+	info->fbops->fb_copyarea(info, &area);
+}
+
+void splash_clear_margins(struct vc_data *vc, struct fb_info *info,
+				int bottom_only)
+{
+	struct splash_data *sd;
+	unsigned int tw = vc->vc_cols*vc->vc_font.width;
+	unsigned int th = vc->vc_rows*vc->vc_font.height;
+	SPLASH_DEBUG();
+
+	if (!oops_in_progress
+	    && (console_blanked || info->splash_data->splash_dosilent))
+		return;
+
+	sd = info->splash_data;
+
+	if (!bottom_only) {
+		/* top margin */
+		splashfill(info,
+			   0,
+			   0,
+			   sd->imgd->splash_text_yo,
+			   info->var.xres);
+		/* left margin */
+		splashfill(info,
+			   sd->imgd->splash_text_yo,
+			   0,
+			   th,
+			   sd->imgd->splash_text_xo);
+		/* right margin */
+		splashfill(info,
+			   sd->imgd->splash_text_yo,
+			   sd->imgd->splash_text_xo + tw,
+			   th,
+			   info->var.xres - sd->imgd->splash_text_xo - tw);
+	}
+	splashfill(info,
+		   sd->imgd->splash_text_yo + th,
+		   0,
+		   info->var.yres - sd->imgd->splash_text_yo - th,
+		   info->var.xres);
+}
+
+int splash_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+	struct splash_data *sd;
+	int i;
+	unsigned int dsize, s_pitch;
+
+	if (info->state != FBINFO_STATE_RUNNING)
+		return 0;
+
+	sd = info->splash_data;
+
+	s_pitch = (cursor->image.width + 7) >> 3;
+	dsize = s_pitch * cursor->image.height;
+	if (cursor->enable) {
+		switch (cursor->rop) {
+		case ROP_XOR:
+			for (i = 0; i < dsize; i++)
+				info->fb_cursordata[i] = cursor->image.data[i]
+					^ cursor->mask[i];
+			break;
+		case ROP_COPY:
+		default:
+			for (i = 0; i < dsize; i++)
+				info->fb_cursordata[i] = cursor->image.data[i]
+					& cursor->mask[i];
+			break;
+		}
+	} else if (info->fb_cursordata != cursor->image.data)
+		memcpy(info->fb_cursordata, cursor->image.data, dsize);
+	cursor->image.data = info->fb_cursordata;
+	splash_renderc(info, cursor->image.fg_color, cursor->image.bg_color,
+		       (u8 *)info->fb_cursordata,
+		       cursor->image.dy + sd->imgd->splash_text_yo,
+		       cursor->image.dx + sd->imgd->splash_text_xo,
+		       cursor->image.height,
+		       cursor->image.width);
+	return 0;
+}
+
+void splash_bmove_redraw(struct vc_data *vc, struct fb_info *info,
+			 int y, int sx, int dx, int width)
+{
+	struct splash_data *sd;
+	unsigned short *d = (unsigned short *) (vc->vc_origin
+						+ vc->vc_size_row * y
+						+ dx * 2);
+	unsigned short *s = d + (dx - sx);
+	unsigned short *start = d;
+	unsigned short *ls = d;
+	unsigned short *le = d + width;
+	unsigned short c;
+	int x = dx;
+	unsigned short attr = 1;
+
+	if (console_blanked || info->splash_data->splash_dosilent)
+		return;
+
+	sd = info->splash_data;
+
+	do {
+		c = scr_readw(d);
+		if (attr != (c & 0xff00)) {
+			attr = c & 0xff00;
+			if (d > start) {
+				splash_putcs(vc, info, start, d - start, y, x);
+				x += d - start;
+				start = d;
+			}
+		}
+		if (s >= ls && s < le && c == scr_readw(s)) {
+			if (d > start) {
+				splash_putcs(vc, info, start, d - start, y, x);
+				x += d - start + 1;
+				start = d + 1;
+			} else {
+				x++;
+				start++;
+			}
+		}
+		s++;
+		d++;
+	} while (d < le);
+	if (d > start)
+		splash_putcs(vc, info, start, d - start, y, x);
+}
+
+void splash_blank(struct vc_data *vc, struct fb_info *info, int blank)
+{
+	SPLASH_DEBUG();
+
+	if (blank) {
+		splashset((u8 *)info->screen_base,
+			  info->var.yres, info->var.xres,
+			  info->fix.line_length,
+			  0,
+			  (info->var.bits_per_pixel + 1) >> 3);
+		splash_sync_region(info, 0, 0, info->var.xres, info->var.yres);
+	} else {
+		/* splash_prepare(vc, info);  *//* do we really need this? */
+		splash_clear_margins(vc, info, 0);
+		/* no longer needed, done in fbcon_blank */
+		/* update_screen(vc->vc_num); */
+	}
+}
--- a/drivers/video/console/bitblit.c
+++ b/drivers/video/console/bitblit.c
@@ -19,6 +19,9 @@
 #include <asm/types.h>
 #include "fbcon.h"
 
+#include <linux/bootsplash.h>
+
+
 /*
  * Accelerated handlers.
  */
@@ -48,6 +51,12 @@ static void bit_bmove(struct vc_data *vc
 {
 	struct fb_copyarea area;
 
+	if (SPLASH_DATA(info)) {
+		splash_bmove(vc, info,
+			sy, sx, dy, dx, height, width);
+		return;
+	}
+
 	area.sx = sx * vc->vc_font.width;
 	area.sy = sy * vc->vc_font.height;
 	area.dx = dx * vc->vc_font.width;
@@ -64,6 +73,12 @@ static void bit_clear(struct vc_data *vc
 	int bgshift = (vc->vc_hi_font_mask) ? 13 : 12;
 	struct fb_fillrect region;
 
+	if (SPLASH_DATA(info)) {
+		splash_clear(vc, info,
+			     sy, sx, height, width);
+		return;
+	}
+
 	region.color = attr_bgcol_ec(bgshift, vc, info);
 	region.dx = sx * vc->vc_font.width;
 	region.dy = sy * vc->vc_font.height;
@@ -161,6 +176,11 @@ static void bit_putcs(struct vc_data *vc
 	image.height = vc->vc_font.height;
 	image.depth = 1;
 
+	if (SPLASH_DATA(info)) {
+		splash_putcs(vc, info, s, count, yy, xx);
+		return;
+	}
+
 	if (attribute) {
 		buf = kmalloc(cellsize, GFP_KERNEL);
 		if (!buf)
@@ -214,6 +234,11 @@ static void bit_clear_margins(struct vc_
 	unsigned int bs = info->var.yres - bh;
 	struct fb_fillrect region;
 
+	if (SPLASH_DATA(info)) {
+		splash_clear_margins(vc, info, bottom_only);
+		return;
+	}
+
 	region.color = attr_bgcol_ec(bgshift, vc, info);
 	region.rop = ROP_COPY;
 
@@ -380,6 +405,12 @@ static void bit_cursor(struct vc_data *v
 	cursor.image.depth = 1;
 	cursor.rop = ROP_XOR;
 
+	if (SPLASH_DATA(info)) {
+		splash_cursor(info, &cursor);
+		ops->cursor_reset = 0;
+		return;
+	}
+
 	if (info->fbops->fb_cursor)
 		err = info->fbops->fb_cursor(info, &cursor);
 
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -79,6 +79,7 @@
 #include <asm/irq.h>
 
 #include "fbcon.h"
+#include <linux/bootsplash.h>
 
 #ifdef FBCONDEBUG
 #  define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args)
@@ -94,7 +95,11 @@ enum {
 
 static struct display fb_display[MAX_NR_CONSOLES];
 
+#ifdef CONFIG_BOOTSPLASH
+signed char con2fb_map[MAX_NR_CONSOLES];
+#else
 static signed char con2fb_map[MAX_NR_CONSOLES];
+#endif
 static signed char con2fb_map_boot[MAX_NR_CONSOLES];
 
 static int logo_lines;
@@ -535,6 +540,8 @@ static int fbcon_takeover(int show_logo)
 	for (i = first_fb_vc; i <= last_fb_vc; i++)
 		con2fb_map[i] = info_idx;
 
+	splash_init();
+
 	err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
 				fbcon_is_default);
 
@@ -1098,6 +1105,21 @@ static void fbcon_init(struct vc_data *v
 	new_cols /= vc->vc_font.width;
 	new_rows /= vc->vc_font.height;
 
+#ifdef CONFIG_BOOTSPLASH
+	if (vc->vc_splash_data && vc->vc_splash_data->splash_state) {
+		new_cols = vc->vc_splash_data->splash_vc_text_wi
+			/ vc->vc_font.width;
+		new_rows = vc->vc_splash_data->splash_vc_text_he
+			/ vc->vc_font.height;
+		logo = 0;
+		con_remap_def_color(vc,
+				    (vc->vc_splash_data->imgd->splash_color
+				     << 4) |
+				    vc->vc_splash_data->imgd->splash_fg_color);
+	}
+#endif
+
+
 	/*
 	 * We must always set the mode. The mode of the previous console
 	 * driver could be in the same resolution but we are using different
@@ -1799,6 +1821,8 @@ static int fbcon_scroll(struct vc_data *
 			fbcon_softback_note(vc, t, count);
 		if (logo_shown >= 0)
 			goto redraw_up;
+		if (SPLASH_DATA(info))
+			goto redraw_up;
 		switch (p->scrollmode) {
 		case SCROLL_MOVE:
 			fbcon_redraw_blit(vc, info, p, t, b - t - count,
@@ -1890,6 +1914,8 @@ static int fbcon_scroll(struct vc_data *
 			count = vc->vc_rows;
 		if (logo_shown >= 0)
 			goto redraw_down;
+		if (SPLASH_DATA(info))
+			goto redraw_down;
 		switch (p->scrollmode) {
 		case SCROLL_MOVE:
 			fbcon_redraw_blit(vc, info, p, b - 1, b - t - count,
@@ -2038,6 +2064,12 @@ static void fbcon_bmove_rec(struct vc_da
 		}
 		return;
 	}
+
+	if (SPLASH_DATA(info) && sy == dy && height == 1) {
+		/*must use slower redraw bmove to keep background pic intact*/
+		splash_bmove_redraw(vc, info, sy, sx, dx, width);
+		return;
+	}
 	ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx,
 		   height, width);
 }
@@ -2146,6 +2178,23 @@ static int fbcon_switch(struct vc_data *
 	info = registered_fb[con2fb_map[vc->vc_num]];
 	ops = info->fbcon_par;
 
+#ifdef CONFIG_BOOTSPLASH
+	{
+		struct splash_data *prev_sd = vc->vc_splash_data;
+		splash_prepare(vc, info);
+		if (vc->vc_splash_data && vc->vc_splash_data->splash_state &&
+		    vc->vc_splash_data != prev_sd) {
+			vc_resize(vc, vc->vc_splash_data->splash_vc_text_wi
+				  / vc->vc_font.width,
+				  vc->vc_splash_data->splash_vc_text_he
+				  / vc->vc_font.height);
+			con_remap_def_color(vc,
+					    vc->vc_splash_data->imgd->splash_color << 4
+					    | vc->vc_splash_data->imgd->splash_fg_color);
+		}
+	}
+#endif
+
 	if (softback_top) {
 		if (softback_lines)
 			fbcon_set_origin(vc);
@@ -2279,6 +2328,11 @@ static void fbcon_generic_blank(struct v
 {
 	struct fb_event event;
 
+	if (SPLASH_DATA(info)) {
+		splash_blank(vc, info, blank);
+		return;
+	}
+
 	if (blank) {
 		unsigned short charmask = vc->vc_hi_font_mask ?
 			0x1ff : 0xff;
@@ -2504,6 +2558,10 @@ static int fbcon_do_set_font(struct vc_d
 
 		cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres);
 		rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres);
+		if (SPLASH_DATA(info)) {
+			cols = TEXT_WIDTH_FROM_SPLASH_DATA(info);
+			rows = TEXT_HIGHT_FROM_SPLASH_DATA(info);
+		}
 		cols /= w;
 		rows /= h;
 		vc_resize(vc, cols, rows);
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -25,6 +25,54 @@
     *    low-level frame buffer device
     */
 
+#ifdef CONFIG_BOOTSPLASH
+struct splash_img_data {
+	int ref_cnt;
+	int splash_color;	/* transparent color */
+	int splash_fg_color;	/* foreground color */
+	int splash_width;	/* width of image */
+	int splash_height;	/* height of image */
+	int splash_text_xo;	/* text area origin of origin */
+	int splash_text_yo;
+	int splash_text_wi;	/* text area size of jpeg*/
+	int splash_text_he;
+	int splash_boxcount;
+	int splash_sboxcount;
+	int splash_overpaintok;	/* is it ok to overpaint boxes */
+	unsigned char *splash_boxes;
+	unsigned char *splash_jpeg;		/* jpeg */
+	unsigned char *splash_sboxes;
+	unsigned char *splash_silentjpeg;
+	unsigned char *splash_palette;		/* palette for 8-bit */
+};
+
+struct splash_pic_data {
+	int ref_cnt;
+	unsigned char *splash_pic;
+	int splash_pic_stride;
+	int splash_pic_size;
+};
+
+struct splash_data {
+	struct splash_data *next;
+	struct splash_img_data *imgd;
+	struct splash_pic_data *pic;
+	int splash_state;			/* show splash? */
+	int splash_percent;
+	int splash_dosilent;			/* show silent jpeg */
+
+	int splash_vc_text_wi;			/* text area size used*/
+	int splash_vc_text_he;
+	int splash_boxes_xoff;
+	int splash_boxes_yoff;
+	int splash_sboxes_xoff;
+	int splash_sboxes_yoff;
+
+	bool color_set;
+	bool need_sync;
+};
+#endif
+
 struct display {
     /* Filled in by the low-level console driver */
     const u_char *fontdata;
--- /dev/null
+++ b/include/linux/bootsplash.h
@@ -0,0 +1,87 @@
+/*
+ *    linux/drivers/video/bootsplash/bootsplash.h - splash screen definition.
+ *
+ *	(w) 2001-2003 by Volker Poplawski, <volker@poplawski.de>
+ *		Stefan Reinauer, <stepan@suse.de>
+ *
+ *
+ *	idea and SuSE screen work by Ken Wimer, <wimer@suse.de>
+ */
+
+#ifndef __BOOTSPLASH_H
+#define __BOOTSPLASH_H
+
+# ifdef CONFIG_BOOTSPLASH
+
+struct fb_info;
+union pt {
+	u32 *ul;
+	u16 *us;
+	u8  *ub;
+};
+
+enum splash_color_format {
+	SPLASH_DEPTH_UNKNOWN = 0,
+	SPLASH_DEPTH_15 = 15,
+	SPLASH_DEPTH_16 = 16,
+	SPLASH_DEPTH_24_PACKED = 24,
+	SPLASH_DEPTH_24 = 32
+};
+
+#define splash_octpp(cf) (((int)cf + 1) >> 3)
+
+struct vc_data;
+struct fb_info;
+struct fb_cursor;
+struct splash_data;
+
+/* splash.c */
+extern int splash_prepare(struct vc_data *, struct fb_info *);
+extern void splash_init(void);
+extern int splash_verbose(void);
+
+/* splash_render.c */
+extern void splash_putcs(struct vc_data *vc, struct fb_info *info,
+			const unsigned short *s, int count,
+			 int ypos, int xpos);
+extern void splash_sync_region(struct fb_info *info, int x, int y,
+			       int width, int height);
+extern void splashcopy(u8 *dst, u8 *src, int height, int width,
+		       int dstbytes, int srcbytes, int octpp);
+extern void splash_clear(struct vc_data *vc, struct fb_info *info, int sy,
+			int sx, int height, int width);
+extern void splash_bmove(struct vc_data *vc, struct fb_info *info, int sy,
+			int sx, int dy, int dx, int height, int width);
+extern void splash_clear_margins(struct vc_data *vc, struct fb_info *info,
+			int bottom_only);
+extern int splash_cursor(struct fb_info *info, struct fb_cursor *cursor);
+extern void splash_bmove_redraw(struct vc_data *vc, struct fb_info *info,
+			int y, int sx, int dx, int width);
+extern void splash_blank(struct vc_data *vc, struct fb_info *info,
+			int blank);
+
+#  define SPLASH_VERBOSE() splash_verbose()
+#  define SPLASH_DATA(x) (x->splash_data)
+#  define TEXT_WIDTH_FROM_SPLASH_DATA(x) (x->splash_data->splash_vc_text_wi)
+#  define TEXT_HIGHT_FROM_SPLASH_DATA(x) (x->splash_data->splash_vc_text_he)
+/* vt.c */
+extern void con_remap_def_color(struct vc_data *vc, int new_color);
+
+# else
+#  define splash_init()
+#  define splash_verbose() 0
+#  define SPLASH_VERBOSE()
+#  define splash_blank(vc, info, blank)
+#  define splash_bmove(vc, info, sy, sx, dy, dx, height, width)
+#  define splash_bmove_redraw(vc, info, sy, sx, dx, width)
+#  define splash_cursor(info, cursor)
+#  define splash_clear(vc, info, sy, sx, height, width)
+#  define splash_clear_margins(vc, info, bottom_only)
+#  define splash_putcs(vc, info, s, count, ypos, xpos)
+
+#  define SPLASH_DATA(x) 0
+#  define TEXT_WIDTH_FROM_SPLASH_DATA(x) 0
+#  define TEXT_HIGHT_FROM_SPLASH_DATA(x) 0
+# endif
+
+#endif
--- a/include/linux/console_struct.h
+++ b/include/linux/console_struct.h
@@ -107,6 +107,9 @@ struct vc_data {
 	unsigned long	vc_uni_pagedir;
 	unsigned long	*vc_uni_pagedir_loc;  /* [!] Location of uni_pagedir variable for this console */
 	bool vc_panic_force_write; /* when oops/panic this VC can accept forced output/blanking */
+#ifdef CONFIG_BOOTSPLASH
+	struct splash_data *vc_splash_data;
+#endif
 	/* additional information is in vt_kern.h */
 };
 
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -883,6 +883,10 @@ struct fb_info {
 	void *fbcon_par;                /* fbcon use-only private area */
 	/* From here on everything is device dependent */
 	void *par;
+#ifdef CONFIG_BOOTSPLASH
+	struct splash_data *splash_data;
+	char fb_cursordata[64];
+#endif
 	/* we need the PCI or similar aperture base/size not
 	   smem_start/size as smem_start may just be an object
 	   allocated inside the aperture so may not actually overlap */