/* Copyright (c) 1993 by NeXT Computer, Inc as an unpublished work.
 * All rights reserved.
 *
 * QVisionSetMode.m -- Mode support for the QVision.
 *
 * Author:  Derek B Clegg	21 May 1993
 * Based on work by Joe Pasqua.
 */
#import <string.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/i386/ioPorts.h>
#import <driverkit/i386/directDevice.h>
#import "QVision.h"

/* The `SetMode' category of `QVision'. */

@implementation QVision (SetMode)

- (void)reportConfiguration
{
    const char *adapterString, *dacString;

    switch (adapter) {
    case QVisionAdapter: adapterString = "QVision"; break;
    case OrionAdapter: adapterString = "Orion"; break;
    case Orion12Adapter: adapterString = "Orion12"; break;
    default: adapterString = "unknown"; break;
    }

    switch (dac) {
    case Bt484: dacString = "Brooktree 484"; break;
    case Bt485: dacString = "Brooktree 485"; break;
    case Bt485A: dacString = "Brooktree 485A"; break;
    default: dacString = "unknown"; break;
    }

    IOLog("%s: %s adapter; %s DAC.\n", [self name], adapterString, dacString);
}

- determineConfiguration
{
    int slot;
    unsigned int cardID;

    adapter = UnknownAdapter;

    if ([self isEISAPresent] == TRUE) {
	for (slot = 1; slot < 16; slot++) {
	    if ([self getEISAId:&cardID forSlot:slot] == TRUE) {
		switch (cardID) {
		case QVISION_EISA_ID:
		    adapter = QVisionAdapter;
		    break;
		case ORION_EISA_ID:
		    adapter = OrionAdapter;
		    break;
		case ORION12_EISA_ID:
		    adapter = Orion12Adapter;
		    break;
		case QVISION_ISA_ID:
		case ORION_ISA_ID:
		case ORION12_ISA_ID:
		    IOLog("%s: Sorry, ISA cards are not supported.\n",
			  [self name]);
		    break;
		default:
		    /* We found some other EISA card.  Just ignore it. */
		    continue;
		}
		break;
	    }
	}
    }
    [self determineDACType];
    [self reportConfiguration];
    return self;
}

- selectMode
{
    int k, mode;
    const QVisionMode *qvMode;
    BOOL validModes[QVisionModeTableCount];

    for (k = 0; k < QVisionModeTableCount; k++) {
	qvMode = QVisionModeTable[k].parameters;
	validModes[k] = (qvMode->adapter <= adapter);
    }

    mode = [self selectMode:QVisionModeTable count:QVisionModeTableCount
	valid:validModes];

    if (mode < 0) {
	IOLog("%s: Sorry, cannot use requested display mode.\n", [self name]);
	switch (adapter) {
	case Orion12Adapter:
	    mode = DEFAULT_ORION12_MODE;
	    break;
	case OrionAdapter:
	    mode = DEFAULT_ORION_MODE;
	    break;
	case QVisionAdapter:
	default:
	    mode = DEFAULT_QVISION_MODE;
	    break;
	}
    }
    *[self displayInfo] = QVisionModeTable[mode];
    return self;
}

- initializeMode
{
    unsigned int i;
    const QVisionMode *mode;
    const IODisplayInfo *displayInfo;

    displayInfo = [self displayInfo];
    mode = displayInfo->parameters;

    /* Turn off video while setting all of the registers. */
    inb(VGA_INPUT_STATUS_1);
    outb(VGA_ATTR_INDEX, 0x00);
    
    /* Set the sequencer registers. */
    for (i = 0; i < VGA_SEQ_COUNT; i++) {
	outb(VGA_SEQ_INDEX, i);
	outb(VGA_SEQ_DATA, mode->vgaData.seqx[i]);
    }
    outb(VGA_SEQ_INDEX, 0x00);
    outb(VGA_SEQ_DATA, 0x03);	/* Restart the sequencer. */
    
    /* Unlock extended graphics registers. */
    outw(VGA_GRFX_INDEX, 0x050f);

    /* Unlock more extended registers. */
    outb(VGA_GRFX_INDEX, 0x10);
    outb(VGA_GRFX_DATA, 0x08);

    /* Set Advanced VGA mode (set bit 0 of Ctrl Reg 0). */
    outb(VGA_GRFX_INDEX, 0x40);
    outb(VGA_GRFX_DATA, 0x01);
    
    /* Fix sequencer pixel mask for 8 bits. */
    outb(VGA_SEQ_INDEX, SEQ_PIXEL_WR_MSK);
    outb(VGA_SEQ_DATA, 0xff);
    
    outb(CTRL_REG_1, mode->ctrlReg1);
    if (mode->adapter >= OrionAdapter) {
	/* Set access level & enable high address map. */
	outb(QVGA_CTL_2, 0x14);
	/* Select 2 Meg mode. */
	outb(QVGA_CTL_3, 0x05);
    }
    
    /* Set miscellaneous output register. */
    outb(VGA_MISC_OUTPUT, mode->vgaData.miscOutput);
    
    [self programDAC];

    /* Load CRTC registers. */
    outb(VGA_CRTC_INDEX, 0x11);		/* Unlock CRTC regs 0-7. */
    outb(VGA_CRTC_DATA, 0x00);
    for (i = 0; i < VGA_CRTC_COUNT; i++) {
	outb(VGA_CRTC_INDEX, i);
	outb(VGA_CRTC_DATA, mode->vgaData.crtc[i]);
    }

    /* Load overflow registers. */
    outb(VGA_GRFX_INDEX, 0x42);
    outb(VGA_GRFX_DATA, mode->overflow1);
    outb(VGA_GRFX_INDEX, 0x51);
    outb(VGA_GRFX_DATA, mode->overflow2);
    
    /* Load attribute registers. */

    inb(VGA_INPUT_STATUS_1);	/* Reset latch. */
    for (i = 0; i < VGA_ATTR_COUNT; i++) {
	outb(VGA_ATTR_INDEX, i);
	outb(VGA_ATTR_DATA, mode->vgaData.attr[i]);
    }
    
    /* Load graphics registers. */
    for (i = 0; i < VGA_GRFX_COUNT; i++) {
	outb(VGA_GRFX_INDEX, i);
	outb(VGA_GRFX_DATA, mode->vgaData.grfx[i]);
    }
    
    [self setGammaTable];
    
    /* Re-enable video display. */
    inb(VGA_INPUT_STATUS_1);
    outb(VGA_ATTR_INDEX, 0x20);

    return self;
}

- enableLinearFrameBuffer
{
    const IODisplayInfo *displayInfo;
    unsigned char tmp;
    
    /* Override the high address map disable, thus allowing access to
     * the high address map of the current board, even when the board
     * is disabled. */

    outb(VGA_GRFX_INDEX, HI_ADDR_MAP+1);
    tmp = inb(VGA_GRFX_DATA);
    outb(VGA_GRFX_DATA, tmp | 0x80);

    /* Map VRAM.  Tell the adapter where to decode the framebuffer. */

    /* Set low 8 bits */
    outb(VGA_GRFX_INDEX, HI_ADDR_MAP);
    outb(VGA_GRFX_DATA, (videoRamAddress >> 20) & 0xFF);

    /* Set upper 4 bits */
    outb(VGA_GRFX_INDEX, HI_ADDR_MAP + 1);
    outb(VGA_GRFX_DATA, (videoRamAddress >> 28) & 0x0F);

    /* Leave them with a nice clear screen. */

    displayInfo = [self displayInfo];
    memset(displayInfo->frameBuffer, 0, 
	   displayInfo->rowBytes * displayInfo->height);

    return self;
}

- resetVGA
{
    const IODisplayInfo *displayInfo;
    const QVisionMode *mode;

    displayInfo = [self displayInfo];
    mode = displayInfo->parameters;

    /* Clear the QVision extended mode bit. This is bit 0 of CTRL_REG_1. */

    outb(CTRL_REG_1, inb(CTRL_REG_1) & 0xFE);

    if (mode != 0 && mode->adapter >= OrionAdapter) {
	/* Select 1 meg mode. */
	outb(QVGA_CTL_3, 0x00);
	/* Reset access level & disable high address map. */
	outb(QVGA_CTL_2, 0x00);
    }
    
    /* Clear the extended 256 color bit. This is bit 0 of 3CF.40. */
    outb(VGA_GRFX_INDEX, 0x40);
    outb(VGA_GRFX_DATA, (inb(VGA_GRFX_DATA) & 0xFE));
    
    /* Clear the page registers - 3CF.45 and 3CF.46. */
    outb(VGA_GRFX_INDEX, PAGE_REG_0);
    outb(VGA_GRFX_DATA, 0x00);
    outb(VGA_GRFX_INDEX, PAGE_REG_1);
    outb(VGA_GRFX_DATA, 0x00);
    
    [self resetDAC];

    /* Clear the overflow registers. */
    outb(VGA_GRFX_INDEX, 0x42);
    outb(VGA_GRFX_DATA, 0x00);
    outb(VGA_GRFX_INDEX, 0x51);
    outb(VGA_GRFX_DATA, 0x00);

    VGASetMode(0x03);

    return self;
}
@end
