app: aboot: support RLE24 compressed logo image

While the RGB raw data is so large, we can compress the data by
the RLE24 algorithm. And usually the logo image is simple and
have so much repeated pixels.

Change-Id: Ibc061aed6464ea2bd81925b35856ba1424513999
diff --git a/app/aboot/aboot.c b/app/aboot/aboot.c
index 88e192d..b4c10db 100644
--- a/app/aboot/aboot.c
+++ b/app/aboot/aboot.c
@@ -2647,146 +2647,155 @@
 	fastboot_okay("");
 }
 
-struct fbimage* splash_screen_flash();
+static uint8_t logo_header[LOGO_IMG_HEADER_SIZE];
 
-int splash_screen_check_header(struct fbimage *logo)
+int splash_screen_check_header(logo_img_header *header)
 {
-	if (memcmp(logo->header.magic, LOGO_IMG_MAGIC, 8))
+	if (memcmp(header->magic, LOGO_IMG_MAGIC, 8))
 		return -1;
-	if (logo->header.width == 0 || logo->header.height == 0)
+	if (header->width == 0 || header->height == 0)
 		return -1;
 	return 0;
 }
 
-struct fbimage* splash_screen_flash()
+int splash_screen_flash()
 {
 	struct ptentry *ptn;
 	struct ptable *ptable;
+	struct logo_img_header *header;
 	struct fbcon_config *fb_display = NULL;
-	struct fbimage *logo = NULL;
-
-
-	logo = (struct fbimage *) malloc(ROUNDUP(page_size, sizeof(struct fbimage)));
-	ASSERT(logo);
 
 	ptable = flash_get_ptable();
 	if (ptable == NULL) {
-	dprintf(CRITICAL, "ERROR: Partition table not found\n");
-	goto err;
+		dprintf(CRITICAL, "ERROR: Partition table not found\n");
+		return -1;
 	}
+
 	ptn = ptable_find(ptable, "splash");
 	if (ptn == NULL) {
 		dprintf(CRITICAL, "ERROR: splash Partition not found\n");
-		goto err;
+		return -1;
 	}
-
-	if (flash_read(ptn, 0,(unsigned int *) logo, sizeof(logo->header))) {
+	if (flash_read(ptn, 0, (void *)logo_header, LOGO_IMG_HEADER_SIZE)) {
 		dprintf(CRITICAL, "ERROR: Cannot read boot image header\n");
-		goto err;
+		return -1;
 	}
 
-	if (splash_screen_check_header(logo)) {
+	header = (struct logo_img_header *)logo_header;
+	if (splash_screen_check_header(header)) {
 		dprintf(CRITICAL, "ERROR: Boot image header invalid\n");
-		goto err;
+		return -1;
 	}
 
 	fb_display = fbcon_display();
 	if (fb_display) {
-		if ((logo->header.width != fb_display->width) || (logo->header.height != fb_display->height)) {
-			dprintf(CRITICAL, "Logo config doesn't match with fb config. Fall back to default logo\n");
-			goto err;
+		if (header->type && (header->blocks != 0)) { // RLE24 compressed data
+			uint8_t *base = (uint8_t *) fb_display->base + LOGO_IMG_OFFSET;
+
+			/* if the logo is full-screen size, remove "fbcon_clear()" */
+			if ((header->width != fb_display->width)
+						|| (header->height != fb_display->height))
+					fbcon_clear();
+
+			if (flash_read(ptn + LOGO_IMG_HEADER_SIZE, 0,
+				(uint32_t *)base,
+				(header->blocks * 512))) {
+				dprintf(CRITICAL, "ERROR: Cannot read splash image from partition\n");
+				return -1;
+			}
+			fbcon_extract_to_screen(header, base);
+			return 0;
 		}
+
+		if ((header->width != fb_display->width) || (header->height != fb_display->height)) {
+			dprintf(CRITICAL, "Logo config doesn't match with fb config. Fall back default logo\n");
+			return -1;
+		}
+
 		uint8_t *base = (uint8_t *) fb_display->base;
-		if (flash_read(ptn + sizeof(logo->header), 0,
-			base,
-			((((logo->header.width * logo->header.height * fb_display->bpp/8) + 511) >> 9) << 9))) {
+		if (flash_read(ptn + LOGO_IMG_HEADER_SIZE, 0,
+			(uint32_t *)base,
+			((((header->width * header->height * fb_display->bpp/8) + 511) >> 9) << 9))) {
 			fbcon_clear();
 			dprintf(CRITICAL, "ERROR: Cannot read splash image from partition\n");
-			goto err;
+			return -1;
 		}
-		logo->image = base;
 	}
 
-	return logo;
-
-err:
-	free(logo);
-	return NULL;
+	return 0;
 }
 
-struct fbimage* splash_screen_mmc()
+int splash_screen_mmc()
 {
 	int index = INVALID_PTN;
 	unsigned long long ptn = 0;
 	struct fbcon_config *fb_display = NULL;
-	struct fbimage *logo = NULL;
-	uint32_t blocksize;
-	uint32_t readsize;
-	uint32_t logosize;
-	uint32_t ptn_size;
+	struct logo_img_header *header;
 
 	index = partition_get_index("splash");
 	if (index == 0) {
 		dprintf(CRITICAL, "ERROR: splash Partition table not found\n");
-		return NULL;
+		return -1;
 	}
 
 	ptn = partition_get_offset(index);
 	if (ptn == 0) {
 		dprintf(CRITICAL, "ERROR: splash Partition invalid\n");
-		return NULL;
+		return -1;
 	}
 
-	ptn_size = partition_get_size(index);
-	blocksize = mmc_get_device_blocksize();
-	logosize = ROUNDUP(sizeof(logo->header), blocksize);
-
-	logo = (struct fbimage *)memalign(CACHE_LINE, ROUNDUP(logosize, CACHE_LINE));
-	ASSERT(logo);
-
-	if (mmc_read(ptn, (uint32_t *) logo, logosize)) {
+	if (mmc_read(ptn, (uint32_t *)logo_header, LOGO_IMG_HEADER_SIZE)) {
 		dprintf(CRITICAL, "ERROR: Cannot read splash image header\n");
-		goto err;
+		return -1;
 	}
 
-	if (splash_screen_check_header(logo)) {
+	header = (struct logo_img_header *)logo_header;
+	if (splash_screen_check_header(header)) {
 		dprintf(CRITICAL, "ERROR: Splash image header invalid\n");
-		goto err;
+		return -1;
 	}
 
 	fb_display = fbcon_display();
 	if (fb_display) {
-		if ((logo->header.width != fb_display->width) || (logo->header.height != fb_display->height)) {
+		/* 1 RLE24 compressed data */
+		if (header->type && (header->blocks != 0)) {
+			uint8_t *base = (uint8_t *) fb_display->base + LOGO_IMG_OFFSET;
+
+			/* if the logo is full-screen size, remove "fbcon_clear()" */
+			if ((header->width != fb_display->width)
+						|| (header->height != fb_display->height))
+				fbcon_clear();
+
+			if (mmc_read(ptn + LOGO_IMG_HEADER_SIZE,
+				(uint32_t *)base,
+				(header->blocks * 512))) {
+				dprintf(CRITICAL, "ERROR: Cannot read splash image from partition\n");
+				return -1;
+			}
+			fbcon_extract_to_screen(header, base);
+			return 0;
+		}
+
+		/* 2 Raw BGR data */
+		if ((header->width != fb_display->width) || (header->height != fb_display->height)) {
 			dprintf(CRITICAL, "Logo config doesn't match with fb config. Fall back default logo\n");
-			goto err;
+			return -1;
 		}
 		uint8_t *base = (uint8_t *) fb_display->base;
-		readsize = ROUNDUP((logo->header.width * logo->header.height * fb_display->bpp/8), blocksize);
-
-		if (readsize > ptn_size)
-		{
-			dprintf(CRITICAL, "@%d:Invalid logo header readsize:%u exceeds ptn_size:%u\n", __LINE__, readsize,ptn_size);
-			goto err;
-		}
-
-		if (mmc_read(ptn + logosize,(uint32_t *)base, readsize)) {
+		if (mmc_read(ptn + LOGO_IMG_HEADER_SIZE,
+			(uint32_t *)base,
+			((((header->width * header->height * fb_display->bpp/8) + 511) >> 9) << 9))) {
 			fbcon_clear();
 			dprintf(CRITICAL, "ERROR: Cannot read splash image from partition\n");
-			goto err;
+			return -1;
 		}
 
-		logo->image = base;
 	}
 
-	return logo;
-
-err:
-	free(logo);
-	return NULL;
+	return 0;
 }
 
-
-struct fbimage* fetch_image_from_partition()
+int fetch_image_from_partition()
 {
 	if (target_is_emmc_boot()) {
 		return splash_screen_mmc();
diff --git a/dev/fbcon/fbcon.c b/dev/fbcon/fbcon.c
index ce79a3e..266e220 100644
--- a/dev/fbcon/fbcon.c
+++ b/dev/fbcon/fbcon.c
@@ -92,10 +92,18 @@
 
 static void fbcon_flush(void)
 {
+	unsigned total_x, total_y;
+	unsigned bytes_per_bpp;
+
 	if (config->update_start)
 		config->update_start();
 	if (config->update_done)
 		while (!config->update_done());
+
+	total_x = config->width;
+	total_y = config->height;
+	bytes_per_bpp = ((config->bpp) / 8);
+	arch_clean_invalidate_cache_range((addr_t) config->base, (total_x * total_y * bytes_per_bpp));
 }
 
 /* TODO: Take stride into account */
@@ -183,10 +191,10 @@
 		fg = RGB565_WHITE;
 		bg = RGB565_BLACK;
 		break;
-        case FB_FORMAT_RGB888:
-                fg = RGB888_WHITE;
-                bg = RGB888_BLACK;
-                break;
+	case FB_FORMAT_RGB888:
+		fg = RGB888_WHITE;
+		bg = RGB888_BLACK;
+		break;
 	default:
 		dprintf(CRITICAL, "unknown framebuffer pixel format\n");
 		ASSERT(0);
@@ -206,128 +214,132 @@
 
 struct fbcon_config* fbcon_display(void)
 {
-    return config;
+	return config;
 }
 
-
-extern struct fbimage* fetch_image_from_partition();
-void fbcon_putImage(struct fbimage *fbimg, bool flag);
-
-void display_image_on_screen()
+void fbcon_extract_to_screen(logo_img_header *header, void* address)
 {
-	struct fbimage default_fbimg, *fbimg;
-	bool flag = true;
+	const uint8_t *imagestart = (const uint8_t *)address;
+	uint pos = 0, offset;
+	uint count = 0;
+	uint x = 0, y = 0;
+	uint8_t *base, *p;
 
-	fbcon_clear();
-	fbimg = fetch_image_from_partition();
-
-	if(!fbimg) {
-		flag = false;
-		fbimg = &default_fbimg;
-		fbimg->header.width = SPLASH_IMAGE_WIDTH;
-		fbimg->header.height = SPLASH_IMAGE_HEIGHT;
-#if DISPLAY_TYPE_MIPI
-		fbimg->image = (unsigned char *)imageBuffer_rgb888;
-#else
-		fbimg->image = (unsigned char *)imageBuffer;
-#endif
+	if (!config || header->width > config->width
+				|| header->height > config->height) {
+		dprintf(INFO, "the logo img is too large\n");
+		return;
 	}
 
-	fbcon_putImage(fbimg, flag);
-	if(flag)
-		free(fbimg);
+	base = (uint8_t *) config->base;
+
+	/* put the logo to be center */
+	offset = (config->height - header->height) / 2;
+	if (offset)
+		base += (offset * config->width) * 3;
+	offset = (config->width - header->width ) / 2;
+
+	x = offset;
+	while (count < (uint)header->height * (uint)header->width) {
+		uint8_t run = *(imagestart + pos);
+		bool repeat_run = (run & 0x80);
+		uint runlen = (run & 0x7f) + 1;
+		uint runpos;
+
+		/* consume the run byte */
+		pos++;
+
+		p = base + (y * config->width + x) * 3;
+
+		/* start of a run */
+		for (runpos = 0; runpos < runlen; runpos++) {
+			*p++ = *(imagestart + pos);
+			*p++ = *(imagestart + pos + 1);
+			*p++ = *(imagestart + pos + 2);
+			count++;
+
+			x++;
+
+			/* if a run of raw pixels, consume an input pixel */
+			if (!repeat_run)
+				pos += 3;
+		}
+
+		/* if this was a run of repeated pixels, consume the one input pixel we repeated */
+		if (repeat_run)
+			pos += 3;
+
+		/* the generator will keep compressing data line by line */
+		/* don't cross the lines */
+		if (x == header->width + offset) {
+			y++;
+			x = offset;
+		}
+	}
+
 }
 
-void fbcon_putImage(struct fbimage *fbimg, bool flag)
+void display_default_image_on_screen(void)
 {
-    unsigned i = 0;
-    unsigned total_x;
-    unsigned total_y;
-    unsigned bytes_per_bpp;
-    unsigned image_base;
-    unsigned width = 0, pitch = 0, height = 0;
-#if DISPLAY_TYPE_MIPI
-    unsigned char *logo_base = NULL;
-#endif
-    struct logo_img_header *header = NULL;
-
+	unsigned i = 0;
+	unsigned total_x;
+	unsigned total_y;
+	unsigned bytes_per_bpp;
+	unsigned image_base;
 
 	if (!config) {
 		dprintf(CRITICAL,"NULL configuration, image cannot be displayed\n");
 		return;
 	}
 
-	if(fbimg) {
-		header = &fbimg->header;
-		width = pitch = header->width;
-		height = header->height;
-	}
+	fbcon_clear(); // clear screen with Black color
 
 	total_x = config->width;
 	total_y = config->height;
 	bytes_per_bpp = ((config->bpp) / 8);
+	image_base = ((((total_y/2) - (SPLASH_IMAGE_HEIGHT / 2) - 1) *
+			(config->width)) + (total_x/2 - (SPLASH_IMAGE_WIDTH / 2)));
 
 #if DISPLAY_TYPE_MIPI
-	if(fbimg) {
-		logo_base = (unsigned char *)fbimg->image;
-	}
-	if (bytes_per_bpp == 3)
-	{
-		if(flag && header) {
-			if (header->width == config->width && header->height == config->height)
-				return;
-			else {
-				logo_base = (unsigned char *)config->base + LOGO_IMG_OFFSET;
-				if (header->width > config->width) {
-					width = config->width;
-					pitch = header->width;
-					logo_base += (header->width - config->width) / 2 * bytes_per_bpp;
-				} else {
-					width = pitch = header->width;
-				}
-
-				if (header->height > config->height) {
-					height = config->height;
-					logo_base += (header->height - config->height) / 2 * pitch * bytes_per_bpp;
-				} else {
-					height = header->height;
-				}
-			}
-		}
-
-		image_base = ((((total_y/2) - (height / 2) ) *
-				(config->width)) + (total_x/2 - (width / 2)));
-		for (i = 0; i < height; i++) {
+	if (bytes_per_bpp == 3) {
+		for (i = 0; i < SPLASH_IMAGE_HEIGHT; i++) {
 			memcpy (config->base + ((image_base + (i * (config->width))) * bytes_per_bpp),
-				logo_base + (i * pitch * bytes_per_bpp), width * bytes_per_bpp);
+			imageBuffer_rgb888 + (i * SPLASH_IMAGE_WIDTH * bytes_per_bpp),
+			SPLASH_IMAGE_WIDTH * bytes_per_bpp);
 		}
-		/* Flush the contents to memory before giving the data to dma */
-		arch_clean_invalidate_cache_range((addr_t) config->base, (total_x * total_y * bytes_per_bpp));
 	}
-
 	fbcon_flush();
-
 #if DISPLAY_MIPI_PANEL_NOVATEK_BLUE
 	if(is_cmd_mode_enabled())
-        mipi_dsi_cmd_mode_trigger();
+		mipi_dsi_cmd_mode_trigger();
 #endif
 
 #else
-	if (bytes_per_bpp == 2)
-	{
-		image_base = ((((total_y/2) - (height / 2) ) *
-				(config->width)) + (total_x/2 - (width / 2)));
 
-		for (i = 0; i < header->width; i++)
-		{
-			memcpy (config->base +
-				((image_base + (i * (config->width))) * bytes_per_bpp),
-				(fbimg->image + (i * header->height * bytes_per_bpp)),
-				(header->height * bytes_per_bpp));
+	if (bytes_per_bpp == 2) {
+		for (i = 0; i < SPLASH_IMAGE_HEIGHT; i++) {
+			memcpy (config->base + ((image_base + (i * (config->width))) * bytes_per_bpp),
+			imageBuffer + (i * SPLASH_IMAGE_WIDTH * bytes_per_bpp),
+			SPLASH_IMAGE_WIDTH * bytes_per_bpp);
 		}
 	}
-	/* Flush the contents to memory before giving the data to dma */
-	arch_clean_invalidate_cache_range((addr_t) config->base, (total_x * total_y * bytes_per_bpp));
 	fbcon_flush();
 #endif
 }
+
+
+void display_image_on_screen(void)
+{
+#if DISPLAY_TYPE_MIPI
+	int fetch_image_from_partition();
+
+	if (fetch_image_from_partition() < 0) {
+		display_default_image_on_screen();
+	} else {
+		/* data has been put into the right place */
+		fbcon_flush();
+	}
+#else
+	display_default_image_on_screen();
+#endif
+}
diff --git a/include/dev/fbcon.h b/include/dev/fbcon.h
index 2587277..e96b663 100644
--- a/include/dev/fbcon.h
+++ b/include/dev/fbcon.h
@@ -2,7 +2,7 @@
  * Copyright (c) 2008, Google Inc.
  * All rights reserved.
  *
- * Copyright (c) 2009-2014, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -35,17 +35,20 @@
 #define LOGO_IMG_OFFSET (12*1024*1024)
 #define LOGO_IMG_MAGIC "SPLASH!!"
 #define LOGO_IMG_MAGIC_SIZE sizeof(LOGO_IMG_MAGIC) - 1
+#define LOGO_IMG_HEADER_SIZE 512
 
-
-struct logo_img_header {
-    unsigned char magic[LOGO_IMG_MAGIC_SIZE]; // "SPLASH!!"
-    uint32_t width; // logo's width, little endian
-    uint32_t height; // logo's height, little endian
-    uint32_t offset;
-};
+typedef struct logo_img_header {
+	unsigned char magic[LOGO_IMG_MAGIC_SIZE]; // "SPLASH!!"
+	uint32_t width;  // logo's width, little endian
+	uint32_t height; // logo's height, little endian
+	uint32_t type;   // 0, Raw BGR data; 1, RLE24 Compressed data
+	uint32_t blocks; // block number, compressed data size / 512
+	uint32_t offset;
+	uint8_t  reserved[512-28];
+}logo_img_header;
 
 struct fbimage {
-	struct logo_img_header  header;
+	struct logo_img_header header;
 	void *image;
 };
 
@@ -70,5 +73,6 @@
 void fbcon_putc(char c);
 void fbcon_clear(void);
 struct fbcon_config* fbcon_display(void);
+void fbcon_extract_to_screen(logo_img_header *header, void* address);
 
 #endif /* __DEV_FBCON_H */