328 lines
9.1 KiB
Diff
328 lines
9.1 KiB
Diff
|
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
|