Skip to content
Snippets Groups Projects
0005-bootsplash.patch 9.14 KiB
Newer Older
Philip Müller's avatar
Philip Müller committed
diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
index 815b007f81ca..c8642142cfea 100644
--- a/drivers/video/fbdev/core/bootsplash.c
+++ b/drivers/video/fbdev/core/bootsplash.c
@@ -53,6 +53,14 @@ static void splash_callback_redraw_vc(struct work_struct *ignored)
 	console_unlock();
 }
 
+static void splash_callback_animation(struct work_struct *ignored)
+{
+	if (bootsplash_would_render_now()) {
+		/* This will also re-schedule this delayed worker */
+		splash_callback_redraw_vc(ignored);
+	}
+}
+
 
 static bool is_fb_compatible(const struct fb_info *info)
 {
@@ -103,17 +111,44 @@ static bool is_fb_compatible(const struct fb_info *info)
  */
 void bootsplash_render_full(struct fb_info *info)
 {
+	bool is_update = false;
+
 	mutex_lock(&splash_state.data_lock);
 
-	if (!is_fb_compatible(info))
-		goto out;
+	/*
+	 * If we've painted on this FB recently, we don't have to do
+	 * the sanity checks and background drawing again.
+	 */
+	if (splash_state.splash_fb == info)
+		is_update = true;
+
+
+	if (!is_update) {
+		/* Check whether we actually support this FB. */
+		splash_state.splash_fb = NULL;
+
+		if (!is_fb_compatible(info))
+			goto out;
+
+		/* Draw the background only once */
+		bootsplash_do_render_background(info, splash_state.file);
 
-	bootsplash_do_render_background(info, splash_state.file);
+		/* Mark this FB as last seen */
+		splash_state.splash_fb = info;
+	}
 
-	bootsplash_do_render_pictures(info, splash_state.file);
+	bootsplash_do_render_pictures(info, splash_state.file, is_update);
 
 	bootsplash_do_render_flush(info);
 
+	bootsplash_do_step_animations(splash_state.file);
+
+	/* Schedule update for animated splash screens */
+	if (splash_state.file->frame_ms > 0)
+		schedule_delayed_work(&splash_state.dwork_animation,
+				      msecs_to_jiffies(
+				      splash_state.file->frame_ms));
+
 out:
 	mutex_unlock(&splash_state.data_lock);
 }
@@ -169,8 +204,14 @@ void bootsplash_enable(void)
 
 	was_enabled = test_and_set_bit(0, &splash_state.enabled);
 
-	if (!was_enabled)
+	if (!was_enabled) {
+		/* Force a full redraw when the splash is re-activated */
+		mutex_lock(&splash_state.data_lock);
+		splash_state.splash_fb = NULL;
+		mutex_unlock(&splash_state.data_lock);
+
 		schedule_work(&splash_state.work_redraw_vc);
+	}
 }
 
 
@@ -227,6 +268,14 @@ ATTRIBUTE_GROUPS(splash_dev);
  */
 static int splash_resume(struct device *device)
 {
+	/*
+	 * Force full redraw on resume since we've probably lost the
+	 * framebuffer's contents meanwhile
+	 */
+	mutex_lock(&splash_state.data_lock);
+	splash_state.splash_fb = NULL;
+	mutex_unlock(&splash_state.data_lock);
+
 	if (bootsplash_would_render_now())
 		schedule_work(&splash_state.work_redraw_vc);
 
@@ -235,6 +284,7 @@ static int splash_resume(struct device *device)
 
 static int splash_suspend(struct device *device)
 {
+	cancel_delayed_work_sync(&splash_state.dwork_animation);
 	cancel_work_sync(&splash_state.work_redraw_vc);
 
 	return 0;
@@ -296,6 +346,8 @@ void bootsplash_init(void)
 	set_bit(0, &splash_state.enabled);
 
 	INIT_WORK(&splash_state.work_redraw_vc, splash_callback_redraw_vc);
+	INIT_DELAYED_WORK(&splash_state.dwork_animation,
+			  splash_callback_animation);
 
 
 	if (!splash_state.bootfile || !strlen(splash_state.bootfile))
diff --git a/drivers/video/fbdev/core/bootsplash_internal.h b/drivers/video/fbdev/core/bootsplash_internal.h
index 0acb383aa4e3..b3a74835d90f 100644
--- a/drivers/video/fbdev/core/bootsplash_internal.h
+++ b/drivers/video/fbdev/core/bootsplash_internal.h
@@ -37,6 +37,8 @@ struct splash_pic_priv {
 
 	struct splash_blob_priv *blobs;
 	u16 blobs_loaded;
+
+	u16 anim_nextframe;
 };
 
 
@@ -45,6 +47,12 @@ struct splash_file_priv {
 	const struct splash_file_header *header;
 
 	struct splash_pic_priv *pics;
+
+	/*
+	 * A local copy of the frame delay in the header.
+	 * We modify it to keep the code simple.
+	 */
+	u16 frame_ms;
 };
 
 
@@ -71,6 +79,7 @@ struct splash_priv {
 	struct platform_device *splash_device;
 
 	struct work_struct work_redraw_vc;
+	struct delayed_work dwork_animation;
 
 	/* Splash data structures including lock for everything below */
 	struct mutex data_lock;
@@ -88,8 +97,10 @@ struct splash_priv {
 void bootsplash_do_render_background(struct fb_info *info,
 				     const struct splash_file_priv *fp);
 void bootsplash_do_render_pictures(struct fb_info *info,
-				   const struct splash_file_priv *fp);
+				   const struct splash_file_priv *fp,
+				   bool is_update);
 void bootsplash_do_render_flush(struct fb_info *info);
+void bootsplash_do_step_animations(struct splash_file_priv *fp);
 
 
 void bootsplash_free_file(struct splash_file_priv *fp);
diff --git a/drivers/video/fbdev/core/bootsplash_load.c b/drivers/video/fbdev/core/bootsplash_load.c
index fd807571ab7d..1f661b2d4cc9 100644
--- a/drivers/video/fbdev/core/bootsplash_load.c
+++ b/drivers/video/fbdev/core/bootsplash_load.c
@@ -71,6 +71,7 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device,
 {
 	const struct firmware *fw;
 	struct splash_file_priv *fp;
+	bool have_anim = false;
 	unsigned int i;
 	const u8 *walker;
 
@@ -135,6 +136,13 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device,
 			goto err;
 		}
 
+		if (ph->anim_type > SPLASH_ANIM_LOOP_FORWARD) {
+			pr_warn("Picture %u: Unsupported animation type %u.\n",
+				i, ph->anim_type);
+
+			ph->anim_type = SPLASH_ANIM_NONE;
+		}
+
 		pp->pic_header = ph;
 		pp->blobs = vzalloc(ph->num_blobs
 					* sizeof(struct splash_blob_priv));
@@ -202,6 +210,7 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device,
 	/* Walk over pictures and ensure all blob slots are filled */
 	for (i = 0; i < fp->header->num_pics; i++) {
 		struct splash_pic_priv *pp = &fp->pics[i];
+		const struct splash_pic_header *ph = pp->pic_header;
 
 		if (pp->blobs_loaded != pp->pic_header->num_blobs) {
 			pr_err("Picture %u doesn't have all blob slots filled.\n",
@@ -209,8 +218,20 @@ struct splash_file_priv *bootsplash_load_firmware(struct device *device,
 
 			goto err;
 		}
+
+		if (ph->anim_type
+		    && ph->num_blobs > 1
+		    && ph->anim_loop < pp->blobs_loaded)
+			have_anim = true;
 	}
 
+	if (!have_anim)
+		/* Disable animation timer if there is nothing to animate */
+		fp->frame_ms = 0;
+	else
+		/* Enforce minimum delay between frames */
+		fp->frame_ms = max((u16)20, fp->header->frame_ms);
+
 	pr_info("Loaded (%ld bytes, %u pics, %u blobs).\n",
 		fw->size,
 		fp->header->num_pics,
diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c
index 07e3a4eab811..76033606ca8a 100644
--- a/drivers/video/fbdev/core/bootsplash_render.c
+++ b/drivers/video/fbdev/core/bootsplash_render.c
@@ -148,7 +148,8 @@ void bootsplash_do_render_background(struct fb_info *info,
 
 
 void bootsplash_do_render_pictures(struct fb_info *info,
-				   const struct splash_file_priv *fp)
+				   const struct splash_file_priv *fp,
+				   bool is_update)
 {
 	unsigned int i;
 
@@ -161,7 +162,11 @@ void bootsplash_do_render_pictures(struct fb_info *info,
 		if (pp->blobs_loaded < 1)
 			continue;
 
-		bp = &pp->blobs[0];
+		/* Skip static pictures when refreshing animations */
+		if (ph->anim_type == SPLASH_ANIM_NONE && is_update)
+			continue;
+
+		bp = &pp->blobs[pp->anim_nextframe];
 
 		if (!bp || bp->blob_header->type != 0)
 			continue;
@@ -351,3 +356,24 @@ void bootsplash_do_render_flush(struct fb_info *info)
 		info->fbops->fb_copyarea(info, &area);
 	}
 }
+
+
+void bootsplash_do_step_animations(struct splash_file_priv *fp)
+{
+	unsigned int i;
+
+	/* Step every animation once */
+	for (i = 0; i < fp->header->num_pics; i++) {
+		struct splash_pic_priv *pp = &fp->pics[i];
+
+		if (pp->blobs_loaded < 2
+		    || pp->pic_header->anim_loop > pp->blobs_loaded)
+			continue;
+
+		if (pp->pic_header->anim_type == SPLASH_ANIM_LOOP_FORWARD) {
+			pp->anim_nextframe++;
+			if (pp->anim_nextframe >= pp->pic_header->num_blobs)
+				pp->anim_nextframe = pp->pic_header->anim_loop;
+		}
+	}
+}
diff --git a/include/uapi/linux/bootsplash_file.h b/include/uapi/linux/bootsplash_file.h
index 71cedcc68933..b3af0a3c6487 100644
--- a/include/uapi/linux/bootsplash_file.h
+++ b/include/uapi/linux/bootsplash_file.h
@@ -77,7 +77,17 @@ struct splash_file_header {
 	uint16_t num_blobs;
 	uint8_t num_pics;
 
-	uint8_t padding[103];
+	uint8_t unused_1;
+
+	/*
+	 * Milliseconds to wait before painting the next frame in
+	 * an animation.
+	 * This is actually a minimum, as the system is allowed to
+	 * stall for longer between frames.
+	 */
+	uint16_t frame_ms;
+
+	uint8_t padding[100];
 } __attribute__((__packed__));
 
 
@@ -116,7 +126,23 @@ struct splash_pic_header {
 	 */
 	uint16_t position_offset;
 
-	uint8_t padding[24];
+	/*
+	 * Animation type.
+	 *  0 - off
+	 *  1 - forward loop
+	 */
+	uint8_t anim_type;
+
+	/*
+	 * Animation loop point.
+	 * Actual meaning depends on animation type:
+	 * Type 0 - Unused
+	 *      1 - Frame at which to restart the forward loop
+	 *          (allowing for "intro" frames)
+	 */
+	uint8_t anim_loop;
+
+	uint8_t padding[22];
 } __attribute__((__packed__));
 
 
@@ -158,4 +184,9 @@ enum splash_position {
 	SPLASH_POS_FLAG_CORNER = 0x10,
 };
 
+enum splash_anim_type {
+	SPLASH_ANIM_NONE = 0,
+	SPLASH_ANIM_LOOP_FORWARD = 1,
+};
+
 #endif