/*
 * Copyright (c) 2017 Actions Semiconductor Co., Ltd
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief link memory address overlay helper
 */

#include <kernel.h>
#include <string.h>
#include <section_overlay.h>
#include <logging/log.h>

LOG_MODULE_REGISTER(overlay, LOG_LEVEL_DBG);


struct overlay_info {
	u32_t	idcode;

	u32_t	code_vma;
	u32_t	code_size;
	u32_t	code_lma;

	u32_t	data_vma;
	u32_t	data_size;
	u32_t	data_lma;

	u32_t	bss_vma;
	u32_t	bss_size;
};

struct overlay_table
{
	u32_t magic;
	u32_t count;

	const struct overlay_info items[7];
};

#ifndef __UVISION_VERSION
/* overlay table is dynamic generated by linker */
extern const struct overlay_table __overlay_table;
#else
extern char __mp3_p_overlay_bss_start[];
extern char __mp3_p_overlay_bss_size[];
extern char __mp3_p_overlay_data_start[];
extern char __mp3_p_overlay_data_size[];
extern char __mp3_p_overlay_data_image_start[];

extern char __aac_p_overlay_bss_start[];
extern char __aac_p_overlay_bss_size[];
extern char __aac_p_overlay_data_start[];
extern char __aac_p_overlay_data_size[];
extern char __aac_p_overlay_data_image_start[];

extern char __ape_p_overlay_bss_start[];
extern int __ape_p_overlay_bss_size[];
extern char __ape_p_overlay_data_start[];
extern char __ape_p_overlay_data_size[];
extern char __ape_p_overlay_data_image_start[];

extern char __wav_p_overlay_bss_start[];
extern char __wav_p_overlay_bss_size[];
extern char __wav_p_overlay_data_start[];
extern char __wav_p_overlay_data_size[];
extern char __wav_p_overlay_data_image_start[];

extern char __wma_p_overlay_bss_start[];
extern char __wma_p_overlay_bss_size[];
extern char __wma_p_overlay_data_start[];
extern char __wma_p_overlay_data_size[];
extern char __wma_p_overlay_data_image_start[];

extern char __flac_p_overlay_bss_start[];
extern char __flac_p_overlay_bss_size[];
extern char __flac_p_overlay_data_start[];
extern char __flac_p_overlay_data_size[];
extern char __flac_p_overlay_data_image_start[];

const struct overlay_table __overlay_table = {
	.magic = OVERLAY_TABLE_MAGIC,
	.count = 7,
	{
		{
			.idcode = OVERLAY_ID_LIBAPMP3,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__mp3_p_overlay_data_start,
			.data_size = 0,
			.data_lma = (int)__mp3_p_overlay_data_image_start,
			.bss_vma = (int)__mp3_p_overlay_bss_start,
			.bss_size = (int)__mp3_p_overlay_bss_size,
		},
		{
			.idcode = OVERLAY_ID_LIBAPFLA,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__flac_p_overlay_data_start,
			.data_size = (int)__flac_p_overlay_data_size,
			.data_lma = (int)__flac_p_overlay_data_image_start,
			.bss_vma = (int)__flac_p_overlay_bss_start,
			.bss_size = (int)__flac_p_overlay_bss_size,
		},
		{
			.idcode = OVERLAY_ID_LIBAPAPE,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__ape_p_overlay_data_start,
			.data_size = (int)__ape_p_overlay_data_size,
			.data_lma = (int)__ape_p_overlay_data_image_start,
			.bss_vma = (int)__ape_p_overlay_bss_start,
			.bss_size = (int)__ape_p_overlay_bss_size,
		},
		{
			.idcode = OVERLAY_ID_LIBAPWAV,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__wav_p_overlay_data_start,
			.data_size = (int)__wav_p_overlay_data_size,
			.data_lma = (int)__wav_p_overlay_data_image_start,
			.bss_vma = (int)__wav_p_overlay_bss_start,
			.bss_size = (int)__wav_p_overlay_bss_size,
		},
		{
			.idcode = OVERLAY_ID_LIBAPMP3,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__mp3_p_overlay_data_start,
			.data_size =(int)__mp3_p_overlay_data_size,
			.data_lma = (int)__mp3_p_overlay_data_image_start,
			.bss_vma = (int)__mp3_p_overlay_bss_start,
			.bss_size = (int)__mp3_p_overlay_bss_size,
		},
		{
			.idcode = OVERLAY_ID_LIBAPWMA,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__wma_p_overlay_data_start,
			.data_size = (int)__wma_p_overlay_data_size,
			.data_lma = (int)__wma_p_overlay_data_image_start,
			.bss_vma = (int)__wma_p_overlay_bss_start,
			.bss_size = (int)__wma_p_overlay_bss_size,
		},
		{
			.idcode = OVERLAY_ID_LIBAPAAC,
			.code_vma = 0,
			.code_size = 0,
			.code_lma = 0,
			.data_vma = (int)__aac_p_overlay_data_start,
			.data_size = (int)__aac_p_overlay_data_size,
			.data_lma = (int)__aac_p_overlay_data_image_start,
			.bss_vma = (int)__aac_p_overlay_bss_start,
			.bss_size = (int)__aac_p_overlay_bss_size,
		},
	},
};
#endif

static int check_overlay_table(const struct overlay_table *tbl)
{
	if (!tbl || tbl->magic != OVERLAY_TABLE_MAGIC || !tbl->count)
		return -1;

	return 0;
}

void overlay_dump(void)
{
	const struct overlay_info *ovl;
	int i, cnt;

	if (check_overlay_table(&__overlay_table)) {
		printk("invalid overlay table\n");
		return;
	}

	cnt = __overlay_table.count;

	printk("section overlay, total count %d\n", cnt);

	for (i = 0; i < cnt; i++) {
		ovl = &__overlay_table.items[i];

		printk(" -- item %d --\n", i);
		printk("      idcode: %d\n", ovl->idcode);
		printk("    code_vma: 0x%08x\n", ovl->code_vma);
		printk("   code_size: 0x%08x\n", ovl->code_size);
		printk("    code_lma: 0x%08x\n", ovl->code_lma);
		printk("    data_vma: 0x%08x\n", ovl->data_vma);
		printk("   data_size: 0x%08x\n", ovl->data_size);
		printk("    data_lma: 0x%08x\n", ovl->data_lma);
		printk("     bss_vma: 0x%08x\n", ovl->bss_vma);
		printk("    bss_size: 0x%08x\n", ovl->bss_size);
	}

}

int overlay_section_init(u32_t idcode)
{
	const struct overlay_info *ovl = NULL;
	int i, cnt;

	LOG_DBG("init overlay 0x%x", idcode);

	if (check_overlay_table(&__overlay_table)) {
		LOG_WRN("invalid overlay table\n");
		return -1;
	}

	cnt = __overlay_table.count;
	for (i = 0; i < cnt; i++) {
		ovl = &__overlay_table.items[i];
		if (idcode == ovl->idcode)
			break;
	}

	if (cnt == i) {
		LOG_WRN("cannot found overlay for idcode 0x%x\n", idcode);
		return -1;
	}

	if (ovl->code_size) {
		LOG_DBG("overlay id:0x%x: copy code from 0x%x to 0x%x, size 0x%x\n",
			idcode, ovl->code_lma, ovl->code_vma, ovl->code_size);
		memcpy((char *)ovl->code_vma, (char *)ovl->code_lma, ovl->code_size);
	}

	if (ovl->data_size) {
		LOG_DBG("overlay id:0x%x: copy data from 0x%x to 0x%x, size 0x%x\n",
			idcode, ovl->data_lma, ovl->data_vma, ovl->data_size);
		memcpy((char *)ovl->data_vma, (char *)ovl->data_lma, ovl->data_size);
	}

	if (ovl->bss_size) {
		LOG_DBG("overlay id:0x%x: init bss from 0x%x, size 0x%x\n",
			idcode,  ovl->bss_vma, ovl->bss_size);
		memset((char *)ovl->bss_vma, 0x0, ovl->bss_size);
	}

	LOG_DBG("init overlay 0x%x ok", idcode);
	return 0;
}
