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