blob: 8c996a7362807a4f7dc3338b5d2f5eccb5f5c823 [file] [log] [blame]
#include "VideoDriverPrivate.h"
#include "VideoDriverPrototypes.h"
#include "DriverQDCalls.h"
#include "QemuVga.h"
/* List of supported modes */
struct vMode {
UInt32 width;
UInt32 height;
};
static struct vMode vModes[] = {
{ 640, 480 },
{ 800, 600 },
{ 1024, 768 },
{ 1280, 1024 },
{ 1600, 1200 },
{ 1920, 1080 },
{ 1920, 1200 },
{ 0,0 }
};
static void VgaWriteB(UInt16 port, UInt8 val)
{
UInt8 *ptr;
ptr = (UInt8 *)((UInt32)GLOBAL.boardRegAddress + port + 0x400 - 0x3c0);
*ptr = val;
SynchronizeIO();
}
static UInt8 VgaReadB(UInt16 port)
{
UInt8 *ptr, val;
ptr = (UInt8 *)((UInt32)GLOBAL.boardRegAddress + port + 0x400 - 0x3c0);
val = *ptr;
SynchronizeIO();
return val;
}
static void DispiWriteW(UInt16 reg, UInt16 val)
{
UInt16 *ptr;
ptr = (UInt16 *)((UInt32)GLOBAL.boardRegAddress + (reg << 1) + 0x500);
*ptr = EndianSwap16Bit(val);
SynchronizeIO();
}
static UInt16 DispiReadW(UInt16 reg)
{
UInt16 *ptr, val;
ptr = (UInt16 *)((UInt32)GLOBAL.boardRegAddress + (reg << 1) + 0x500);
val = EndianSwap16Bit(*ptr);
SynchronizeIO();
return val;
}
static void ExtWriteL(UInt16 reg, UInt32 val)
{
UInt32 *ptr;
ptr = (UInt32 *)((UInt32)GLOBAL.boardRegAddress + (reg << 2) + 0x600);
*ptr = EndianSwap32Bit(val);
SynchronizeIO();
}
static UInt32 ExtReadL(UInt32 reg)
{
UInt32 *ptr, val;
ptr = (UInt32 *)((UInt32)GLOBAL.boardRegAddress + (reg << 2) + 0x600);
val = EndianSwap32Bit(*ptr);
SynchronizeIO();
return val;
}
OSStatus QemuVga_Init(void)
{
UInt16 id, i;
UInt32 mem, width, height, depth;
id = DispiReadW(VBE_DISPI_INDEX_ID);
mem = DispiReadW(VBE_DISPI_INDEX_VIDEO_MEMORY_64K);
mem <<= 16;
lprintf("DISPI_ID=%04x VMEM=%d Mb\n", id, mem >> 20);
if ((id & 0xfff0) != VBE_DISPI_ID0) {
lprintf("Unsupported ID !\n");
return controlErr;
}
if (mem > GLOBAL.boardFBMappedSize)
mem = GLOBAL.boardFBMappedSize;
GLOBAL.vramSize = mem;
// XXX Add endian control regs
width = DispiReadW(VBE_DISPI_INDEX_XRES);
height = DispiReadW(VBE_DISPI_INDEX_YRES);
depth = DispiReadW(VBE_DISPI_INDEX_BPP);
lprintf("Current setting: %dx%dx%d\n", width, height, depth);
GLOBAL.depth = GLOBAL.bootDepth = depth;
for (i = 0; vModes[i].width; i++) {
if (width == vModes[i].width && height == vModes[i].height)
break;
}
if (!vModes[i].width) {
lprintf("Not found in list ! using default.\n");
i = 0;
}
GLOBAL.curMode = GLOBAL.bootMode = i;
GLOBAL.numModes = sizeof(vModes) / sizeof(struct vMode) - 1;
return noErr;
}
static OSStatus VBLTimerProc(void *p1, void *p2);
static OSStatus ScheduleVBLTimer(void)
{
/* XXX HACK: Run timer at 20Hz */
AbsoluteTime target = AddDurationToAbsolute(50, UpTime());
return SetInterruptTimer(&target, VBLTimerProc, NULL, &GLOBAL.VBLTimerID);
}
static OSStatus VBLTimerProc(void *p1, void *p2)
{
GLOBAL.inInterrupt = 1;
/* This can be called before the service is ready */
if (GLOBAL.qdVBLInterrupt && GLOBAL.qdInterruptsEnable)
VSLDoInterruptService(GLOBAL.qdVBLInterrupt);
/* Reschedule */
ScheduleVBLTimer();
GLOBAL.inInterrupt = 0;
}
OSStatus QemuVga_Open(void)
{
lprintf("QemuVga v1.00\n");
GLOBAL.isOpen = true;
/* Schedule the timer now if timers are supported. They aren't on OS X
* in which case we must not create the VSL service, otherwise OS X will expect
* a VBL and fail to update the cursor when not getting one.
*/
GLOBAL.hasTimer = (ScheduleVBLTimer() == noErr);
GLOBAL.qdInterruptsEnable = GLOBAL.hasTimer;
/* Create VBL if timer works */
if (GLOBAL.hasTimer && !GLOBAL.qdVBLInterrupt)
VSLNewInterruptService(&GLOBAL.deviceEntry, kVBLInterruptServiceType, &GLOBAL.qdVBLInterrupt);
if (GLOBAL.hasTimer)
lprintf("Using timer to simulate VBL.\n");
else
lprintf("No timer service (OS X ?), VBL not registered.\n");
return noErr;
}
OSStatus QemuVga_Close(void)
{
lprintf("Closing Driver...\n");
GLOBAL.isOpen = false;
QemuVga_DisableInterrupts();
if (GLOBAL.qdVBLInterrupt)
VSLDisposeInterruptService( GLOBAL.qdVBLInterrupt );
GLOBAL.qdVBLInterrupt = NULL;
return noErr;
}
OSStatus QemuVga_Exit(void)
{
QemuVga_Close();
return noErr;
}
void QemuVga_EnableInterrupts(void)
{
GLOBAL.qdInterruptsEnable = true;
if (GLOBAL.hasTimer)
ScheduleVBLTimer();
}
void QemuVga_DisableInterrupts(void)
{
AbsoluteTime remaining;
GLOBAL.qdInterruptsEnable = false;
if (GLOBAL.hasTimer)
CancelTimer(GLOBAL.VBLTimerID, &remaining);
}
OSStatus QemuVga_SetColorEntry(UInt32 index, RGBColor *color)
{
//lprintf("SetColorEntry %d, %x %x %x\n", index, color->red, color->green, color->blue);
VgaWriteB(0x3c8, index);
VgaWriteB(0x3c9, color->red >> 8);
VgaWriteB(0x3c9, color->green >> 8);
VgaWriteB(0x3c9, color->blue >> 8);
return noErr;
}
OSStatus QemuVga_GetColorEntry(UInt32 index, RGBColor *color)
{
UInt32 r,g,b;
VgaWriteB(0x3c7, index);
r = VgaReadB(0x3c9);
g = VgaReadB(0x3c9);
b = VgaReadB(0x3c9);
color->red = (r << 8) | r;
color->green = (g << 8) | g;
color->blue = (b << 8) | b;
return noErr;
}
OSStatus QemuVga_GetModeInfo(UInt32 index, UInt32 *width, UInt32 *height)
{
if (index >= GLOBAL.numModes)
return paramErr;
if (width)
*width = vModes[index].width;
if (height)
*height = vModes[index].height;
return noErr;
}
OSStatus QemuVga_SetMode(UInt32 mode, UInt32 depth)
{
UInt32 width, height;
if (mode >= GLOBAL.numModes)
return paramErr;
width = vModes[mode].width;
height = vModes[mode].height;
lprintf("Set Mode: %dx%dx%d\n", width, height, depth);
DispiWriteW(VBE_DISPI_INDEX_ENABLE, 0);
DispiWriteW(VBE_DISPI_INDEX_BPP, depth);
DispiWriteW(VBE_DISPI_INDEX_XRES, width);
DispiWriteW(VBE_DISPI_INDEX_YRES, height);
DispiWriteW(VBE_DISPI_INDEX_BANK, 0);
DispiWriteW(VBE_DISPI_INDEX_VIRT_WIDTH, width);
DispiWriteW(VBE_DISPI_INDEX_VIRT_HEIGHT, height);
DispiWriteW(VBE_DISPI_INDEX_X_OFFSET, 0);
DispiWriteW(VBE_DISPI_INDEX_Y_OFFSET, 0);
DispiWriteW(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED | VBE_DISPI_8BIT_DAC);
GLOBAL.curMode = mode;
GLOBAL.depth = depth;
return noErr;
}