From: Takashi Iwai <tiwai@suse.de>
Subject: Keep multiple splash screens for KMS
Patch-mainline: Never
References: bnc#570082

Keep multiple splash screens for reloading splash again when KMS is
kicked off.

Signed-off-by: Takashi Iwai <tiwai@suse.de>

---
 drivers/video/bootsplash/bootsplash.c |  148 ++++++++++++++++++++++------------
 drivers/video/bootsplash/decode-jpg.c |    7 -
 drivers/video/bootsplash/decode-jpg.h |    2 
 drivers/video/console/fbcon.c         |   11 ++
 drivers/video/console/fbcon.h         |    1 
 5 files changed, 115 insertions(+), 54 deletions(-)

--- a/drivers/video/bootsplash/bootsplash.c
+++ b/drivers/video/bootsplash/bootsplash.c
@@ -331,12 +331,14 @@ static int splash_check_jpeg(unsigned ch
 
 static void splash_free(struct vc_data *vc, struct fb_info *info)
 {
+	struct splash_data *sd;
+	struct splash_data *next;
 	SPLASH_DEBUG();
-	if (!vc->vc_splash_data)
-		return;
-	if (vc->vc_splash_data->splash_silentjpeg)
-		vfree(vc->vc_splash_data->splash_sboxes);
-	vfree(vc->vc_splash_data);
+	for (sd = vc->vc_splash_data; sd; sd = next) {
+		next = sd->next;
+		vfree(sd->splash_sboxes);
+		vfree(sd);
+	}
 	vc->vc_splash_data = 0;
 	info->splash_data = 0;
 }
@@ -418,6 +420,32 @@ static inline int splash_geti(unsigned c
            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;
+	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;
+	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;
+			return;
+		}
+	}
+}
+
 static int splash_getraw(unsigned char *start, unsigned char *end, int *update)
 {
     unsigned char *ndata;
@@ -434,6 +462,8 @@ static int splash_getraw(unsigned char *
     struct vc_data *vc;
     struct fb_info *info;
     struct splash_data *sd;
+    struct splash_data *splash_found = NULL;
+    int unit_found = -1;
     int oldpercent, oldsilent;
 
     if (update)
@@ -442,6 +472,8 @@ static int splash_getraw(unsigned char *
     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 = -1;
+    oldsilent = -1;
     for (ndata = start; ndata < end; ndata++) {
 	if (ndata[0] != 'B' || ndata[1] != 'O' || ndata[2] != 'O' || ndata[3] != 'T')
 	    continue;
@@ -522,12 +554,6 @@ static int splash_getraw(unsigned char *
 	    printk(KERN_ERR "bootsplash: ...found, but truncated!\n");
 	    return -1;
 	}
-	if (!jpeg_check_size(ndata + len + boxcnt * 12 + palcnt, width, height)) {
-	    ndata += len + splash_size - 1;
-	    continue;
-	}
-	if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel))
-	    return -1;
 	silentsize = splash_geti(ndata, SPLASH_OFF_SSIZE);
 	if (silentsize)
 	    printk(KERN_INFO "bootsplash: silentjpeg size %d bytes\n", silentsize);
@@ -543,32 +569,21 @@ static int splash_getraw(unsigned char *
 	    silentsize = 0;
 	}
 	sboxcnt = splash_gets(ndata, SPLASH_OFF_SBOXCNT);
-	if (silentsize) {
-	    unsigned char *simage = ndata + len + splash_size + 12 * sboxcnt;
-	    if (!jpeg_check_size(simage, width, height) ||
-		splash_check_jpeg(simage, width, height, info->var.bits_per_pixel)) {
-		    printk(KERN_WARNING "bootsplash: error in silent jpeg.\n");
-		    silentsize = 0;
-		}
-	}
-	oldpercent = -1;
-	oldsilent = -1;
 	if (vc->vc_splash_data) {
 	    oldpercent = vc->vc_splash_data->splash_percent;
 	    oldsilent = vc->vc_splash_data->splash_dosilent;
-	    splash_free(vc, info);
 	}
-	vc->vc_splash_data = sd = vmalloc(sizeof(*sd) + splash_size + (version < 3 ? 2 * 12 : 0));
+	sd = vmalloc(sizeof(*sd) + splash_size + (version < 3 ? 2 * 12 : 0));
 	if (!sd)
 	    break;
-	sd->splash_silentjpeg = 0;
-	sd->splash_sboxes = 0;
-	sd->splash_sboxcount = 0;
+	memset(sd, 0, sizeof(*sd));
+	jpeg_get_size(ndata + len + boxcnt * 12 + palcnt,
+		      &sd->splash_width, &sd->splash_height);
 	if (silentsize) {
 	    sd->splash_silentjpeg = vmalloc(silentsize);
 	    if (sd->splash_silentjpeg) {
 		memcpy(sd->splash_silentjpeg, ndata + len + splash_size, silentsize);
-		sd->splash_sboxes = vc->vc_splash_data->splash_silentjpeg;
+		sd->splash_sboxes = sd->splash_silentjpeg;
 		sd->splash_silentjpeg += 12 * sboxcnt;
 		sd->splash_sboxcount = sboxcnt;
 	    }
@@ -591,22 +606,6 @@ static int splash_getraw(unsigned char *
 	    sd->splash_fg_color = (splash_default >> 4) & 0x0f;
 	    sd->splash_state    = splash_default & 1;
 	}
-	if (sd->splash_text_xo + sd->splash_text_wi > width || sd->splash_text_yo + sd->splash_text_he > height) {
-	    splash_free(vc, info);
-	    printk(KERN_ERR "bootsplash: found, but has oversized text area!\n");
-	    return -1;
-	}
-	if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) {
-	    splash_free(vc, info);
-	    printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n");
-	    return -1;
-	}
-	printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, 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");
-	}
-
 	/* fake penguin box for older formats */
 	if (version == 1)
 	    boxcnt = splash_mkpenguin(sd, sd->splash_text_xo + 10, sd->splash_text_yo + 10, sd->splash_text_wi - 20, sd->splash_text_he - 20, 0xf0, 0xf0, 0xf0);
@@ -620,8 +619,38 @@ static int splash_getraw(unsigned char *
 	sd->splash_jpeg = sd->splash_palette + palcnt;
 	sd->splash_palcnt = palcnt / 3;
 	sd->splash_dosilent = sd->splash_silentjpeg != 0 ? (oldsilent == -1 ? 1 : oldsilent) : 0;
-	return unit;
+
+	sd->next = vc->vc_splash_data;
+	vc->vc_splash_data = sd;
+
+	if (sd->splash_width != width || sd->splash_height != height) {
+	    ndata += len + splash_size - 1;
+	    continue;
+	}
+	if (splash_check_jpeg(ndata + len + boxcnt * 12 + palcnt, width, height, info->var.bits_per_pixel)) {
+	    ndata += len + splash_size - 1;
+	    continue;
+	}
+	if (!vc_cons[unit].d || info->fbops->fb_imageblit != cfb_imageblit) {
+	    splash_free(vc, info);
+	    printk(KERN_ERR "bootsplash: found, but framebuffer can't handle it!\n");
+	    return -1;
+	}
+	printk(KERN_INFO "bootsplash: ...found (%dx%d, %d bytes, v%d).\n", width, 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;
     }
+
     printk(KERN_ERR "bootsplash: ...no good signature found.\n");
     return -1;
 }
@@ -696,6 +725,20 @@ static void splash_off(struct fb_info *i
 	info->splash_pic_size = 0;
 }
 
+/* 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;
+
+	for (sd = vc->vc_splash_data; sd; sd = sd->next) {
+		if (sd->splash_width == width && sd->splash_height == height) {
+			splash_pivot_current(vc, sd);
+			return 0;
+		}
+	}
+	return -1;
+}
+
 int splash_prepare(struct vc_data *vc, struct fb_info *info)
 {
 	int err;
@@ -719,6 +762,12 @@ int splash_prepare(struct vc_data *vc, s
 		splash_off(info);
 		return -2;
 	}
+	if (splash_look_for_jpeg(vc, width, height) < 0) {
+		printk(KERN_INFO "bootsplash: no matching splash %dx%d\n",
+		       width, height);
+		splash_off(info);
+		return -2;
+	}
 
 	sbytes = ((width + 15) & ~15) * octpp;
 	size = sbytes * ((height + 15) & ~15);
@@ -1007,11 +1056,14 @@ static int splash_write_proc(struct file
 	if (!strncmp(buffer,"freesilent\n",11)) {
 	        SPLASH_DEBUG( " freesilent");
 		if (vc->vc_splash_data && vc->vc_splash_data->splash_silentjpeg) {
+			struct splash_data *sd;
 			printk(KERN_INFO "bootsplash: freeing silent jpeg\n");
-			vc->vc_splash_data->splash_silentjpeg = 0;
-			vfree(vc->vc_splash_data->splash_sboxes);
-			vc->vc_splash_data->splash_sboxes = 0;
-			vc->vc_splash_data->splash_sboxcount = 0;
+			for (sd = vc->vc_splash_data; sd; sd = sd->next) {
+				sd->splash_silentjpeg = 0;
+				vfree(sd->splash_sboxes);
+				sd->splash_sboxes = 0;
+				sd->splash_sboxcount = 0;
+			}
 			if (vc->vc_splash_data->splash_dosilent) {
 				splash_status(vc);
 			}
--- a/drivers/video/bootsplash/decode-jpg.c
+++ b/drivers/video/bootsplash/decode-jpg.c
@@ -240,7 +240,7 @@ static int dec_checkmarker(void)
 	return 0;
 }
 
-int jpeg_check_size(unsigned char *buf, int width, int height)
+void jpeg_get_size(unsigned char *buf, int *width, int *height)
 {
 	datap = buf;
 	getbyte();
@@ -248,9 +248,8 @@ int jpeg_check_size(unsigned char *buf,
 	readtables(M_SOF0);
 	getword();
 	getbyte();
-        if (height != getword() || width != getword())
-		return 0;
-        return 1;
+        *height = getword();
+	*width = getword();
 }
 
 int jpeg_decode(buf, pic, width, height, depth, decdata)
--- a/drivers/video/bootsplash/decode-jpg.h
+++ b/drivers/video/bootsplash/decode-jpg.h
@@ -30,6 +30,6 @@ struct jpeg_decdata {
 };
 
 extern int jpeg_decode(unsigned char *, unsigned char *, int, int, int, struct jpeg_decdata *);
-extern int jpeg_check_size(unsigned char *, int, int);
+extern void jpeg_get_size(unsigned char *, int *, int *);
 
 #endif
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -2187,7 +2187,16 @@ static int fbcon_switch(struct vc_data *
 	ops = info->fbcon_par;
 
 #ifdef CONFIG_BOOTSPLASH
-	splash_prepare(vc, info);
+	{
+		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_text_wi / vc->vc_font.width,
+				  vc->vc_splash_data->splash_text_he / vc->vc_font.height);
+			con_remap_def_color(vc, vc->vc_splash_data->splash_color << 4 | vc->vc_splash_data->splash_fg_color);
+		}
+	}
 #endif
 
 	if (softback_top) {
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -50,6 +50,7 @@ struct splash_data {
     unsigned char *splash_silentjpeg;
     unsigned char *splash_sboxes;
     int splash_sboxcount;
+    struct splash_data *next;
 };
 #endif
 
