@@ -64,6 +64,17 @@ static void splash_callback_redraw_vc(struct work_struct *ignored)
static DECLARE_WORK(splash_work_redraw_vc, splash_callback_redraw_vc);
+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 DECLARE_DELAYED_WORK(splash_dwork_animation, splash_callback_animation);
+
+
/*
@@ -117,17 +128,45 @@ static bool is_fb_compatible(struct fb_info *info)
*/
void bootsplash_render_full(struct fb_info *info)
{
+ bool is_update = false;
+
mutex_lock(&splash_global.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_global.splash_fb == info)
+ is_update = true;
+
+
+ if (!is_update) {
+ /* Check whether we actually support this FB. */
+ splash_global.splash_fb = NULL;
+
+ if (!is_fb_compatible(info))
+ goto out;
- bootsplash_do_render_background(info);
+ /* Draw the background only once */
+ bootsplash_do_render_background(info);
- bootsplash_do_render_pictures(info);
+ /* Mark this FB as last seen */
+ splash_global.splash_fb = info;
+ }
+
+
+ bootsplash_do_render_pictures(info, is_update);
bootsplash_do_render_flush(info);
+ bootsplash_do_step_animations();
+
+ /* Schedule update for animated splash screens */
+ if (splash_global.header->frame_ms > 0)
+ queue_delayed_work(splash_global.wq,
+ &splash_dwork_animation,
+ msecs_to_jiffies(
+ splash_global.header->frame_ms));
+
out:
mutex_unlock(&splash_global.data_lock);
}
@@ -201,8 +240,14 @@ void bootsplash_enable(void)
spin_unlock_irqrestore(&splash_global.state_lock, flags);
- if (!was_enabled)
+ if (!was_enabled) {
+ /* Force a full redraw when the splash is re-activated */
+ mutex_lock(&splash_global.data_lock);
+ splash_global.splash_fb = NULL;
+ mutex_unlock(&splash_global.data_lock);
+
queue_work(splash_global.wq, &splash_work_redraw_vc);
+ }
}
@@ -281,6 +326,13 @@ 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_global.data_lock);
+ splash_global.splash_fb = NULL;
+ mutex_unlock(&splash_global.data_lock);
+
if (bootsplash_would_render_now())
queue_work(splash_global.wq, &splash_work_redraw_vc);
@@ -74,7 +74,14 @@ struct splash_file_header {
u16 num_blobs;
u8 num_pics;
- u8 padding[103];
+ /* 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.
+ */
+ u16 frame_ms;
+
+ u8 padding[101];
} __attribute__((__packed__));
@@ -108,7 +115,22 @@ struct splash_pic_header {
*/
u8 corner_offset;
- u8 padding[9];
+
+ /* Animation type.
+ * 0 - off
+ * 1 - forward loop
+ */
+ u8 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)
+ */
+ u8 anim_loop;
+
+ u8 padding[7];
} __attribute__((__packed__));
@@ -137,6 +159,11 @@ struct splash_blob_header {
* Enums for on-disk types
*/
+enum splash_anim_type {
+ SPLASH_ANIM_NONE = 0,
+ SPLASH_ANIM_LOOP_FORWARD = 1,
+};
+
enum splash_corner {
SPLASH_CORNER_CENTER = 0,
SPLASH_CORNER_TOP_LEFT = 1,
@@ -40,6 +40,8 @@ struct splash_pic_priv {
struct splash_blob_priv *blobs;
u16 blobs_loaded;
+
+ u16 anim_nextframe;
};
@@ -98,8 +100,9 @@ extern struct splash_priv splash_global;
*/
void bootsplash_do_render_background(struct fb_info *info);
-void bootsplash_do_render_pictures(struct fb_info *info);
+void bootsplash_do_render_pictures(struct fb_info *info, bool is_update);
void bootsplash_do_render_flush(struct fb_info *info);
+void bootsplash_do_step_animations(void);
void bootsplash_free_locked(void);
@@ -67,6 +67,7 @@ void bootsplash_free_locked(void)
int bootsplash_activate_buf(char *buf, long buflen)
{
+ bool have_anim = false;
unsigned int i;
char *walker;
@@ -141,6 +142,13 @@ int bootsplash_activate_buf(char *buf, long buflen)
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));
@@ -213,6 +221,7 @@ int bootsplash_activate_buf(char *buf, long buflen)
/* Walk over pictures and ensure all blob slots are filled */
for (i = 0; i < splash_global.header->num_pics; i++) {
struct splash_pic_priv *pp = &splash_global.pics[i];
+ 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",
@@ -220,9 +229,24 @@ int bootsplash_activate_buf(char *buf, long buflen)
goto err;
}
+
+ if (ph->num_blobs < 2 || ph->anim_loop > pp->blobs_loaded)
+ /* Nothing to animate or loading error */
+ ph->anim_type = SPLASH_ANIM_NONE;
+ else if (ph->anim_type)
+ have_anim = true;
}
+ /* Disable animation timer if there is nothing to animate */
+ if (!have_anim)
+ splash_global.header->frame_ms = 0;
+
+ /* Enforce minimum delay between frames */
+ if (splash_global.header->frame_ms > 0
+ && splash_global.header->frame_ms < 20)
+ splash_global.header->frame_ms = 20;
+
/* Force a full redraw when the splash is re-activated */
splash_global.splash_fb = NULL;
@@ -151,7 +151,7 @@ void bootsplash_do_render_background(struct fb_info *info)
}
-void bootsplash_do_render_pictures(struct fb_info *info)
+void bootsplash_do_render_pictures(struct fb_info *info, bool is_update)
{
unsigned int i;
@@ -167,7 +167,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;
@@ -274,3 +278,23 @@ void bootsplash_do_render_flush(struct fb_info *info)
info->fbops->fb_copyarea(info, &area);
}
}
+
+
+void bootsplash_do_step_animations(void)
+{
+ unsigned int i;
+
+ /* Step every animation once */
+ for (i = 0; i < splash_global.header->num_pics; i++) {
+ struct splash_pic_priv *pp = &splash_global.pics[i];
+
+ if (pp->blobs_loaded < 1)
+ 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;
+ }
+ }
+}