/* 	Copyright (c) 1992 NeXT Computer, Inc.  All rights reserved. 
 *
 * JAWS.m - Jaws display driver, ObjC implementation.
 *
 * HISTORY
 * 02 Sep 92	Joe Pasqua
 *      Created. 
 * 12 July 1993	Derek B Clegg
 *	Cleanup for external release.
 */

/* TO DO:
 *   Implement other modes. Perhaps a large grayscale mode.
 */

#import <string.h>
#import <driverkit/generalFuncs.h>
#import <driverkit/kernelDriver.h>
#import <driverkit/i386/ioPorts.h>
#import <driverkit/i386/directDevice.h>
#import "JAWS.h"
#import "JAWSModes.h"

static inline void
setValue(unsigned int address, unsigned int value)
{
    *(unsigned int *)address = value;
    IODelay(50);
}

@implementation JAWS

- (const char *)valueForStringKey:(const char *)key
{
    IOConfigTable *configTable;
    configTable = [[self deviceDescription] configTable];
    if (configTable == nil)
	return 0;
    return [configTable valueForStringKey:key];
}

- (BOOL)booleanForStringKey:(const char *)key withDefault:(BOOL)defaultValue
{
    const char *value;

    value = [self valueForStringKey:key];
    if (value == 0)
	return defaultValue;
    
    if (value[0] == 'Y' || value[0] == 'y') {
	if (value[1] == '\0'
	    || ((value[1] == 'E' || value[1] == 'e')
		&& (value[2] == 'S' || value[2] == 's')
		&& value[3] == '\0')) {
	    return YES;
	}
    } else if (value[0] == 'N' || value[0] == 'n') {
	if (value[1] == '\0'
	    || ((value[1] == 'O' || value[1] == 'o')
		&& value[2] == '\0')) {
	    return NO;
	}
    }
    IOLog("%s: Unrecognized value for key `%s': `%s'.\n",
	  [self name], key, value);
    return defaultValue;
}

/* Default gamma precompensation table for color displays.
 * Gamma 2.2 LUT for P22 phosphor displays (Hitachi, NEC, generic VGA).
 */

static const unsigned char gamma_table[16] = {
      0,  74, 102, 123, 140, 155, 168, 180,
    192, 202, 212, 221, 230, 239, 247, 255,
};

/* Load the JAWS palette and scale the values according to the brightness
 * levels entered.  A 16 step table is used.  The value for each step is
 * loaded into the pixel addressable RAM location, and the 15 following,
 * not normally addressed locations, to prevent problems from vendor PWB
 * changes and noise (specifically ground bounce).
 */
- (void)setJawsPalette:(int)level
{
    unsigned int g, k, v, palette;

    palette = (unsigned int)JAWS_COL_PAL;

    for (g = 0; g < 16; g++) {
	v = EV_SCALE_BRIGHTNESS(level, gamma_table[g]);
    	for (k = 0; k < 16; k++) {
	    setValue(palette, (v << 16) | (v << 8) | v);
	    palette += sizeof(unsigned int);
	}
    }
}

/* Initialize the guts of the Jaws card.
 */
- (void)initializeG332
{
    unsigned int cra_reg_value;

    /* Boot location. */

#if JAWS_VIDEO_MW == 1024
    setValue(JAWS_BOOT_REG, CLK_SRC_PLL | CLK_PLL_MUL_9);
#elif JAWS_VIDEO_MW == 1120
    setValue(JAWS_BOOT_REG, CLK_SRC_PLL | CLK_PLL_MUL_C);
#elif JAWS_VIDEO_MW == 1152
    setValue(JAWS_BOOT_REG, CLK_SRC_PLL | CLK_PLL_MUL_B);
#endif

    /* Control Register */

    cra_reg_value = (CRA_VTG_DIS | CRA_NON_INT | CRA_FMT_CCI | CRA_MDE_MST
		     | CRA_CBL_INP | CRA_BLK_PED | CRA_CLK_PAD | CRA_BLK_ENA
		     | CRA_1KB_1KB | CRA_DMA_DIS | CRA_SYN_000 | CRA_PPI_INT
		     | CRA_SMP_DIS | CRA_NON_008 | CRA_CRS_DIS);

    if (useCompositeSync) {
	cra_reg_value |= (CRA_SYN_PLN | CRA_SYN_CMP | CRA_FMT_C_S);
    } else {
	cra_reg_value |= (CRA_SYN_PLN | CRA_SYN_SEP | CRA_FMT_VID);
    }
    setValue(JAWS_CRA_REG, cra_reg_value);

#if JAWS_VIDEO_MW == 1024
    /* Various VTG registers - values determined empirically. */
    setValue(JAWS_VTG_HALF_SYNC, 	0x00000011);
    setValue(JAWS_VTG_BACK_PORCH, 	0x00000024);
    setValue(JAWS_VTG_DISPLAY, 		0x00000100);
    setValue(JAWS_VTG_SHORTDISPLAY, 	0x0000005B);
    setValue(JAWS_VTG_BROADPAUSE, 	0x000000A1);
    setValue(JAWS_VTG_VSYNC, 		0x0000000C);
    setValue(JAWS_VTG_VPREEQUALISE, 	0x0000000C);
    setValue(JAWS_VTG_VPOSTEQUALISE, 	0x0000000C);
    setValue(JAWS_VTG_VBLANK, 		0x00000028);
    setValue(JAWS_VTG_VDISPLAY, 	0x00000600);
    setValue(JAWS_VTG_LINETIME, 	0x0000014A);
    setValue(JAWS_VTG_LINESTART, 	0x00000000);
    setValue(JAWS_VTG_MEMINIT, 		0x000003E8);
    setValue(JAWS_VTG_TRANSFERDELAY, 	0x00000018);

#elif JAWS_VIDEO_MW == 1120
    /* Various VTG registers - values determined empirically. */
    setValue(JAWS_VTG_HALF_SYNC, 	0x00000010);
    setValue(JAWS_VTG_BACK_PORCH, 	0x00000047);
    setValue(JAWS_VTG_DISPLAY, 		0x00000118);
    setValue(JAWS_VTG_SHORTDISPLAY, 	0x0000004E);
    setValue(JAWS_VTG_BROADPAUSE, 	0x000000B5);
    setValue(JAWS_VTG_VSYNC, 		0x00000010);
    setValue(JAWS_VTG_VPREEQUALISE, 	0x00000010);
    setValue(JAWS_VTG_VPOSTEQUALISE, 	0x00000010);
    setValue(JAWS_VTG_VBLANK, 		0x00000050);
    setValue(JAWS_VTG_VDISPLAY, 	0x00000680);
    setValue(JAWS_VTG_LINETIME, 	0x00000194);
    setValue(JAWS_VTG_LINESTART, 	0x00000000);
    setValue(JAWS_VTG_MEMINIT, 		0x000001E8);
    setValue(JAWS_VTG_TRANSFERDELAY, 	0x00000018);

#elif JAWS_VIDEO_MW == 1152
    /* Various VTG registers - values determined empirically. */
    setValue(JAWS_VTG_HALF_SYNC, 	0x0000000D);
    setValue(JAWS_VTG_BACK_PORCH, 	0x0000002E);
    setValue(JAWS_VTG_DISPLAY, 		0x00000120);
    setValue(JAWS_VTG_SHORTDISPLAY, 	0x00000069);
    setValue(JAWS_VTG_BROADPAUSE, 	0x000000B1);
    setValue(JAWS_VTG_VSYNC, 		0x00000006);
    setValue(JAWS_VTG_VPREEQUALISE, 	0x00000006);
    setValue(JAWS_VTG_VPOSTEQUALISE, 	0x00000006);
    setValue(JAWS_VTG_VBLANK, 		0x00000040);
    setValue(JAWS_VTG_VDISPLAY, 	0x00000708);
    setValue(JAWS_VTG_LINETIME, 	0x0000016E);
    setValue(JAWS_VTG_LINESTART, 	0x00000000);
    setValue(JAWS_VTG_MEMINIT, 		0x000001E0);
    setValue(JAWS_VTG_TRANSFERDELAY, 	0x00000020);
#endif

    /* Mask register. */
    setValue(JAWS_MASK_REG, 0x00FFFFFF);

    /* Top of Screen. */
    setValue(JAWS_TOS_REG, 0x00000000);

    /* CRA reg again. */
    cra_reg_value = (CRA_VTG_ENA | CRA_NON_INT | CRA_FMT_CCI | CRA_MDE_MST
		     | CRA_CBL_INP | CRA_CLK_PAD | CRA_BLK_PED | CRA_BLK_ENA
		     | CRA_1KB_1KB | CRA_DMA_ENA | CRA_SYN_000 | CRA_PPI_INT
		     | CRA_SMP_DEL | CRA_NON_016 | CRA_CRS_DIS);

    if (useCompositeSync) {
	cra_reg_value |= (CRA_SYN_PLN | CRA_SYN_CMP | CRA_FMT_C_S);
    } else {
	cra_reg_value |= (CRA_SYN_PLN | CRA_SYN_SEP | CRA_FMT_VID);
    }
    setValue(JAWS_CRA_REG, cra_reg_value);

    [self setJawsPalette:EV_SCREEN_MAX_BRIGHTNESS];
}	

static inline void
setJawsCtl(jaws_ctl_reg_t reg)
{
    union {
	jaws_ctl_reg_t reg;
	unsigned char iodata;
    } v;
    v.reg = reg;
    outb(JAWS_CTL_REG_IOADDR, v.iodata);
}

- (void)enableJawsMode
{
    jaws_ctl_reg_t ctl;

    ctl.phys = JAWS_CTL_PHYS_512M;
    ctl.cache = 1;
    ctl.ienable = 0;
    ctl.JAWSvga = 1;
    ctl.bbp12 = 1;

    ctl.reset = 1;
    setJawsCtl(ctl);
    
    IODelay(100);
    
    ctl.reset = 0;
    setJawsCtl(ctl);

    /* Initialize the video controller. */
    [self initializeG332];
}

- (void)revertToVGAMode
{
    jaws_ctl_reg_t ctl;

    ctl.phys = 0;
    ctl.cache = 0;
    ctl.ienable = 0;
    ctl.retrace = 0;
    ctl.JAWSvga = 0;
    ctl.bbp12 = 0;

    ctl.reset = 1;
    setJawsCtl(ctl);
    
    IODelay(100);
    
    ctl.reset = 0;
    setJawsCtl(ctl);
}

- (void)enterLinearMode
{
    IODisplayInfo *display;
    [self enableJawsMode];
    display = [self displayInfo];
    memset(display->frameBuffer, 0, display->height * display->rowBytes);
}

- (BOOL)JAWSPresent
{
    int slot;
    unsigned int cardID;

    if (![self isEISAPresent])
	return FALSE;

    for (slot = 1; slot < 16; slot++)
	if ([self getEISAId:&cardID forSlot:slot] && cardID == JAWS_EISA_ID)
	    return TRUE;
    return FALSE;
}

- initFromDeviceDescription:deviceDescription
{
    IODisplayInfo *display;

    if (![self JAWSPresent])
	return [super free];

    if ([super initFromDeviceDescription:deviceDescription] == nil)
	return [super free];

    useCompositeSync = [self booleanForStringKey:"UseCompositeSync"
		    withDefault:NO];

    display = [self displayInfo];
    display->frameBuffer = 
	(void *)[self mapFrameBufferAtPhysicalAddress:0 length:0];
    if (display->frameBuffer == 0)
        return [super free];

    baseAddress = (vm_offset_t)display->frameBuffer;

    display->width = JAWS_VIDEO_W;
    display->height = JAWS_VIDEO_H;
    display->totalWidth = JAWS_VIDEO_MW;
    display->rowBytes = JAWS_VIDEO_NBPL;
    display->bitsPerPixel = IO_12BitsPerPixel;
    strcpy(display->pixelEncoding, "RRRRGGGGBBBB----");
    display->flags = 0;
    return self;
}

/* This is from the `IOScreenEvents' protocol. We override our superclass
 * for this since it doesn't know how to set our brightness.
 */

- setBrightness:(int)level token:(int)t
{
    if (level < EV_SCREEN_MIN_BRIGHTNESS || level > EV_SCREEN_MAX_BRIGHTNESS) {
	IOLog("%s: Invalid argument to setBrightness: %d\n", [self name],
	      level);
	return nil;
    }
    [self setJawsPalette:level];
    return self;
}
@end
