/*	$NetBSD: cons_fb.c,v 1.4 2008/04/28 20:23:18 martin Exp $	*/

/*-
 * Copyright (c) 2004 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by UCHIYAMA Yasushi.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>

#include <lib/libsa/stand.h>
#include <lib/libkern/libkern.h>

#include <machine/sbd.h>

#include "console.h"

struct fb fb;

void
fb_set_addr(uint32_t fb_addr, uint32_t fb_size, uint32_t font_addr)
{

	fb.fb_addr = (uint8_t *)fb_addr;
	fb.fb_size = fb_size;
	fb.font_addr = (uint8_t *)font_addr;

	cons.init = fb_init;
	cons.putc = fb_drawchar;
	cons.scroll = fb_scroll;
	cons.cursor = fb_drawcursor;
}

void *
fb_get_addr(void)
{

	return fb.fb_addr;
}

void
fb_init(void)
{

	cons.x = X_INIT;
	cons.y = Y_INIT;
	fb.active = true;
	fb_clear(0, 0, FB_WIDTH, FB_HEIGHT, CONS_BG);
}

void
fb_active(bool on)
{

	if (fb.active && !on)
		printf("FB disabled.\n");

	fb.active = on;

	if (fb.active && on)
		printf("FB enabled.\n");
}

void
fb_scroll(void)
{

	if (!fb.active)
		return;
#if 0	/* 1-line scroll */
	cons.y--;
	fb_copy(0, ROM_FONT_HEIGHT, 0, 0,
	    FB_WIDTH, FB_HEIGHT * (CONS_HEIGHT - 1));
	fb_clear(0, cons.y * ROM_FONT_HEIGHT, FB_WIDTH, ROM_FONT_HEIGHT,
	    CONS_BG);
#else	/* jump scroll */
	cons.y /= 2;
	fb_copy(0, cons.y * ROM_FONT_HEIGHT, 0, 0, FB_WIDTH, FB_HEIGHT / 2);
	fb_clear(0, cons.y *ROM_FONT_HEIGHT, FB_WIDTH, FB_HEIGHT / 2, CONS_BG);
#endif
}

#define	MINMAX(x, min, max)						\
	((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
void
fb_clear(int x, int y, int w, int h, int q)
{
	uint8_t *p;
	int i, j, k, xend, yend;

	if (!fb.active)
		return;

	x = MINMAX(x, 0, FB_WIDTH);
	y = MINMAX(y, 0, FB_HEIGHT);
	xend = MINMAX(x + w, 0, FB_WIDTH);
	yend = MINMAX(y + h , 0, FB_HEIGHT);

	p = (uint8_t *)fb.fb_addr + x + y * FB_LINEBYTES;

	j = xend - x;
	k = j + FB_LINEBYTES - w;
	for (i = y; i < yend; i++, p+= k)
		memset(p, q, j);
}

void
fb_copy(int x0, int y0, int x1, int y1, int w, int h)
{
	int x1end, y1end, i, j, k;
	uint8_t *p, *q;

	if (!fb.active)
		return;

	x0 = MINMAX(x0, 0, FB_WIDTH);
	y0 = MINMAX(y0, 0, FB_HEIGHT);
	x1 = MINMAX(x1, 0, FB_WIDTH);
	y1 = MINMAX(y1, 0, FB_HEIGHT);
	x1end = MINMAX(x1 + w, 0, FB_WIDTH);
	y1end = MINMAX(y1 + h, 0, FB_HEIGHT);

	p = fb.fb_addr + x1 + y1 * FB_LINEBYTES;
	q = fb.fb_addr + x0 + y0 * FB_LINEBYTES;

	j = x1end - x1;
	k = j + FB_LINEBYTES - w;
	for (i = y1; i < y1end; i++, p += k, q += k)
		memmove(p, q, j);
}
#undef MINMAX

void
fb_drawchar(int x, int y, int c)
{
	uint16_t *font_addr;
	int font_ofs;

	if (!fb.active)
		return;

	if ((font_ofs = (c & 0x7f) - 0x20) < 0)
		return;

	font_addr = (uint16_t *)(fb.font_addr +
	    font_ofs * sizeof(uint16_t) * ROM_FONT_HEIGHT);

	fb_drawfont(x, y, font_addr);
}

void
fb_drawfont(int x, int y, uint16_t *font_addr)
{
	uint8_t *fb_addr;
	uint16_t bitmap;
	int i, j;

	if (!fb.active)
		return;

	fb_addr = fb.fb_addr + x + y * FB_LINEBYTES;

	for (i = 0;  i < 24; i++) {
		bitmap = *font_addr++;
		for (j = 0; j < 12; j++, bitmap <<= 1)
			fb_addr[j] = bitmap & 0x8000 ? CONS_FG : CONS_BG;
		fb_addr += FB_LINEBYTES;
	}
}

void
fb_drawcursor(int x, int y)
{
	uint8_t *fb_addr;
	int i, j;

	if (!fb.active)
		return;

	fb_addr = fb.fb_addr + x + y * FB_LINEBYTES;
	for (i = 0;  i < 24; i++) {
		for (j = 0; j < 12; j++)
			fb_addr[j] = fb_addr[j] == CONS_FG ? CONS_BG : CONS_FG;
		fb_addr += FB_LINEBYTES;
	}
}
