/* 
 *    linux/drivers/video/fbcon-splash.c - splash screen handling functions.
 *	
 *	(w) 2001-2003 by Volker Poplawski, <volker@suse.de>
 * 		    Stefan Reinauer, <stepan@suse.de>
 * 		    Steffen Winterfeldt, <snwint@suse.de>
 * 		    
 * 	       Ideas & SuSE screen work by Ken Wimer, <wimer@suse.de>
 */

#include <linux/version.h>
#include <linux/config.h>
#include <linux/module.h>
#include <linux/fb.h>
#include <linux/vt_kern.h>
#include <linux/vmalloc.h>

#include <asm/irq.h>
#include <asm/system.h>

#include <video/fbcon.h>
#include <video/font.h>
#include <video/fbcon-cfb16.h>  /* for fbcon_cfb16 */

#include "fbcon-splash.h"
#include "fbcon-jpegdec.h"

#define SPLASH_VERSION "3.0.9-2003/09/08"

#ifdef CONFIG_BLK_DEV_INITRD
static unsigned char signature[] = "BOOTSPL1SPL2SPL3";
#endif

/* 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"
};

/* from drivers/char/console.c */
extern void con_remap_def_color(int currcons, int new_color);

/* from drivers/video/fbcon-splash16.c */
extern void splashfill(u8 *dest, u8 *src, int width, int height, 
			int dest_linesize, int src_linesize);

/* internal control states and data pointers */
struct splash_data splash_data;

unsigned char *linux_splash;	/* decoded picture */
int linux_splash_size = 0;

static struct jpeg_decdata *decdata = 0; /* private decoder data */

int splash_bytes;		/* bytes per line in linux_splash */
int splash_shown = 0;		/* is the splash onscreen? */
int splash_usesilent = 0;	/* shall we display the silentjpeg */
int splash_default = 0xf01;

static int splash_status(struct display *p);
static int splash_recolor(struct display *p);
static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth);

extern struct display_switch fbcon_splash16;

int __init splash_init(char *options)
{
	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;
		return 0;
	}
		
	splash_default = simple_strtoul(options, NULL, 0);

	return 0;
}

__setup("splash=", splash_init);


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 overpaint)
{
    int x, y, i, p, doblend, r, g, b, a, add;
    unsigned short data1[4];
    unsigned char cols1[16];
    unsigned short data2[4];
    unsigned char cols2[16];
    unsigned char *bufend;
    unsigned short *picp;

    if (num == 0)
	return;
    bufend = buf + num * 12;
    while(buf < bufend) {
	doblend = 0;
	buf += boxextract(buf, data1, cols1, &doblend);
	if (data1[0] > 32767)
	    buf += boxextract(buf, data2, cols2, &doblend);
	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) {
	    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;
	for (y = data1[1]; y <= data1[3]; y++) {
	    if (doblend) {
		if ((p = data1[3] - data1[1]) != 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 = (data1[0] & 1);
	    add ^= (add ^ y) & 1 ? 1 : 3;		/* 2x2 ordered dithering */
	    picp = (unsigned short *)(pic + data1[0] * 2 + y * bytes);
	    for (x = data1[0]; x <= data1[2]; x++) {
		if (doblend) {
		    if ((p = data1[2] - data1[0]) != 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];
		if (a != 255) {
		    i = *picp;
		    r = ((i >> 8 & 0xf8) * (255 - a) + r * a) / 255;
		    g = ((i >> 3 & 0xfc) * (255 - a) + g * a) / 255;
		    b = ((i << 3 & 0xf8) * (255 - a) + b * a) / 255;
		}
  #define CLAMP(x) ((x) >= 256 ? 255 : (x))
		i = ((CLAMP(r + add*2+1) & 0xf8) <<  8) |
		    ((CLAMP(g + add    ) & 0xfc) <<  3) |
		    ((CLAMP(b + add*2+1)       ) >>  3);
		*picp++ = i;
		add ^= 3;
	    }
	}
    }
}

static int splash_check_jpeg(unsigned char *jpeg, int width, int height, int depth)
{
    int size, err;
    unsigned char *mem;

    size = ((width + 15) & ~15) * ((height + 15) & ~15) * (depth >> 3);
    mem = vmalloc(size);
    if (!mem) {
	printk(KERN_INFO "bootsplash: no memory for decoded picture.\n");
	return -1;
    }
    if (!decdata)
	decdata = vmalloc(sizeof(*decdata));
    if ((err = jpeg_decode(jpeg, mem, ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata)))
	  printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d)\n",jpg_errors[err-1], err);
    vfree(mem);
    return err ? -1 : 0;
}

static void splash_free(struct display * p)
{
    if (!p->splash_data)
	return;
    if (p->splash_data->oldscreen_base)
	    p->screen_base = p->splash_data->oldscreen_base;
    if (p->splash_data->olddispsw)
	    p->dispsw = p->splash_data->olddispsw;
    if (p->splash_data->splash_silentjpeg)
	    vfree(p->splash_data->splash_sboxes);
    vfree(p->splash_data);
    p->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;
}

int splash_getraw(unsigned char *start, unsigned char *end)
{
	unsigned char *ndata;
	int found = 0;
	int splash_size = 0;
	void *splash_start = 0;
        int unit = 0;
        int width = 0, height = 0;
	int silentsize;
	int boxcount = 0;
	int sboxcount = 0;
	int palcnt = 0;
	struct display *p;

	printk(KERN_INFO "bootsplash %s: looking for picture...", SPLASH_VERSION);

	p = &fb_display[0];
        silentsize = 0;

	for (ndata = start; ndata < end; ndata++) {
		if (*((unsigned int *)ndata) != *((unsigned int *)signature))
			continue;
		if (*((unsigned int *)(ndata+4))==*((unsigned int *)(signature+4))) {
			printk(".");
			unit = 0;
			p = &fb_display[0];
			width = p->var.xres;
			height = p->var.yres;

			splash_size = ndata[16] + (ndata[17] << 8) + (ndata[18] << 16) + (ndata[19] << 24);
			if (ndata + 20 + splash_size > end) {
				printk(" found, but truncated!\n");
				return -1;
			}
			if (!jpeg_check_size(ndata + 20, width, height)) { 
				ndata += 20 - 1 + splash_size;
				continue;
			}
			if (splash_check_jpeg(ndata + 20, width, height, p->var.bits_per_pixel))
				return -1;
			if (p->splash_data)
				splash_free(p);
			p->splash_data = vmalloc(sizeof(*p->splash_data) + splash_size + 24);
			if (!p->splash_data)
				break;

			p->splash_data->oldscreen_base = 0;
			p->splash_data->olddispsw = 0;
			p->splash_data->splash_silentjpeg = 0;

			p->splash_data->splash_text_xo = ndata[ 8] + (ndata[ 9] << 8);
			p->splash_data->splash_text_yo = ndata[10] + (ndata[11] << 8);
			p->splash_data->splash_text_wi = ndata[12] + (ndata[13] << 8);
			p->splash_data->splash_text_he = ndata[14] + (ndata[15] << 8);
			/* use 8x16 font... */
			p->splash_data->splash_text_xo *= 8;
			p->splash_data->splash_text_wi *= 8;
			p->splash_data->splash_text_yo *= 16;
			p->splash_data->splash_text_he *= 16;
			p->splash_data->splash_color = (splash_default >> 8) & 0x0f;
			p->splash_data->splash_fg_color = (splash_default >> 4) & 0x0f;
			p->splash_data->splash_state = splash_default & 1;
			p->splash_data->splash_percent = 0;
			p->splash_data->splash_overpaintok = 0;
			boxcount = splash_mkpenguin(p->splash_data, p->splash_data->splash_text_xo + 10, p->splash_data->splash_text_yo + 10, p->splash_data->splash_text_wi - 20, p->splash_data->splash_text_he - 20, 0xf0, 0xf0, 0xf0);
			splash_start = ndata + 20;
			found = 1;
			break;
		}
		if (*((unsigned int *)(ndata + 4)) == *((unsigned int *)(signature+8))) {
			printk(".");
			unit = ndata[8];
			if (unit >= MAX_NR_CONSOLES)
			    continue;
			if (unit)
			    vc_allocate(unit);
			p = &fb_display[unit];
			width = fb_display[unit].var.xres;
			height = fb_display[unit].var.yres;
			splash_size = ndata[12] + (ndata[13] << 8) + (ndata[14] << 16) + (ndata[15] << 24);
			if (splash_size == -1) {
			    printk(" found, updating values.\n");
			    if (p->splash_data) {
				if (ndata[9] != 255)
				    p->splash_data->splash_state = ndata[9];
				if (ndata[10] != 255)
				    p->splash_data->splash_fg_color = ndata[10];
				if (ndata[11] != 255)
				    p->splash_data->splash_color = ndata[11];
			    }
			    return unit;
			}
			if (splash_size == 0) {
				printk(" found, freeing memory.\n");
				if (p->splash_data)
					splash_free(p);
				return unit;
			}
			if (ndata + 35 + splash_size > end) {
				printk(" found, but truncated!\n");
				return -1;
			}
			if (!jpeg_check_size(ndata + 35, width, height)) {
				ndata += 35 - 1 + splash_size;
				continue;
			}
			if (splash_check_jpeg(ndata + 35, width, height, p->var.bits_per_pixel))
				return -1;
			if (p->splash_data)
				splash_free(p);
			p->splash_data = vmalloc(sizeof(*p->splash_data) + splash_size + 24);
			if (!p->splash_data)
				break;

			p->splash_data->oldscreen_base = 0;
			p->splash_data->olddispsw = 0;
			p->splash_data->splash_silentjpeg = 0;

			p->splash_data->splash_state = ndata[9];
			p->splash_data->splash_fg_color = ndata[10];
			p->splash_data->splash_color = ndata[11];
			p->splash_data->splash_text_xo = ndata[16] + (ndata[17] << 8);
			p->splash_data->splash_text_yo = ndata[18] + (ndata[19] << 8);
			p->splash_data->splash_text_wi = ndata[20] + (ndata[21] << 8);
			p->splash_data->splash_text_he = ndata[22] + (ndata[23] << 8);
			p->splash_data->splash_percent = 0;
			p->splash_data->splash_overpaintok = 0;
			boxcount = splash_mkpenguin(p->splash_data, ndata[24] + (ndata[25] << 8), ndata[26] + (ndata[27] << 8), ndata[28] + (ndata[29] << 8), ndata[30] + (ndata[31] << 8), ndata[32], ndata[33], ndata[34]);
			splash_start = ndata + 35;
			found = 2;
			break;
		}
		if (*((unsigned int *)(ndata + 4)) == *((unsigned int *)(signature+12))) {
			printk(".");
			unit = ndata[8];
			if (unit >= MAX_NR_CONSOLES)
			    continue;
			if (unit)
			    vc_allocate(unit);
			p = &fb_display[unit];
			width = p->var.xres;
			height = p->var.yres;
			splash_size = ndata[12] + (ndata[13] << 8) + (ndata[14] << 16) + (ndata[15] << 24);
			if (splash_size == -1) {
			    printk(" found, updating values.\n");
			    if (p->splash_data) {
				if (ndata[9] != 255)
				    p->splash_data->splash_state = ndata[9];
				if (ndata[10] != 255)
				    p->splash_data->splash_fg_color = ndata[10];
				if (ndata[11] != 255)
				    p->splash_data->splash_color = ndata[11];
			    }
			    return unit;
			}
			if (splash_size == 0) {
				printk(" found, freeing memory.\n");
				if (p->splash_data)
					splash_free(p);
				return unit;
			}
			boxcount = ndata[24] + (ndata[25] << 8);
			palcnt = ndata[37] * 3;
			if (ndata + 38 + splash_size > end) {
				printk(" found, but truncated!\n");
				return -1;
			}
			if (!jpeg_check_size(ndata + 38 + boxcount * 12 + palcnt, width, height)) {
				ndata += 38 - 1 + splash_size;
				continue;
			}
			if (splash_check_jpeg(ndata + 38 + boxcount * 12 + palcnt, width, height, p->var.bits_per_pixel))
				return -1;
			silentsize = ndata[28] + (ndata[29] << 8) + (ndata[30] << 16) + (ndata[31] << 24);
			if (silentsize)
			    printk(" silenjpeg size %d bytes,", silentsize);
			if (silentsize >= splash_size) {
				printk(" bigger than splashsize!\n");
				return -1;
			}
			splash_size -= silentsize;
			if (!splash_usesilent || !p->fb_info->fbops->fb_get_fix ) {
				silentsize = 0;
			} else {
				struct fb_fix_screeninfo fix;
				p->fb_info->fbops->fb_get_fix(&fix, unit, 0);
				if (height * 2 * p->next_line > fix.smem_len) {
					printk(" does not fit into framebuffer.\n");
					silentsize = 0;
				}
			}

			sboxcount = ndata[32] + (ndata[33] << 8);
			if (silentsize) {
				char *simage=ndata + 38 + splash_size + 12 * sboxcount;
				if (!jpeg_check_size(simage, width, height) ||
				    splash_check_jpeg(simage, width, height, p->var.bits_per_pixel)) {
					printk(" error in silent jpeg.\n");
					silentsize = 0;
				}
			}
			if (p->splash_data)
				splash_free(p);
			p->splash_data = vmalloc(sizeof(*p->splash_data) + splash_size);
			if (!p->splash_data)
				break;
			p->splash_data->oldscreen_base = 0;
			p->splash_data->olddispsw = 0;
			p->splash_data->splash_silentjpeg = 0;
			if (silentsize) {
				p->splash_data->splash_silentjpeg = vmalloc(silentsize);
				if (p->splash_data->splash_silentjpeg) {
					memcpy(p->splash_data->splash_silentjpeg, ndata + 38 + splash_size, silentsize);
					p->splash_data->splash_sboxes = p->splash_data->splash_silentjpeg;
					p->splash_data->splash_silentjpeg += 12 * sboxcount;
					p->splash_data->splash_sboxcount = sboxcount;
				}
			}
			p->splash_data->splash_state = ndata[9];
			p->splash_data->splash_fg_color = ndata[10];
			p->splash_data->splash_color = ndata[11];
			p->splash_data->splash_overpaintok = ndata[36];
			p->splash_data->splash_text_xo = ndata[16] + (ndata[17] << 8);
			p->splash_data->splash_text_yo = ndata[18] + (ndata[19] << 8);
			p->splash_data->splash_text_wi = ndata[20] + (ndata[21] << 8);
			p->splash_data->splash_text_he = ndata[22] + (ndata[23] << 8);
			p->splash_data->splash_percent = ndata[34] + (ndata[35] << 8);
			p->splash_data->splash_percent += p->splash_data->splash_percent > 32767;
			splash_start = ndata + 38;
			found = 3;
			break;
		}
	}
	
	if (!found) {
		printk(" no good signature found.\n");
		return -1;
	}
	if (p->splash_data->splash_text_xo + p->splash_data->splash_text_wi > width || p->splash_data->splash_text_yo + p->splash_data->splash_text_he > height) {
		splash_free(p);
		p->splash_data = 0;
		printk(" found, but has oversized text area!\n");
		return -1;
	}
	if (p->dispsw->setup != fbcon_cfb16.setup) {
		splash_free(p);
		p->splash_data = 0;
		printk(" found, but framebuffer can't handle it!\n");
		return -1;
	}
	printk(" found (%dx%d, %d bytes, v%d).\n", width, height, splash_size, found);

	if (found==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");
	}

	/* copy data so that initrd memory can be freed. */
	memcpy((char *)p->splash_data + sizeof(*p->splash_data) + (found < 3 ? boxcount * 12 : 0), splash_start, splash_size);
	p->splash_data->splash_boxcount = boxcount;
	p->splash_data->splash_boxes = (unsigned char *)p->splash_data + sizeof(*p->splash_data);
	p->splash_data->splash_jpeg = (unsigned char *)p->splash_data + sizeof(*p->splash_data) + boxcount * 12 + palcnt;
	p->splash_data->splash_palette = (unsigned char *)p->splash_data + sizeof(*p->splash_data) + boxcount * 12;
	p->splash_data->splash_palcnt = palcnt / 3;
	p->splash_data->splash_dosilent = p->splash_data->splash_silentjpeg != 0;
	return unit;
}

int splash_verbose(void) 
{
    struct display *p;

    if (!splash_shown || !splash_usesilent)
        return 0;

    p = &fb_display[0];

    if (!p || !p->splash_data || !p->splash_data->splash_state || !p->conp)
	return 0;
    if (fg_console != p->conp->vc_num)
	return 0;
    if (!p->splash_data->splash_silentjpeg || !p->splash_data->splash_dosilent)
	return 0;
    if (!p->splash_data->oldscreen_base)
	return 0;

    p->splash_data->splash_dosilent = 0;

    splashfill(p->splash_data->oldscreen_base, p->screen_base, p->var.xres, p->var.yres, p->next_line, p->next_line);
    p->screen_base = p->splash_data->oldscreen_base;

    return 1;
}

static void splash_off(struct display *p)
{
    if (p->splash_data && p->splash_data->oldscreen_base)
	p->screen_base = p->splash_data->oldscreen_base;
    if (p->splash_data && p->splash_data->olddispsw)
	p->dispsw = p->splash_data->olddispsw;
    splash_shown = 0;
}

int splash_prepare(struct display *p)
{
	int err;
        int width, height, depth, size, sbytes;

	if (!p->splash_data || !p->splash_data->splash_state) {
		if (linux_splash)
			vfree(linux_splash);
		if (decdata)
			vfree(decdata);
		linux_splash = 0;
		decdata = 0;
		linux_splash_size = 0;
		splash_off(p);
		return -1;
	}

        width = p->var.xres;
        height = p->var.yres;
        depth = p->var.bits_per_pixel;
	if (depth != 16) {	/* Other targets might need fixing */
		splash_off(p);
		return -2;
	}

	sbytes = ((width + 15) & ~15) * (depth >> 3);
	size = sbytes * ((height + 15) & ~15);
	if (size != linux_splash_size) {
		if (linux_splash)
			vfree(linux_splash);
		linux_splash_size = 0;
		linux_splash = 0;
		splash_off(p);
	}
	if (!linux_splash)
		linux_splash = vmalloc(size);

	if (!linux_splash) {
		linux_splash_size = 0;
		printk(KERN_INFO "bootsplash: not enough memory.\n");
		splash_off(p);
		return -3;
	}

	if (!decdata)
	    decdata = vmalloc(sizeof(*decdata));

	if (!p->splash_data->oldscreen_base)
		p->splash_data->oldscreen_base = p->screen_base;

	if (p->splash_data->splash_silentjpeg && p->splash_data->splash_dosilent) {
		printk(KERN_INFO "bootsplash: silent jpeg found.\n");
		/* fill area after framebuffer with other jpeg */
		if ((err = jpeg_decode(p->splash_data->splash_silentjpeg, linux_splash, 
			 ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) {
			printk(KERN_INFO "bootsplash: error while decompressing silent picture: %s (%d)\n", jpg_errors[err-1], err);
			p->screen_base = p->splash_data->oldscreen_base;
			p->splash_data->splash_dosilent = 0;
		} else {
			if (p->splash_data->splash_sboxcount)
				boxit(linux_splash, sbytes, p->splash_data->splash_sboxes, 
					p->splash_data->splash_sboxcount, p->splash_data->splash_percent, 0);

			splashfill(p->splash_data->oldscreen_base, linux_splash, p->var.xres, p->var.yres, p->next_line, sbytes);
			p->screen_base = p->splash_data->oldscreen_base + p->next_line * p->var.yres;
		}
	} else
		p->screen_base = p->splash_data->oldscreen_base;

	if ((err = jpeg_decode(p->splash_data->splash_jpeg, linux_splash, 
		 ((width + 15) & ~15), ((height + 15) & ~15), depth, decdata))) {
		printk(KERN_INFO "bootsplash: error while decompressing picture: %s (%d) .\n", jpg_errors[err-1], err);
		vfree(linux_splash);
		linux_splash = 0;
		linux_splash_size = 0;
		splash_off(p);
		return -4;
	}
	linux_splash_size = size;
	splash_bytes = sbytes;
	if (p->splash_data->splash_boxcount)
		boxit(linux_splash, sbytes, p->splash_data->splash_boxes, p->splash_data->splash_boxcount, p->splash_data->splash_percent, 0);
	splash_data = *p->splash_data;
	splash_shown = p->splash_data->splash_state;
	if (splash_shown) {
	    if (p->dispsw != &fbcon_splash16)
		p->splash_data->olddispsw = p->dispsw;
	    p->dispsw = &fbcon_splash16;
	} else
		splash_off(p);
	return 0;
}


#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>

struct proc_dir_entry *proc_splash;
int splash_read_proc(char *buffer, char **start, off_t offset, int size,
			int *eof, void *data);
int splash_write_proc(struct file *file, const char *buffer,
			unsigned long count, void *data);

int splash_read_proc(char *buffer, char **start, off_t offset, int size,
			int *eof, void *data)
{
	int len = 0;
	
	off_t begin = 0;
	struct display *p = &fb_display[0];

	int color = p->splash_data ? p->splash_data->splash_color << 4 |
			p->splash_data->splash_fg_color : splash_default >> 4;
	int status = p->splash_data ? p->splash_data->splash_state & 1 : 0;
	len += sprintf(buffer + len, "Splash screen v%s (0x%02x, %dx%d%s): %s\n",
		        SPLASH_VERSION, color, p->var.xres, p->var.yres,
			(p->splash_data ?  p->splash_data->splash_dosilent : 0)? ", silent" : "",
					status ? "on" : "off");
	if (offset >= begin + len)
		return 0;

	*start = buffer + (begin - offset);

	return (size < begin + len - offset ? size : begin + len - offset);
}

static int splash_recolor(struct display *p)
{
	if (!p->splash_data)
	    return -1;
	if (!p->splash_data->splash_state)
	    return 0;
	con_remap_def_color(p->conp->vc_num, p->splash_data->splash_color << 4 | p->splash_data->splash_fg_color);
	if (fg_console == p->conp->vc_num) {
	        splash_data = *p->splash_data;
		update_region(fg_console,
			      p->conp->vc_origin +
			      p->conp->vc_size_row *
			      p->conp->vc_top,
			      p->conp->vc_size_row *
			      (p->conp->vc_bottom -
			       p->conp->vc_top) / 2);
	}
	return 0;
}

static int splash_status(struct display *p)
{
	printk(KERN_INFO "bootsplash: status on console %d changed to %s\n", p->conp->vc_num, p->splash_data && p->splash_data->splash_state ? "on" : "off");

	if (fg_console == p->conp->vc_num)
		splash_prepare(p);
	if (p->splash_data && p->splash_data->splash_state) {
		con_remap_def_color(p->conp->vc_num, p->splash_data->splash_color << 4 | p->splash_data->splash_fg_color);
		/* resize_con also calls con_switch which resets yscroll */
		vc_resize_con(p->splash_data->splash_text_he / fontheight(p),
                              p->splash_data->splash_text_wi / fontwidth(p),
                              p->conp->vc_num);
		if (fg_console == p->conp->vc_num) {
			update_region(fg_console,
				      p->conp->vc_origin +
				      p->conp->vc_size_row *
				      p->conp->vc_top,
				      p->conp->vc_size_row *
				      (p->conp->vc_bottom -
				       p->conp->vc_top) / 2);
			    fbcon_splash16.clear_margins(p->conp, p, 0);
		}
	} else {
	  	/* Switch bootsplash off */
		con_remap_def_color(p->conp->vc_num, 0x07);
		vc_resize_con(p->var.yres / fontheight(p),
			      p->var.xres / fontwidth(p),
			      p->conp->vc_num);
	}
	return 0;
}

int splash_write_proc(struct file *file, const char *buffer,
		      unsigned long count, void *data)
{
        int new, unit;
	struct display *p;
	
	if (!buffer || !splash_default)
		return count;

	if (!strncmp(buffer, "show", 4) || !strncmp(buffer, "hide", 4)) {
		int pe, oldpe;

		p = &fb_display[0];
		if (buffer[4] == ' ' && buffer[5] == 'p')
			pe = 0;
		else if (buffer[4] == '\n')
			pe = 65535;
		else
			pe = simple_strtoul(buffer + 5, NULL, 0);
		if (pe < 0)
			pe = 0;
		if (pe > 65535)
			pe = 65535;
		if (*buffer == 'h')
			pe = 65535 - pe;
		pe += pe > 32767;
		oldpe = p->splash_data->splash_percent;
		if (p->splash_data && oldpe != pe) {
			p->splash_data->splash_percent = pe;
			if (fg_console != p->conp->vc_num || !p->splash_data->splash_state)
			    return count;
			if (!p->splash_data->splash_overpaintok || pe == 65536 || pe < oldpe) {
				if (splash_hasinter(p->splash_data->splash_boxes, p->splash_data->splash_boxcount))
					splash_status(p);
				else
					splash_prepare(p);
			} else {
				if (p->splash_data->splash_silentjpeg && p->splash_data->splash_dosilent && p->splash_data->oldscreen_base)
					boxit(p->splash_data->oldscreen_base, p->next_line, p->splash_data->splash_sboxes, p->splash_data->splash_sboxcount, p->splash_data->splash_percent, 1);
				boxit(p->screen_base, p->next_line, p->splash_data->splash_boxes, p->splash_data->splash_boxcount, p->splash_data->splash_percent, 1);
			}
		}
		return count;
	}
	if (!strncmp(buffer,"silent\n",7) || !strncmp(buffer,"verbose\n",8)) {
		p = &fb_display[0];
		if (p->splash_data && p->splash_data->splash_silentjpeg) {
		    if (p->splash_data->splash_dosilent != (buffer[0] == 's')) {
			p->splash_data->splash_dosilent = buffer[0] == 's';
			splash_status(p);
		    }
		}
		return count;
	}
	if (!strncmp(buffer,"freesilent\n",11)) {
		p = &fb_display[0];
		if (p->splash_data && p->splash_data->splash_silentjpeg) {
		    printk(KERN_INFO "bootsplash: freeing silent jpeg\n");
		    p->splash_data->splash_silentjpeg = 0;
		    vfree(p->splash_data->splash_sboxes);
		    p->splash_data->splash_sboxes = 0;
		    p->splash_data->splash_sboxcount = 0;
		    if (p->splash_data->splash_dosilent)
			splash_status(p);
		    p->splash_data->splash_dosilent = 0;
		}
		return count;
	}

	if (!strncmp(buffer, signature, 7)) {
	    unit = splash_getraw((unsigned char *)buffer, (unsigned char *)buffer + count);
	    if (unit >= 0) {
		p = &fb_display[unit];
		splash_status(p);
	    }
	    return count;
	}
	p = &fb_display[0];
	if (!p->splash_data)
		return count;
	if (buffer[0] == 't') {
	        p->splash_data->splash_state ^= 1;
		splash_status(p);
		return count;
	}
	new = simple_strtoul(buffer, NULL, 0);
	if (new > 1) {
		/* expert user */
		p->splash_data->splash_color    = new >> 8 & 0xff;
		p->splash_data->splash_fg_color = new >> 4 & 0x0f;
	}
	if ((new & 1) == p->splash_data->splash_state)
		splash_recolor(p);
	else {
	        p->splash_data->splash_state = new & 1;
		splash_status(p);
	}
	return count;
}

int splash_proc_register(void)
{
	if ((proc_splash = create_proc_entry("splash", 0, 0))) {
		proc_splash->read_proc = splash_read_proc;
		proc_splash->write_proc = splash_write_proc;
		return 0;
	}
	return 1;
}

int splash_proc_unregister(void)
{
	if (proc_splash)
		remove_proc_entry("splash", 0);
	return 0;
}
#endif
