/*
 * $XFree86: xc/programs/Xserver/hw/kdrive/vesa/vga.c,v 1.2 2001/09/05 07:12:43 keithp Exp $
 *
 * Copyright © 2000 Keith Packard, member of The XFree86 Project, Inc.
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Keith Packard not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  Keith Packard makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */

#include "vesa.h"

static const VesaModeRec vgaModes[] = {
    {
	6,  0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_LINEAR,
	1, 1, MEMORY_PLANAR, 
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	640, 200, 80,
    },
    {
	0xd, 0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_COLOUR,
	4, 4, MEMORY_PLANAR, 
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	320, 200, 40,
    },
    {
	0xe, 0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_COLOUR,
	4, 4, MEMORY_PLANAR, 
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	640, 200, 80,
    },
    {
	0x10, 0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_COLOUR,
	4, 4, MEMORY_PLANAR, 
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	640, 350, 80,
    },
    {
	0x11, 0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_LINEAR,
	1, 1, MEMORY_PLANAR, 
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	640, 480, 80,
    },
    {
	0x12, 0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_COLOUR,
	4, 4, MEMORY_PLANAR, 
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	640, 480, 80,
    },
    {
	0x13, 0,
	MODE_SUPPORTED | MODE_GRAPHICS | MODE_VGA | MODE_COLOUR | MODE_LINEAR,
	8, 8, MEMORY_PSEUDO,
	0, 0, 0, 0, 0, 0, 0, 0, 0,
	320, 200, 320,
    },
};

#define NUM_VGA_MODE	(sizeof vgaModes / sizeof vgaModes[0])

int
VgaGetNmode (Vm86InfoPtr vi)
{
    return NUM_VGA_MODE;
}

int
VgaGetModes (Vm86InfoPtr vi, VesaModePtr mode, int nmode)
{
    if (nmode > NUM_VGA_MODE)
	nmode = NUM_VGA_MODE;
    memcpy (mode, vgaModes, nmode * sizeof (VesaModeRec));
    return nmode;
}

int
VgaSetMode(Vm86InfoPtr vi, int mode)
{
    int code;
    
    vi->vms.regs.eax = mode & 0x7f;
    code = Vm86DoInterrupt (vi, 0x10);
    if(code < 0)
        return -1;
    return 0;
}

int
VgaGetMode (Vm86InfoPtr vi, int *mode)
{
    *mode = Vm86Memory (vi, 0x449);
    return 0;
}

int
VgaSetWritePlaneMask(Vm86InfoPtr vi, int mask)
{
    asm volatile ("outb %b0,%w1" : : "a" (2), "d" (0x3c4));
    asm volatile ("outb %b0,%w1" : : "a" (mask), "d" (0x3c5));
}

int
VgaSetReadPlaneMap(Vm86InfoPtr vi, int map)
{
    asm volatile ("outb %b0,%w1" : : "a" (4), "d" (0x3ce));
    asm volatile ("outb %b0,%w1" : : "a" (map), "d" (0x3cf));
}

int 
VgaSetPalette(Vm86InfoPtr vi, int first, int number, U8 *entries)
{
    U8	    *palette_scratch;
    int	    mark;
    int	    palette_base;
    int	    i, j, code;

    if(number == 0)
        return 0;

    if(first < 0 || number < 0 || first + number > 256) {
        ErrorF("Cannot set %d, %d palette entries\n", first, number);
        return -1;
    }

    mark = Vm86MarkMemory (vi);
    palette_base = Vm86AllocateMemory (vi, 3 * 256);
    
    palette_scratch = &LM(vi, palette_base);

    vi->vms.regs.eax = 0x1012;
    vi->vms.regs.ebx = first;
    vi->vms.regs.ecx = number;
    vi->vms.regs.es = POINTER_SEGMENT(palette_base);
    vi->vms.regs.edx = POINTER_OFFSET(palette_base);
    j = 0;
    i = 0;
    while (number--)
    {
	palette_scratch[j++] = entries[i++] >> 2;
	palette_scratch[j++] = entries[i++] >> 2;
	palette_scratch[j++] = entries[i++] >> 2;
	i++;
    }
    code = Vm86DoInterrupt(vi, 0x10);
    Vm86ReleaseMemory (vi, mark);
    
    if(code < 0)
	return -1;
    return 0;
}
        
int 
VgaGetPalette(Vm86InfoPtr vi, int first, int number, U8 *entries)
{
    U8	    *palette_scratch;
    int	    mark;
    int	    palette_base;
    int	    i, j, code;

    if(number == 0)
        return 0;

    if(first < 0 || number < 0 || first + number > 256) {
        ErrorF("Cannot get %d, %d palette entries\n", first, number);
        return -1;
    }

    mark = Vm86MarkMemory (vi);
    palette_base = Vm86AllocateMemory (vi, 3 * 256);
    
    palette_scratch = &LM(vi, palette_base);

    vi->vms.regs.eax = 0x1017;
    vi->vms.regs.ebx = first;
    vi->vms.regs.ecx = number;
    vi->vms.regs.es = POINTER_SEGMENT(palette_base);
    vi->vms.regs.edx = POINTER_OFFSET(palette_base);
    
    code = VbeDoInterrupt10(vi);
    if(code < 0)
	return -1;
    
    j = 0;
    i = 0;
    while (number--)
    {
	entries[i++] = palette_scratch[j++] << 2;
	entries[i++] = palette_scratch[j++] << 2;
	entries[i++] = palette_scratch[j++] << 2;
	entries[i++] = 0;
    }
    
    Vm86ReleaseMemory (vi, mark);

    return 0;
}   
        
#define VGA_FB(vm)  ((vm) < 8 ? 0xb8000 : 0xa0000)

void *
VgaSetWindow (Vm86InfoPtr vi, int vmode, int bytes, int mode, int *size)
{
    *size = 0x10000 - bytes;
    return &LM(vi,VGA_FB(vmode) + bytes);
}

void *
VgaMapFramebuffer (Vm86InfoPtr vi, int vmode, int *size, CARD32 *ret_phys)
{
    if (VGA_FB(vmode) == 0xa0000)
	*size = 0x10000;
    else
	*size = 0x4000;
    *ret_phys = VGA_FB(vmode);
    return &LM(vi,VGA_FB(vmode));
}

void
VgaUnmapFramebuffer (Vm86InfoPtr vi)
{
}
