/* vi: set sw=4 ts=4: */
/*
 * CCE - Console Chinese Environment -
 * Copyright (C) 1998-2003 Rui He (rhe@3eti.com)
 *
 * 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 AUTHOR ``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 TERRENCE R. LAMBERT 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.
 * 
 */

/*
  This code is based on linux kernel' frame buffer code.
  I try to make it OS-independent, so it will also work on other OS's 
  frame buffer memory   Max font size 32x32
*/
#include	<stdio.h>
#include	<string.h>
#include	<unistd.h>
#include	<stdlib.h>
#include	<sys/types.h>

#include	<defs.h>
#include	<vc.h>
#include	<vga.h>
#include	<fb.h>

#if defined(SUPPORT_FRAMEBUF)

static u_short nibbletab_cfb4[] = 
{
#if defined(CCE_TARGETARCH_BIGENDIAN)   

	0x0000,0x000f,0x0f00,0x00ff,
	0x0f00,0x0f0f,0x0ff0,0x0fff,
	0xf000,0xf00f,0xf0f0,0xf0ff,
	0xff00,0xff0f,0xfff0,0xffff
#else
    0x0000,0xf000,0x0f00,0xff00,
    0x00f0,0xf0f0,0x0ff0,0xfff0,
    0x000f,0xf00f,0x0f0f,0xff0f,
    0x00ff,0xf0ff,0x0fff,0xffff
#endif
};

static void fbcon_cfb4_clear(int sy, int height, u_char bc)
{
    u_char *dest, *p, bgx;
    int i;

    dest = gramMem + sy * dispInfo.glineByte;

    bgx = bc & 0x0F;
    bgx |= bgx << 4;
    while( height-- > 0)
    {
       p = dest;
       for(i = 0; i < dispInfo.gxdim/2; i++)
       {
          *p++ = bgx;
       }
       dest += dispInfo.glineByte;
    }
}

static void fbcon_cfb4_putc(u_char *cdat, u_char fc, u_char bc, int width, int height)
{
        u_char *dest, bits;
        int rows;
        u_int eorx,fgx,bgx;

        dest = gramMem + writeAddr;

        fgx = fc;
        bgx = bc;
        fgx |= (fgx << 4);
        fgx |= (fgx << 8);
        bgx |= (bgx << 4);
        bgx |= (bgx << 8);
        eorx = fgx ^ bgx;

        for (rows = height ; rows-- ; dest += dispInfo.glineByte) 
        {
             bits = *cdat++;
             ((u_short *)dest)[0]= (nibbletab_cfb4[bits >> 4] & eorx) ^ bgx;
             if (width < 8) continue; /* width = 4 */

             ((u_short *)dest)[1] = (nibbletab_cfb4[bits & 0xf] & eorx) ^ bgx;
             if (width < 12) continue; /* width = 8 */
 
             bits = *cdat++;
             ((u_short *)dest)[2]= (nibbletab_cfb4[bits >> 4] & eorx) ^ bgx;
             if (width < 16) continue; /* width = 12 */

             ((u_short *)dest)[3] = (nibbletab_cfb4[bits & 0xf] & eorx) ^ bgx;
             if (width < 20) continue; /* width = 16 */

             bits = *cdat++;
             ((u_short *)dest)[4]= (nibbletab_cfb4[bits >> 4] & eorx) ^ bgx;
             if (width < 24) continue; /* width = 20 */

             ((u_short *)dest)[5] = (nibbletab_cfb4[bits & 0xf] & eorx) ^ bgx;
             if (width < 28) continue; /* width = 24 */

			 bits = *cdat++;
             ((u_short *)dest)[6]= (nibbletab_cfb4[bits >> 4] & eorx) ^ bgx;
             if (width < 32) continue; /* width = 28 */

             ((u_short *)dest)[7] = (nibbletab_cfb4[bits & 0xf] & eorx) ^ bgx;
        }
}

static u_int nibbletab_cfb8[] = 
{
#if defined(CCE_TARGETARCH_BIGENDIAN)
    0x00000000,0x000000ff,0x0000ff00,0x0000ffff,
    0x00ff0000,0x00ff00ff,0x00ffff00,0x00ffffff,
    0xff000000,0xff0000ff,0xff00ff00,0xff00ffff,
    0xffff0000,0xffff00ff,0xffffff00,0xffffffff
#else
    0x00000000,0xff000000,0x00ff0000,0xffff0000,
    0x0000ff00,0xff00ff00,0x00ffff00,0xffffff00,
    0x000000ff,0xff0000ff,0x00ff00ff,0xffff00ff,
    0x0000ffff,0xff00ffff,0x00ffffff,0xffffffff
#endif
};

static void fbcon_cfb8_clear(int sy, int height, u_char bc)
{
    u_char *dest,*p;
    int i;

    dest = gramMem + sy * dispInfo.glineByte;

    while( height-- > 0)
    {
       p = dest;
       for(i = 0; i < dispInfo.gxdim; i++)
       {
          *p++ = bc;
       }
       dest += dispInfo.glineByte;
    }
}

static void fbcon_cfb8_putc(u_char *cdat, u_char fc, u_char bc, int width, int height)
{
    u_char *dest, bits;
    int rows;
    u_int eorx,fgx,bgx;

    dest = gramMem + writeAddr;

    fgx = fc;
    bgx = bc;
    fgx |= (fgx << 8);
    fgx |= (fgx << 16);
    bgx |= (bgx << 8);
    bgx |= (bgx << 16);
    eorx = fgx ^ bgx;

    for (rows = height ; rows-- ; dest += dispInfo.glineByte)
    {
         bits = *cdat++;
         ((u_int *)dest)[0]= (nibbletab_cfb8[bits >> 4] & eorx) ^ bgx;
         if (width < 8) continue; /* width = 4 */

         ((u_int *)dest)[1]= (nibbletab_cfb8[bits & 0xf] & eorx) ^ bgx;
         if (width < 12) continue; /* width = 8 */
         
         bits = *cdat++;
         ((u_int *)dest)[2]= (nibbletab_cfb8[bits >> 4] & eorx) ^ bgx;
         if (width < 16) continue; /* width = 12 */

         ((u_int *)dest)[3]= (nibbletab_cfb8[bits & 0xf] & eorx) ^ bgx;
         if (width < 20) continue; /* width = 16 */

         bits = *cdat++;
         ((u_int *)dest)[4]= (nibbletab_cfb8[bits >> 4] & eorx) ^ bgx;
         if (width < 24) continue; /* width = 20 */

         ((u_int *)dest)[5]= (nibbletab_cfb8[bits & 0xf] & eorx) ^ bgx;
		 if (width < 28) continue; /* width = 24 */
		 
		 bits = *cdat++;
         ((u_int *)dest)[6]= (nibbletab_cfb8[bits >> 4] & eorx) ^ bgx;
         if (width < 32) continue; /* width = 28 */

         ((u_int *)dest)[7]= (nibbletab_cfb8[bits & 0xf] & eorx) ^ bgx;
    }
}

static u_int nibbletab_cfb16[] = 
{
#if defined(CCE_TARGETARCH_BIGENDIAN)
	0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff
#else
    0x00000000, 0xffff0000, 0x0000ffff, 0xffffffff
#endif
};

static void fbcon_cfb16_clear(int sy, int height, u_char bc)
{
    u_char *dest;
    u_short *p;
    int i;

#ifdef DEBUG
	fprintf(stderr, "\rgramMem=%p, sy=%d, lineByte=%d\r\n", gramMem, sy, dispInfo.glineByte);
#endif

    dest = gramMem + sy * dispInfo.glineByte;

    while( height-- > 0)
    {
       p = (u_short*)dest;
#ifdef DEBUG
#endif
       for(i = 0; i < dispInfo.gxdim; i++)
       {
          *p++ = fb_cmap.cfb16[bc];
       }
       dest += dispInfo.glineByte;
    }
#ifdef DEBUG
#endif
}

static void fbcon_cfb16_putc(u_char *cdat, u_char fc, u_char bc,int width, int height)
{
    u_char *dest, bits;
    int rows;
    u_int eorx, fgx, bgx;

    dest = gramMem + writeAddr;

    fgx = fb_cmap.cfb16[fc];
    bgx = fb_cmap.cfb16[bc];

    fgx |= (fgx << 16);
    bgx |= (bgx << 16);
    eorx = fgx ^ bgx;

    for (rows = height; rows--; dest += dispInfo.glineByte) 
    {
         bits = *cdat++;
         ((u_int *)dest)[0] = (nibbletab_cfb16[bits >> 6] & eorx) ^ bgx;
         ((u_int *)dest)[1] = (nibbletab_cfb16[bits >> 4 & 3] & eorx) ^ bgx;
         if (width < 8)  continue;  /* width = 4 */

         ((u_int *)dest)[2] = (nibbletab_cfb16[bits >> 2 & 3] & eorx) ^ bgx;
         ((u_int *)dest)[3] = (nibbletab_cfb16[bits & 3] & eorx) ^ bgx;
         if (width < 12) continue;  /* width = 8 */
 
         bits = *cdat++;
         ((u_int *)dest)[4] = (nibbletab_cfb16[bits >> 6] & eorx) ^ bgx;
         ((u_int *)dest)[5] = (nibbletab_cfb16[bits >> 4 & 3] & eorx) ^ bgx;
         if (width < 16) continue;  /* width = 12 */

         ((u_int *)dest)[6] = (nibbletab_cfb16[bits >> 2 & 3] & eorx) ^ bgx;
         ((u_int *)dest)[7] = (nibbletab_cfb16[bits & 3] & eorx) ^ bgx;
         if (width < 20) continue;  /* width = 16 */

         bits = *cdat++;
         ((u_int *)dest)[8] = (nibbletab_cfb16[bits >> 6] & eorx) ^ bgx;
         ((u_int *)dest)[9] = (nibbletab_cfb16[bits >> 4 & 3] & eorx) ^ bgx;
         if (width < 24) continue;  /* width = 20 */

         ((u_int *)dest)[10] = (nibbletab_cfb16[bits >> 2 & 3] & eorx) ^ bgx;
         ((u_int *)dest)[11] = (nibbletab_cfb16[bits & 3] & eorx) ^ bgx;
         if (width < 28) continue;  /* width = 24 */
		 
		 bits = *cdat++;
         ((u_int *)dest)[12] = (nibbletab_cfb16[bits >> 6] & eorx) ^ bgx;
         ((u_int *)dest)[13] = (nibbletab_cfb16[bits >> 4 & 3] & eorx) ^ bgx;
         if (width < 32) continue;  /* width = 28 */

         ((u_int *)dest)[14] = (nibbletab_cfb16[bits >> 2 & 3] & eorx) ^ bgx;
         ((u_int *)dest)[15] = (nibbletab_cfb16[bits & 3] & eorx) ^ bgx;
 
    }
}

static inline void store4pixels(u_int d1, u_int d2, u_int d3, u_int d4, u_int *dest)
{
#if defined(CCE_TARGETARCH_BIGENDIAN)
	*dest++ = (d1 << 8)  | (d2 >> 16);
	*dest++ = (d2 << 16) | (d3 >> 8);
	*dest++ = (d3 << 24) | d4;
#else
    *dest++ = d1         | (d2 << 24);
    *dest++ = (d2 >> 8)  | (d3 << 16);
    *dest++ = (d3 >> 16) | (d4 << 8);
#endif
}

static void fbcon_cfb24_clear(int sy, int height, u_char bc)
{
    u_char *dest,*p, red, green, blue;
    int i;

    dest = gramMem + sy * dispInfo.glineByte;

    red = red16[bc] >> 8;
    green = green16[bc] >> 8;
    blue = blue16[bc] >> 8;

    while( height-- > 0)
    {
       p = dest;
       for(i = 0; i < dispInfo.gxdim; i++)
       {
          *p++ = blue;
          *p++ = green;
          *p++ = red;
       }  
       dest += dispInfo.glineByte;
    }
}

static void fbcon_cfb24_putc(u_char *cdat, u_char fc, u_char bc, int width, int height)
{
    u_char *dest, bits;
    int rows;
    u_int eorx, fgx, bgx, d1, d2, d3, d4;

   if (cdat == NULL) return;

    dest = gramMem + writeAddr;

    fgx = fb_cmap.cfb24[fc];
    bgx = fb_cmap.cfb24[bc];
    eorx = fgx ^ bgx;

    for (rows = height; rows--; dest += dispInfo.glineByte) 
    {
        bits = *cdat++;
        d1 = (-(bits >> 7) & eorx) ^ bgx;
        d2 = (-(bits >> 6 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 5 & 1) & eorx) ^ bgx;
        d4 = (-(bits >> 4 & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)dest);
        if (width < 8) continue;  /* width = 4 */

        d1 = (-(bits >> 3 & 1) & eorx) ^ bgx;
        d2 = (-(bits >> 2 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 1 & 1) & eorx) ^ bgx;
        d4 = (-(bits & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+12));
        if (width < 12) continue; /* width = 8 */

        bits = *cdat++;
        d1 = (-(bits >> 7) & eorx) ^ bgx;
        d2 = (-(bits >> 6 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 5 & 1) & eorx) ^ bgx;
        d4 = (-(bits >> 4 & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+24));
        if (width < 16) continue;  /* width = 12 */

        d1 = (-(bits >> 3 & 1) & eorx) ^ bgx;
        d2 = (-(bits >> 2 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 1 & 1) & eorx) ^ bgx;
        d4 = (-(bits & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+36));
        if (width < 20) continue;  /* width = 16 */

        bits = *cdat++;
        d1 = (-(bits >> 7) & eorx) ^ bgx;
        d2 = (-(bits >> 6 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 5 & 1) & eorx) ^ bgx;
        d4 = (-(bits >> 4 & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+48));
        if (width < 24) continue;  /* width = 20 */

        d1 = (-(bits >> 3 & 1) & eorx) ^ bgx;
        d2 = (-(bits >> 2 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 1 & 1) & eorx) ^ bgx;
        d4 = (-(bits & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+60));
        if (width < 28) continue;   /* width = 24 */
		
		bits = *cdat++;
        d1 = (-(bits >> 7) & eorx) ^ bgx;
        d2 = (-(bits >> 6 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 5 & 1) & eorx) ^ bgx;
        d4 = (-(bits >> 4 & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+72));
        if (width < 32) continue;  /* width = 28 */

        d1 = (-(bits >> 3 & 1) & eorx) ^ bgx;
        d2 = (-(bits >> 2 & 1) & eorx) ^ bgx;
        d3 = (-(bits >> 1 & 1) & eorx) ^ bgx;
        d4 = (-(bits & 1) & eorx) ^ bgx;
        store4pixels(d1, d2, d3, d4, (u_int *)(dest+84));
 
    }
}

#define fb_writel(b,addr) (*(volatile unsigned int *) (addr) = (b))

static void fbcon_cfb32_clear(int sy, int height, u_char bc)
{
    u_char *dest; 
	u_int *p;
    int i;

    dest = gramMem + sy * dispInfo.glineByte;

    while( height-- > 0)
    {
       p = (u_int *)dest;
       for(i = 0; i < dispInfo.gxdim; i++)
       {

		  *p++ = fb_cmap.cfb32[bc];
       }
       dest += dispInfo.glineByte;
    }
}

static void fbcon_cfb32_putc(u_char *cdat, u_char fc, u_char bc, int width, int height)
{
    u_char *dest, bits;
    int rows;
    u_int eorx, fgx, bgx, *pt;

   if (cdat == NULL) return;

    dest = gramMem + writeAddr;

    fgx = fb_cmap.cfb32[fc];
    bgx = fb_cmap.cfb32[bc];
    eorx = fgx ^ bgx;

    for (rows = height; rows--; dest += dispInfo.glineByte)
    {
        bits = *cdat++;
		pt = (unsigned int *) dest;
    	fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++);
    	if (width < 8) continue; /* width = 4 */

    	fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits & 1) & eorx) ^ bgx, pt++);
    	if (width < 12) continue; /* width = 8 */
    
		bits = *cdat++;
    	fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++);
    	if (width < 16) continue; /* width = 12 */

    	fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++);
    	fb_writel((-(bits & 1) & eorx) ^ bgx, pt++);
		if (width < 20) continue;  /* width = 16 */

		bits = *cdat++;
        fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++);
        if (width < 24) continue; /* width = 20 */

        fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits & 1) & eorx) ^ bgx, pt++);
		if (width < 28) continue; /* width = 24 */

		bits = *cdat++;
        fb_writel((-(bits >> 7) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 6 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 5 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 4 & 1) & eorx) ^ bgx, pt++);
        if (width < 32) continue; /* width = 28 */

        fb_writel((-(bits >> 3 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 2 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits >> 1 & 1) & eorx) ^ bgx, pt++);
        fb_writel((-(bits & 1) & eorx) ^ bgx, pt++);
	}
}

void FBOpCursor(CursorInfo *ci, int xpos, int ypos)
{
    char       *gram;
    u_char      x;
    int         i;
    int bottom = ( cursorBtm + 1 <= dispInfo.glineChar ?
        cursorBtm + 1 : dispInfo.glineChar );

	gram = gramMem + ypos * dispInfo.glineByte + xpos * dispInfo.bpp/8;

    x = cursorTop;
    if (kanjiCursor && ci->kanji)
    {
        for (;x < bottom;x ++, gram += dispInfo.glineByte)
        {
            for( i = 0; i < fontwidth * dispInfo.bpp * 2 / 8; i++)
               if (dispInfo.bpp <= 8)  /* palette-based */
                   gram[i] ^= 0x07;   /* should we use 0x07 or 0x77 or 0xFF ? */
               else gram[i] ^= 0xFF;
        }
    }
    else
    {
        for (;x < bottom;x ++, gram += dispInfo.glineByte)
        {
            for( i = 0; i < fontwidth * dispInfo.bpp / 8; i++)
               if (dispInfo.bpp <= 8)
                    gram[i] ^= 0x07;
               else gram[i] ^= 0xFF;
        }
    }
}

void FBOpClear(int y, int height, int color)
{
switch(dispInfo.bpp)
    {
    case 4:
        fbcon_cfb4_clear(y, height, color);
        break;
    case 8:
#ifdef __Darwin__
		color = DarwinColorMap8[color & DARWIN_COLOR_MASK];
#endif
        fbcon_cfb8_clear(y, height, color);
        break;
    case 16:
        fbcon_cfb16_clear(y, height, color);
        break;
    case 24:
        fbcon_cfb24_clear(y, height, color);
        break;
    case 32:
        fbcon_cfb32_clear(y, height, color);
        break;
    }
}

void FBOpDraw(u_char *code, u_char fc, u_char bc, int width, int height)
{
    switch(dispInfo.bpp)
    {
    case 4:
        fbcon_cfb4_putc(code, fc, bc, width, height);
        break;
    case 8:
#ifdef __Darwin__
		fc = DarwinColorMap8[fc & DARWIN_COLOR_MASK];
		bc = DarwinColorMap8[bc & DARWIN_COLOR_MASK];
#endif
        fbcon_cfb8_putc(code, fc, bc, width, height);
        break;
    case 16:
        fbcon_cfb16_putc(code, fc, bc, width, height);
        break;
    case 24:
        fbcon_cfb24_putc(code, fc, bc, width, height);
        break;
    case 32:
        fbcon_cfb32_putc(code, fc,  bc, width, height);
        break;
    }
}

#endif
