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 */