[RFC 10/14] bootsplash: Add animation support

From: Max Staudt
Date: Wed Oct 25 2017 - 08:48:27 EST


Each 'picture' in the splash file can consist of multiple 'blobs'.

If animation is enabled, these blobs become the frames of an animation,
in the order in which they are stored in the file.

Note: There is only one global timer, so all animations happen at
the same frame rate. It doesn't really make sense to animate
more than one object at a time anyway.

Furthermore, this patch introduces a check for reusing a framebuffer
where the splash has recently been painted on - in this case, we only
redraw the objects that are animated.

Signed-off-by: Max Staudt <mstaudt@xxxxxxx>
Reviewed-by: Oliver Neukum <oneukum@xxxxxxxx>
---
drivers/video/fbdev/core/bootsplash.c | 62 +++++++++++++++++++++++---
drivers/video/fbdev/core/bootsplash_file.h | 31 ++++++++++++-
drivers/video/fbdev/core/bootsplash_internal.h | 5 ++-
drivers/video/fbdev/core/bootsplash_load.c | 24 ++++++++++
drivers/video/fbdev/core/bootsplash_render.c | 28 +++++++++++-
5 files changed, 140 insertions(+), 10 deletions(-)

diff --git a/drivers/video/fbdev/core/bootsplash.c b/drivers/video/fbdev/core/bootsplash.c
index 3253506d26a7..e44376d89750 100644
--- a/drivers/video/fbdev/core/bootsplash.c
+++ b/drivers/video/fbdev/core/bootsplash.c
@@ -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);

diff --git a/drivers/video/fbdev/core/bootsplash_file.h b/drivers/video/fbdev/core/bootsplash_file.h
index 59a084a05171..367d55888ab4 100644
--- a/drivers/video/fbdev/core/bootsplash_file.h
+++ b/drivers/video/fbdev/core/bootsplash_file.h
@@ -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,
diff --git a/drivers/video/fbdev/core/bootsplash_internal.h b/drivers/video/fbdev/core/bootsplash_internal.h
index 791591d10d6b..ba63f3eb99ec 100644
--- a/drivers/video/fbdev/core/bootsplash_internal.h
+++ b/drivers/video/fbdev/core/bootsplash_internal.h
@@ -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);
diff --git a/drivers/video/fbdev/core/bootsplash_load.c b/drivers/video/fbdev/core/bootsplash_load.c
index 2f983a74664c..da171cc961a4 100644
--- a/drivers/video/fbdev/core/bootsplash_load.c
+++ b/drivers/video/fbdev/core/bootsplash_load.c
@@ -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;

diff --git a/drivers/video/fbdev/core/bootsplash_render.c b/drivers/video/fbdev/core/bootsplash_render.c
index 444233583f6f..b3667a0f067b 100644
--- a/drivers/video/fbdev/core/bootsplash_render.c
+++ b/drivers/video/fbdev/core/bootsplash_render.c
@@ -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;
+ }
+ }
+}
--
2.12.3