/*
 * Procedures for drawing on the screen early on in the boot process.
 *
 * Benjamin Herrenschmidt <benh@kernel.crashing.org>
 *
 *   move to coreboot by LYH yhlu@tyan.com
 */

#if 0

#include <delay.h>
#include <stdlib.h>
#include <string.h>
#include <arch/io.h>

#include <console/console.h>
#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ids.h>
#include <device/pci_ops.h>

#endif

#include <arch/io.h>
#include <string.h>
#include <console/console.h>


#include <arch/byteorder.h>

#include <console/btext.h>

//#define NO_SCROLL

#ifndef NO_SCROLL
static void scrollscreen(void);
#endif

static void draw_byte(unsigned char c, u32 locX, u32 locY);
#if 0
static void draw_byte_32(unsigned char *bits, u32 *base, u32 rb);
static void draw_byte_16(unsigned char *bits, u32 *base, u32 rb);
#endif
static void draw_byte_8(unsigned char *bits, u32 *base, u32 rb);

static u32 g_loc_X;
static u32 g_loc_Y;
static u32 g_max_loc_X;
static u32 g_max_loc_Y;

#define CHAR_256 0

#if CHAR_256==1
#define cmapsz	(16*256)
#else
#define cmapsz        (16*96)
#endif

extern unsigned char vga_font[cmapsz];

u32 boot_text_mapped;

boot_infos_t disp_bi;

#define BTEXT
#define BTDATA


/* This function will enable the early boot text when doing OF booting. This
 * way, xmon output should work too
 */
void
btext_setup_display(u32 width, u32 height, u32 depth, u32 pitch,
		    unsigned long address)
{
	boot_infos_t* bi = &disp_bi;

	g_loc_X = 0;
	g_loc_Y = 0;
	g_max_loc_X = width / 8;
	g_max_loc_Y = height / 16;
//	bi->logicalDisplayBase = (unsigned char *)address;
	bi->dispDeviceBase = (unsigned char *)address;
	bi->dispDeviceRowBytes = pitch;
	bi->dispDeviceDepth = depth;
	bi->dispDeviceRect[0] = bi->dispDeviceRect[1] = 0;
	bi->dispDeviceRect[2] = width;
	bi->dispDeviceRect[3] = height;
	boot_text_mapped = 0;
}

/* Here's a small text engine to use during early boot
 * or for debugging purposes
 *
 * todo:
 *
 *  - build some kind of vgacon with it to enable early printk
 *  - move to a separate file
 *  - add a few video driver hooks to keep in sync with display
 *    changes.
 */

void
map_boot_text(void)
{
#if 0
	unsigned long base, offset, size;
	boot_infos_t *bi = &disp_bi;

	if (bi->dispDeviceBase == 0)
		return;
	base = ((unsigned long) bi->dispDeviceBase) & 0xFFFFF000UL;
	offset = ((unsigned long) bi->dispDeviceBase) - base;
	size = bi->dispDeviceRowBytes * bi->dispDeviceRect[3] + offset
		+ bi->dispDeviceRect[0];
	bi->logicalDisplayBase = ioremap(base,0x800000 );
	if (bi->logicalDisplayBase == 0)
		return;
//	bi->logicalDisplayBase += offset;
#endif
	boot_text_mapped = 1;
}

/* Calc the base address of a given point (x,y) */
static unsigned char * BTEXT
calc_base(boot_infos_t *bi, u32 x, u32 y)
{
	unsigned char *base;
#if 0
	base = bi->logicalDisplayBase;
	if (base == 0)
#endif
		base = bi->dispDeviceBase;
	base += (x + bi->dispDeviceRect[0]) * (bi->dispDeviceDepth >> 3);
	base += (y + bi->dispDeviceRect[1]) * bi->dispDeviceRowBytes;
	return base;
}

/* Adjust the display to a new resolution */
void
btext_update_display(unsigned long phys, u32 width, u32 height,
		     u32 depth, u32 pitch)
{
	boot_infos_t *bi = &disp_bi;
#if 0
	if (bi->dispDeviceBase == 0)
		return;
	/* check it's the same frame buffer (within 256MB) */
	if ((phys ^ (unsigned long)bi->dispDeviceBase) & 0xf0000000)
		return;
#endif

	bi->dispDeviceBase = (u8 *) phys;
	bi->dispDeviceRect[0] = 0;
	bi->dispDeviceRect[1] = 0;
	bi->dispDeviceRect[2] = width;
	bi->dispDeviceRect[3] = height;
	bi->dispDeviceDepth = depth;
	bi->dispDeviceRowBytes = pitch;
	if (boot_text_mapped) {
#if 0
		iounmap(bi->logicalDisplayBase);
#endif
		boot_text_mapped = 0;
	}
	map_boot_text();
	g_loc_X = 0;
	g_loc_Y = 0;
	g_max_loc_X = width / 8;
	g_max_loc_Y = height / 16;
}

void BTEXT btext_clearscreen(void)
{
	boot_infos_t* bi	= &disp_bi;
	u32 *base	= (u32 *)calc_base(bi, 0, 0);
	u32 width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
					(bi->dispDeviceDepth >> 3)) >> 2;
	u32 i,j;

	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
	{
		u32 *ptr = base;
		for(j=width; j; --j)
			*(ptr++) = 0;
		base += (bi->dispDeviceRowBytes >> 2);
	}
}

#if 0
__inline__ void dcbst(const void* addr)
{
	__asm__ __volatile__ ("dcbst 0,%0" :: "r" (addr));
}

void BTEXT btext_flushscreen(void)
{
	boot_infos_t* bi	= &disp_bi;
	u32  *base	= (unsigned long *)calc_base(bi, 0, 0);
	u32 width 	= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
					(bi->dispDeviceDepth >> 3)) >> 2;
	u32 i,j;

	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1]); i++)
	{
		u32 *ptr = base;
		for(j=width; j>0; j-=8) {
			dcbst(ptr);
			ptr += 8;
		}
		base += (bi->dispDeviceRowBytes >> 2);
	}
}
#endif


#ifndef NO_SCROLL
static BTEXT void
scrollscreen(void)
{
	boot_infos_t* bi		= &disp_bi;
	u32 *src		= (u32 *)calc_base(bi,0,16);
	u32 *dst		= (u32 *)calc_base(bi,0,0);
	u32 width		= ((bi->dispDeviceRect[2] - bi->dispDeviceRect[0]) *
						(bi->dispDeviceDepth >> 3)) >> 2;
	u32 i,j;

	for (i=0; i<(bi->dispDeviceRect[3] - bi->dispDeviceRect[1] - 16); i++)
	{
		u32 *src_ptr = src;
		u32 *dst_ptr = dst;
		for(j=width; j; --j)
			*(dst_ptr++) = *(src_ptr++);
		src += (bi->dispDeviceRowBytes >> 2);
		dst += (bi->dispDeviceRowBytes >> 2);
	}
	for (i=0; i<16; i++)
	{
		u32 *dst_ptr = dst;
		for(j=width; j; --j)
			*(dst_ptr++) = 0;
		dst += (bi->dispDeviceRowBytes >> 2);
	}
}
#endif /* ndef NO_SCROLL */

void BTEXT btext_drawchar(char c)
{
	u32 cline = 0;

	if (!boot_text_mapped)
		return;

	switch (c) {
	case '\b':
		if (g_loc_X > 0)
			--g_loc_X;
		break;
	case '\t':
		g_loc_X = (g_loc_X & -8) + 8;
		break;
	case '\r':
		g_loc_X = 0;
		break;
	case '\n':
		g_loc_X = 0;
		g_loc_Y++;
		cline = 1;
		break;
	default:
		draw_byte(c, g_loc_X++, g_loc_Y);
	}
	if (g_loc_X >= g_max_loc_X) {
		g_loc_X = 0;
		g_loc_Y++;
		cline = 1;
	}
#ifndef NO_SCROLL
	while (g_loc_Y >= g_max_loc_Y) {
		scrollscreen();
		g_loc_Y--;
	}
#else
	/* wrap around from bottom to top of screen so we don't
	   waste time scrolling each line.  -- paulus. */
	if (g_loc_Y >= g_max_loc_Y)
		g_loc_Y = 0;
	if (cline) {
		for (x = 0; x < g_max_loc_X; ++x)
			draw_byte(' ', x, g_loc_Y);
	}
#endif
}
#if 0
void BTEXT
btext_drawstring(const char *c)
{
	if (!boot_text_mapped)
		return;
	while (*c)
		btext_drawchar(*c++);
}

void BTEXT
btext_drawhex(u32 v)
{
	static char hex_table[] = "0123456789abcdef";

	if (!boot_text_mapped)
		return;
	btext_drawchar(hex_table[(v >> 28) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >> 24) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >> 20) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >> 16) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >> 12) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >>  8) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >>  4) & 0x0000000FUL]);
	btext_drawchar(hex_table[(v >>  0) & 0x0000000FUL]);
	btext_drawchar(' ');
}
#endif
static void BTEXT
draw_byte(unsigned char c, u32 locX, u32 locY)
{
	boot_infos_t* bi	= &disp_bi;
	unsigned char *base	= calc_base(bi, locX << 3, locY << 4);
#if CHAR_256==1
        unsigned char *font     = &vga_font[((u32)c) * 16];
#else
	unsigned char *font	= &vga_font[((u32)c-0x20) * 16]; // skip the first 0x20
#endif
	u32 rb			= bi->dispDeviceRowBytes;

	switch(bi->dispDeviceDepth) {
#if 0
	case 24:
	case 32:
		draw_byte_32(font, (u32 *)base, rb);
		break;
	case 15:
	case 16:
		draw_byte_16(font, (u32 *)base, rb);
		break;
#endif
	case 8:
		draw_byte_8(font, (u32 *)base, rb);
		break;
	}
}
static u32 expand_bits_8[16] BTDATA = {
#if defined(__BIG_ENDIAN)
    0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
    0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
    0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
    0xffff0000,0xffff00ff,0xffffff00,0xffffffff
#elif defined(__LITTLE_ENDIAN)
    0x00000000,0xff000000,0x00ff0000,0xffff0000,
    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
#else
#error FIXME: No endianness??
#endif
};
#if 0
static const u32 expand_bits_16[4] BTDATA = {
#if defined(__BIG_ENDIAN)
    0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
#elif defined(__LITTLE_ENDIAN)
    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
#else
#error FIXME: No endianness??
#endif
};
#endif
#if 0
static void BTEXT
draw_byte_32(unsigned char *font, u32 *base, u32 rb)
{
	u32 l, bits;
	u32 fg = 0xFFFFFFFF;
	u32 bg = 0x00000000;

	for (l = 0; l < 16; ++l)
	{
		bits = *font++;
		base[0] = (-(bits >> 7) & fg) ^ bg;
		base[1] = (-((bits >> 6) & 1) & fg) ^ bg;
		base[2] = (-((bits >> 5) & 1) & fg) ^ bg;
		base[3] = (-((bits >> 4) & 1) & fg) ^ bg;
		base[4] = (-((bits >> 3) & 1) & fg) ^ bg;
		base[5] = (-((bits >> 2) & 1) & fg) ^ bg;
		base[6] = (-((bits >> 1) & 1) & fg) ^ bg;
		base[7] = (-(bits & 1) & fg) ^ bg;
		base = (u32 *) ((char *)base + rb);
	}
}

static void BTEXT
draw_byte_16(unsigned char *font, u32 *base, u32 rb)
{
	u32 l, bits;
	u32 fg = 0xFFFFFFFF;
	u32 bg = 0x00000000;
	u32 *eb = expand_bits_16;

	for (l = 0; l < 16; ++l)
	{
		bits = *font++;
		base[0] = (eb[bits >> 6] & fg) ^ bg;
		base[1] = (eb[(bits >> 4) & 3] & fg) ^ bg;
		base[2] = (eb[(bits >> 2) & 3] & fg) ^ bg;
		base[3] = (eb[bits & 3] & fg) ^ bg;
		base = (u32 *) ((char *)base + rb);
	}
}
#endif
static void BTEXT
draw_byte_8(unsigned char *font, u32 *base, u32 rb)
{
	u32 l, bits;
	u32 fg = 0x0F0F0F0F;
	u32 bg = 0x00000000;
	u32 *eb = expand_bits_8;

	for (l = 0; l < 16; ++l)
	{
		bits = *font++;
		base[0] = (eb[bits >> 4] & fg) ^ bg;
		base[1] = (eb[bits & 0xf] & fg) ^ bg;
		base = (u32 *) ((char *)base + rb);
	}
}

void btext_init(void)
{
	btext_setup_display(640, 480, 8, 640,0xfc000000);
// Not realy init
// It will be init in xlinit.c because We only can access fb after the device resource is allocated and enabled.

}

void btext_tx_byte(unsigned char data)
{
        btext_drawchar(data);
}

static const struct console_driver btext_console __console = {
        .init    = btext_init,
        .tx_byte = btext_tx_byte,
        .rx_byte = 0,
        .tst_byte = 0,
};

