/*
 * Copyright © 1999 Keith Packard
 *
 * 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.
 */
/* $XFree86: xc/programs/Xserver/hw/tinyx/sis530/sis.c,v 1.3 2006/02/20 00:23:09 tsi Exp $ */
/*
 * Copyright (c) 2004 by The XFree86 Project, Inc.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject
 * to the following conditions:
 *
 *   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, and in the same place and form as other copyright,
 *       license and disclaimer information.
 *
 *   3.  The end-user documentation included with the redistribution,
 *       if any, must include the following acknowledgment: "This product
 *       includes software developed by The XFree86 Project, Inc
 *       (http://www.xfree86.org/) and its contributors", in the same
 *       place and form as other third-party acknowledgments.  Alternately,
 *       this acknowledgment may appear in the software itself, in the
 *       same form and location as other such third-party acknowledgments.
 *
 *   4.  Except as contained in this notice, the name of The XFree86
 *       Project, Inc shall not be used in advertising or otherwise to
 *       promote the sale, use or other dealings in this Software without
 *       prior written authorization from The XFree86 Project, Inc.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED 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 XFREE86 PROJECT, INC OR ITS CONTRIBUTORS 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.
 */

#include "sis.h"

#define MAX_FB_SIZE	(4096 * 1024)

#define MMIO_SIZE	(64 * 1024)

int sisMemoryTable[8] = {
    1, 2, 4, 0, 0, 2, 4, 8
};

Bool
sisCardInit (KdCardInfo *card)
{
    SisCardInfo	    *sisc;
    CARD8	    save_sr5;

    sisc = (SisCardInfo *) xalloc (sizeof (SisCardInfo));
    if (!sisc)
	goto bail0;
    
    sisc->io_base = card->attr.io;
    /*
     * enable access to SiS ports (no MMIO available) 
     */
    iopl(3);
    save_sr5 = GetSrtc(sisc,0x5);
    if (save_sr5 != 0x21)
	save_sr5 = 0x86;
    PutSrtc(sisc,0x5,0x86);
#if 0
    {
	int	i;

	for (i = 0; i <= 0x3f; i++)
	    fprintf (stderr, "SR%02x = %02x\n", i, GetSrtc(sisc,i));
    }
#endif
    sisc->memory = sisMemoryTable[GetSrtc(sisc,0xc)&0x7] * 1024 * 1024;

    PutSrtc(sisc,0x5,save_sr5);
    
    if (!sisc->memory)
    {
	ErrorF ("Can't detect SiS530 frame buffer\n");
	goto bail1;
    }

    /*
     * Map frame buffer and MMIO registers
     */
    sisc->frameBuffer = KdMapDevice (card->attr.address[0], sisc->memory);
    if (!sisc->frameBuffer)
	goto bail1;
    
    sisc->registers = KdMapDevice (card->attr.address[1], MMIO_SIZE);
    if (!sisc->registers)
	goto bail2;
    
    /*
     * Offset from base of MMIO to registers
     */
    sisc->sis = (SisPtr) (sisc->registers + SIS_MMIO_OFFSET);
    sisc->cpu_bitblt = (VOL32 *) sisc->registers;

    card->driver = sisc;
    
    return TRUE;
bail2:
    KdUnmapDevice (sisc->frameBuffer, sisc->memory);
bail1:
    xfree (sisc);
bail0:
    return FALSE;
}

static Bool
sisModeSupported (KdScreenInfo *screen, const KdMonitorTiming *t)
{
    if (t->horizontal != 1600 &&
	t->horizontal != 1280 &&
	t->horizontal != 1152 &&
	t->horizontal != 1024 &&
	t->horizontal != 800 &&
	t->horizontal != 640)
	return FALSE;
    return TRUE;
}

static Bool
sisModeUsable (KdScreenInfo *screen)
{
    KdCardInfo	    *card = screen->card;
    SisCardInfo	    *sisc = (SisCardInfo *) card->driver;
    int		    byte_width, pixel_width, screen_size;
    
    if (screen->fb[0].depth >= 24)
    {
	screen->fb[0].depth = 24;
	screen->fb[0].bitsPerPixel = 24;
	screen->dumb = TRUE;
    }
    else if (screen->fb[0].depth >= 16)
    {
	screen->fb[0].depth = 16;
	screen->fb[0].bitsPerPixel = 16;
    }
    else if (screen->fb[0].depth >= 15)
    {
	screen->fb[0].depth = 15;
	screen->fb[0].bitsPerPixel = 16;
    }
    else
    {
	screen->fb[0].depth = 8;
	screen->fb[0].bitsPerPixel = 8;
    }
    byte_width = screen->width * (screen->fb[0].bitsPerPixel >> 3);
    pixel_width = screen->width;
    screen->fb[0].pixelStride = pixel_width;
    screen->fb[0].byteStride = byte_width;

    screen_size = byte_width * screen->height;

    return screen_size <= sisc->memory;
}

Bool
sisScreenInit (KdScreenInfo *screen)
{
    KdCardInfo	    *card = screen->card;
    SisCardInfo	    *sisc = (SisCardInfo *) card->driver;
    SisScreenInfo   *siss;
    const KdMonitorTiming *t;
    CARD32	    memory;
    int		    byte_width;
    int		    screen_size;

    siss = (SisScreenInfo *) xalloc (sizeof (SisScreenInfo));
    if (!siss)
	return FALSE;

    memset (siss, '\0', sizeof (SisScreenInfo));

    if (!screen->width || !screen->height)
    {
	screen->width = 800;
	screen->height = 600;
	screen->rate = 72;
    }
    if (!screen->fb[0].depth)
	screen->fb[0].depth = 8;
    
    t = KdFindMode (screen, sisModeSupported);
    
    screen->rate = t->rate;
    screen->width = t->horizontal;
    screen->height = t->vertical;
    byte_width = screen->width * (screen->fb[0].bitsPerPixel >> 3);
    screen_size = byte_width * screen->height;
    
    if (!KdTuneMode (screen, sisModeUsable, sisModeSupported))
    {
	xfree (sisc);
	return FALSE;
    }

    memory = sisc->memory - screen_size;
    
    screen->fb[0].frameBuffer = sisc->frameBuffer;
    
    /*
     * Cursor lives in the last 16k of memory
     */
    if (memory >= 16384 && !screen->softCursor)
    {
	siss->cursor_base = sisc->frameBuffer + (sisc->memory - 16384);
	siss->cursor_off = siss->cursor_base - sisc->frameBuffer;
	memory -= 16384;
    }
    else
    {
	screen->softCursor = TRUE;
	siss->cursor_base = 0;
	siss->cursor_off = 0;
    }

    if (memory > 8192)
    {
	siss->expand = screen->fb[0].frameBuffer + screen_size;
	siss->expand_off = siss->expand - sisc->frameBuffer;
	siss->expand_len = memory;
	memory = 0;
    }
    else
    {
	siss->expand = 0;
	siss->expand_len = 0;
    }
    
    switch (screen->fb[0].depth) {
    case 8:
	screen->fb[0].visuals = ((1 << StaticGray) |
			   (1 << GrayScale) |
			   (1 << StaticColor) |
			   (1 << PseudoColor) |
			   (1 << TrueColor) |
			   (1 << DirectColor));
	screen->fb[0].blueMask  = 0x00;
	screen->fb[0].greenMask = 0x00;
	screen->fb[0].redMask   = 0x00;
	break;
    case 15:
	screen->fb[0].visuals = (1 << TrueColor);
	screen->fb[0].blueMask  = 0x001f;
	screen->fb[0].greenMask = 0x03e0;
	screen->fb[0].redMask   = 0x7c00;
	break;
    case 16:
	screen->fb[0].visuals = (1 << TrueColor);
	screen->fb[0].blueMask  = 0x001f;
	screen->fb[0].greenMask = 0x07e0;
	screen->fb[0].redMask   = 0xf800;
	break;
    case 24:
	screen->fb[0].visuals = (1 << TrueColor);
	screen->fb[0].blueMask  = 0x0000ff;
	screen->fb[0].greenMask = 0x00ff00;
	screen->fb[0].redMask   = 0xff0000;
	break;
    }
    
    screen->driver = siss;
    
    return TRUE;
}

static void
_sisGetCrtc (SisCardInfo *sisc, SisCrtc *crtc)
{
    crtc->misc_output			= _sisInb(sisc->io_base+0x4c);
    crtc->h_total_0_7			= GetCrtc (sisc, 0x00);
    crtc->h_display_end_0_7		= GetCrtc (sisc, 0x01);
    crtc->h_blank_start_0_7		= GetCrtc (sisc, 0x02);
    crtc->_h_blank_end			= GetCrtc (sisc, 0x03);
    crtc->h_sync_start_0_7		= GetCrtc (sisc, 0x04);
    crtc->_h_sync_end			= GetCrtc (sisc, 0x05);
    crtc->v_total_0_7			= GetCrtc (sisc, 0x06);
    crtc->crtc_overflow			= GetCrtc (sisc, 0x07);
    crtc->preset_row_scan		= GetCrtc (sisc, 0x08);
    crtc->_max_scan_line		= GetCrtc (sisc, 0x09);
    crtc->cursor_start			= GetCrtc (sisc, 0x0a);
    crtc->cursor_end			= GetCrtc (sisc, 0x0a);
    crtc->start_address_8_15		= GetCrtc (sisc, 0x0c);
    crtc->start_address_0_7		= GetCrtc (sisc, 0x0d);
    crtc->text_cursor_15_8		= GetCrtc (sisc, 0x0e);
    crtc->text_cursor_7_0		= GetCrtc (sisc, 0x0f);
    crtc->v_retrace_start_0_7		= GetCrtc (sisc, 0x10);
    crtc->_v_retrace_end		= GetCrtc (sisc, 0x11);
    crtc->v_display_end_0_7		= GetCrtc (sisc, 0x12);
    crtc->screen_off_0_7	    	= GetCrtc (sisc, 0x13);
    crtc->_underline_location		= GetCrtc (sisc, 0x14);
    crtc->v_blank_start_0_7		= GetCrtc (sisc, 0x15);
    crtc->v_blank_end_0_7		= GetCrtc (sisc, 0x16);
    crtc->crtc_mode			= GetCrtc (sisc, 0x17);
    
    crtc->line_compare_0_7		= GetCrtc (sisc, 0x18);
    
    crtc->mode_control			= GetArtc (sisc, 0x10);
    crtc->screen_border_color		= GetArtc (sisc, 0x11);
    crtc->enable_color_plane		= GetArtc (sisc, 0x12);
    crtc->horizontal_pixel_pan		= GetArtc (sisc, 0x13);

    crtc->mode_register			= GetGrtc (sisc, 0x5);
    crtc->misc_register			= GetGrtc (sisc, 0x6);
    crtc->color_dont_care		= GetGrtc (sisc, 0x7);
    
    crtc->clock_mode			= GetSrtc (sisc, 0x1);
    crtc->color_plane_w_enable		= GetSrtc (sisc, 0x2);
    crtc->memory_mode			= GetSrtc (sisc, 0x4);
    
    crtc->graphics_mode			= GetSrtc (sisc, 0x6);
    crtc->misc_control_0    		= GetSrtc (sisc, 0x7);
    crtc->crt_cpu_threshold_control_0	= GetSrtc (sisc, 0x8);
    crtc->crt_cpu_threshold_control_1	= GetSrtc (sisc, 0x9);
    crtc->extended_crt_overflow		= GetSrtc (sisc, 0xa);
    crtc->misc_control_1    		= GetSrtc (sisc, 0xb);
    crtc->misc_control_2    		= GetSrtc (sisc, 0xc);
    
    crtc->ddc_and_power_control		= GetSrtc (sisc, 0x11);
    crtc->extended_horizontal_overflow	= GetSrtc (sisc, 0x12);
    crtc->extended_clock_generator    	= GetSrtc (sisc, 0x13);
    crtc->cursor_0_red			= GetSrtc (sisc, 0x14);
    crtc->cursor_0_green    		= GetSrtc (sisc, 0x15);
    crtc->cursor_0_blue			= GetSrtc (sisc, 0x16);
    crtc->cursor_1_red			= GetSrtc (sisc, 0x17);
    crtc->cursor_1_green    		= GetSrtc (sisc, 0x18);
    crtc->cursor_1_blue			= GetSrtc (sisc, 0x19);
    crtc->cursor_h_start_0_7		= GetSrtc (sisc, 0x1a);
    crtc->cursor_h_start_1    		= GetSrtc (sisc, 0x1b);
    crtc->cursor_h_preset_0_5    	= GetSrtc (sisc, 0x1c);
    crtc->cursor_v_start_0_7		= GetSrtc (sisc, 0x1d);
    crtc->cursor_v_start_1    		= GetSrtc (sisc, 0x1e);
    crtc->cursor_v_preset_0_5 		= GetSrtc (sisc, 0x1f);
    crtc->linear_base_19_26		= GetSrtc (sisc, 0x20);
    crtc->linear_base_1			= GetSrtc (sisc, 0x21);

    crtc->graphics_engine_0		= GetSrtc (sisc, 0x26);
    crtc->graphics_engine_1		= GetSrtc (sisc, 0x27);
    crtc->internal_mclk_0		= GetSrtc (sisc, 0x28);
    crtc->internal_mclk_1		= GetSrtc (sisc, 0x29);
    crtc->internal_vclk_0		= GetSrtc (sisc, 0x2A);
    crtc->internal_vclk_1		= GetSrtc (sisc, 0x2B);

    crtc->misc_control_7		= GetSrtc (sisc, 0x38);
    
    crtc->misc_control_11		= GetSrtc (sisc, 0x3E);
    crtc->misc_control_12		= GetSrtc (sisc, 0x3F);
}

static void
_sisSetBlank (SisCardInfo *sisc, Bool blank)
{
    CARD8   clock;
    
    clock = GetSrtc (sisc, 0x01);
    if (blank)
	clock |= 0x20;
    else
	clock &= ~0x20;
    PutSrtc (sisc, 0x01, clock);
}

static void
_sisSetCrtc (SisCardInfo *sisc, SisCrtc *crtc)
{
    _sisSetBlank (sisc, TRUE);
    PutCrtc (sisc, 0x00, crtc->h_total_0_7);
    PutCrtc (sisc, 0x01, crtc->h_display_end_0_7);
    PutCrtc (sisc, 0x02, crtc->h_blank_start_0_7);
    PutCrtc (sisc, 0x03, crtc->_h_blank_end);
    PutCrtc (sisc, 0x04, crtc->h_sync_start_0_7);
    PutCrtc (sisc, 0x05, crtc->_h_sync_end);
    PutCrtc (sisc, 0x06, crtc->v_total_0_7);
    PutCrtc (sisc, 0x07, crtc->crtc_overflow);
    PutCrtc (sisc, 0x08, crtc->preset_row_scan);
    PutCrtc (sisc, 0x09, crtc->_max_scan_line);
    PutCrtc (sisc, 0x0a, crtc->cursor_start);
    PutCrtc (sisc, 0x0b, crtc->cursor_end);
    PutCrtc (sisc, 0x0c, crtc->start_address_8_15);
    PutCrtc (sisc, 0x0d, crtc->start_address_0_7);
    PutCrtc (sisc, 0x0e, crtc->text_cursor_15_8);
    PutCrtc (sisc, 0x0f, crtc->text_cursor_7_0);
    PutCrtc (sisc, 0x10, crtc->v_retrace_start_0_7);
    PutCrtc (sisc, 0x11, crtc->_v_retrace_end);
    PutCrtc (sisc, 0x12, crtc->v_display_end_0_7);
    PutCrtc (sisc, 0x13, crtc->screen_off_0_7);
    PutCrtc (sisc, 0x14, crtc->_underline_location);
    PutCrtc (sisc, 0x15, crtc->v_blank_start_0_7);
    PutCrtc (sisc, 0x16, crtc->v_blank_end_0_7);
    PutCrtc (sisc, 0x17, crtc->crtc_mode);
    PutCrtc (sisc, 0x18, crtc->line_compare_0_7);
    
    PutArtc (sisc, 0x10, crtc->mode_control);
    PutArtc (sisc, 0x11, crtc->screen_border_color);
    PutArtc (sisc, 0x12, crtc->enable_color_plane);
    PutArtc (sisc, 0x13, crtc->horizontal_pixel_pan);

    PutGrtc (sisc, 0x5, crtc->mode_register);
    PutGrtc (sisc, 0x6, crtc->misc_register);
    PutGrtc (sisc, 0x7, crtc->color_dont_care);
    
    PutSrtc (sisc, 0x1, crtc->clock_mode | 0x20);
    PutSrtc (sisc, 0x2, crtc->color_plane_w_enable);
    PutSrtc (sisc, 0x4, crtc->memory_mode);
    
    PutSrtc (sisc, 0x6, crtc->graphics_mode);
    PutSrtc (sisc, 0x7, crtc->misc_control_0);
    PutSrtc (sisc, 0x8, crtc->crt_cpu_threshold_control_0);
    PutSrtc (sisc, 0x9, crtc->crt_cpu_threshold_control_1);
    PutSrtc (sisc, 0xa, crtc->extended_crt_overflow);
    PutSrtc (sisc, 0xb, crtc->misc_control_1);
    PutSrtc (sisc, 0xc, crtc->misc_control_2);
    
    PutSrtc (sisc, 0x11, crtc->ddc_and_power_control);
    PutSrtc (sisc, 0x12, crtc->extended_horizontal_overflow);
    PutSrtc (sisc, 0x13, crtc->extended_clock_generator);
    PutSrtc (sisc, 0x14, crtc->cursor_0_red);
    PutSrtc (sisc, 0x15, crtc->cursor_0_green);
    PutSrtc (sisc, 0x16, crtc->cursor_0_blue);
    PutSrtc (sisc, 0x17, crtc->cursor_1_red);
    PutSrtc (sisc, 0x18, crtc->cursor_1_green);
    PutSrtc (sisc, 0x19, crtc->cursor_1_blue);
    PutSrtc (sisc, 0x1a, crtc->cursor_h_start_0_7);
    PutSrtc (sisc, 0x1b, crtc->cursor_h_start_1);
    PutSrtc (sisc, 0x1c, crtc->cursor_h_preset_0_5);
    PutSrtc (sisc, 0x1d, crtc->cursor_v_start_0_7);
    PutSrtc (sisc, 0x1e, crtc->cursor_v_start_1);
    PutSrtc (sisc, 0x1f, crtc->cursor_v_preset_0_5);
    PutSrtc (sisc, 0x20, crtc->linear_base_19_26);
    PutSrtc (sisc, 0x21, crtc->linear_base_1);

    PutSrtc (sisc, 0x26, crtc->graphics_engine_0);
    PutSrtc (sisc, 0x27, crtc->graphics_engine_1);
    PutSrtc (sisc, 0x28, crtc->internal_mclk_0);
    PutSrtc (sisc, 0x29, crtc->internal_mclk_1);
    PutSrtc (sisc, 0x2A, crtc->internal_vclk_0);
    PutSrtc (sisc, 0x2B, crtc->internal_vclk_1);

    PutSrtc (sisc, 0x38, crtc->misc_control_7);
    
    PutSrtc (sisc, 0x3E, crtc->misc_control_11);
    PutSrtc (sisc, 0x3F, crtc->misc_control_12);
    
#if 0
    PutCrtc (sisc, 0x5b, 0x27);
    PutCrtc (sisc, 0x5c, 0xe1);
    PutCrtc (sisc, 0x5d, 0x00);

    PutSrtc (sisc, 0x5a, 0xe6);
    PutSrtc (sisc, 0x5d, 0xa1);
    PutSrtc (sisc, 0x9a, 0xe6);
    PutSrtc (sisc, 0x9d, 0xa1);
    PutSrtc (sisc, 0xda, 0xe6);
    PutSrtc (sisc, 0xdd, 0x6c);
#endif
    
    _sisOutb(crtc->misc_output, sisc->io_base+0x42);
    
    outw (0x3c4, 0x0100);
    outw (0x3c4, 0x0300);
    
    _sisSetBlank (sisc, FALSE);
}

CARD8
_sisReadIndexRegister (CARD32 base, CARD8 index)
{
    CARD8   ret;

    _sisOutb (index, base);
    ret = _sisInb (base+1);
    return ret;
}

void
_sisWriteIndexRegister (CARD32 base, CARD8 index, CARD8 value)
{
    _sisOutb (index, base);
    _sisOutb (value, base+1);
}

CARD8
_sisReadArtc (CARD32 base, CARD8 index)
{
    CARD8   ret;
    
    _sisInb (base+0x1a);
    _sisOutb (index,base);
    ret = _sisInb (base+1);
    _sisInb (base+0x1a);
    _sisOutb (0x20,base);
    return ret;
}

void
_sisWriteArtc (CARD32 base, CARD8 index, CARD8 value)
{
    _sisInb (base+0x1a);
    _sisOutb (index|0x20,base);
    _sisOutb (value,base);
    _sisInb (base+0x1a);
    _sisOutb (0x20,base);
}

static void
sisPreserve (KdCardInfo *card)
{
    SisCardInfo	*sisc = card->driver;
    
    sisc->save.sr5 = GetSrtc(sisc,0x5);
    if (sisc->save.sr5 != 0x21)
	sisc->save.sr5 = 0x86;
    /* unlock extension registers */
    PutSrtc(sisc,0x5,0x86);
    /* unlock CRTC registers */
    PutCrtc(sisc,0x11,GetCrtc(sisc,0x11)&~0x80);
    /* enable vga */
    _sisOutb(0x1,sisc->io_base+0x43);
    
    /* enable MMIO access to registers */
    sisc->save.srb = GetSrtc(sisc,0xb);
    PutSrtc(sisc, 0xb, sisc->save.srb | 0x60);
    _sisGetCrtc (sisc, &sisc->save.crtc);
    memcpy (sisc->save.text_save, sisc->frameBuffer, SIS_TEXT_SAVE);
}

Bool
sisEnable (ScreenPtr pScreen)
{
    KdScreenPriv(pScreen);
    KdScreenInfo    *screen = pScreenPriv->screen;
    KdCardInfo	    *card = pScreenPriv->card;
    SisCardInfo	    *sisc = card->driver;
    SisScreenInfo   *siss = screen->driver;
    const KdMonitorTiming *t;
    SisCrtc	    crtc;
    unsigned long   pixel;
    
    int	    hactive;
    int	    hblank;
    int	    hfp;
    int	    hbp;

    int	    vactive;
    int	    vblank;
    int	    vfp;
    int	    vbp;

    int	    h_total;
    int	    h_display_end;
    int	    h_blank_start;
    int	    h_blank_end;
    int	    h_sync_start;
    int	    h_sync_end;
    int	    h_screen_off;

    int	    h_adjust;

    int	    v_total;
    int	    v_retrace_start;
    int	    v_retrace_end;
    int	    v_display_end;
    int	    v_blank_start;
    int	    v_blank_end;

    crtc = sisc->save.crtc;
    
    t = KdFindMode (screen, sisModeSupported);
    
    /* CR9 */
    crtc.max_scan_line = 0;
    
    /* CRA */
    crtc.cursor_start = 0;

    /* CRB */
    crtc.cursor_end = 0;
    
    /* CRE */
    crtc.text_cursor_15_8 = 0;

    /* CRF */
    crtc.text_cursor_7_0 = 0;
    
    /* CR11 */
    crtc.disable_v_retrace_int = 1;
    
    /* CR14 */
    crtc.underline_location = 0;
    crtc.count_by_four = 0;
    crtc.doubleword_mode = 1;
    
    /* 3CC/3C2 */
    crtc.io_address_select = 1;
    crtc.display_ram_enable = 1;
    crtc.clock_select = 3;
    
    /* SR1 */
    crtc.clock_mode = 0;
    crtc.dot_clock_8_9 = 1;

    /* SR2 */
    crtc.color_plane_w_enable = 0xf;

    /* SR4 */
    crtc.memory_mode = 0;
    crtc.chain_4_enable = 1;
    crtc.odd_even_disable = 1;
    crtc.extended_memory_sz = 1;

    /* SR6 */
    crtc.graphics_mode_linear = 1;
    crtc.enhanced_graphics_mode = 1;
    
    /* SR9 */
    crtc.crt_cpu_threshold_control_1 = 0;

    /* SRB */
#if 0
    crtc.cpu_bitblt_enable = 1;
#endif
    crtc.memory_mapped_mode = 3;
    
    /* SRC */
    crtc.graphics_mode_32bit_enable = 1;
    crtc.read_ahead_enable = 1;
    
    /* SR11 */
    crtc.acpi_enable = 0;
    crtc.kbd_cursor_activate = 0;
    crtc.video_memory_activate = 0;
    crtc.vga_standby = 0;
    crtc.vga_suspend = 0;
    
    crtc.cursor_0_red = 0x3f;
    crtc.cursor_0_green = 0x3f;
    crtc.cursor_0_blue = 0x3f;

    /* SR20 */
    crtc.linear_base_19_26 = (card->attr.address[0] & 0x07f80000) >> 19;

    /* SR21 */
    crtc.linear_base_27_31 = (card->attr.address[0] & 0xf8000000) >> 27;
    crtc.linear_aperture   = SIS_LINEAR_APERTURE_4M;
     
    /* SR27 */
    crtc.logical_screen_width = 3;
    crtc.graphics_prog_enable = 1;
    
    /* SR38 */
    crtc.extended_clock_select = 0;
    
    /* AR10 */
    crtc.mode_control = 0;
    crtc.graphics_mode_enable = 1;
    /* AR11 */
    crtc.screen_border_color = 0;
    /* AR12 */
    crtc.enable_color_plane = 0xf;
    /* AR13 */
    crtc.horizontal_pixel_pan = 0;
    
    /* GR5 */
    crtc.mode_register = 0;

    /* GR6 */
    crtc.graphics_enable = 1;
    crtc.chain_odd_even = 0;
    crtc.memory_address_select = 1;

    /* GR7 */
    crtc.color_dont_care = 0xf;
    if (siss->cursor_base)
    {
	crtc_set_cursor_start_addr (&crtc, siss->cursor_off);
	crtc.graphics_mode_hw_cursor = 0;
    }
    
    hactive = t->horizontal;
    hblank = t->hblank;
    hbp = t->hbp;
    hfp = t->hfp;
    
    vactive = t->vertical;
    vblank = t->vblank;
    vbp = t->vbp;
    vfp = t->vfp;
    
    pixel = (hactive + hblank) * (vactive + vblank) * t->rate;
    
    switch (screen->fb[0].bitsPerPixel) {
    case 8:
	hactive /= 8;
	hblank /= 8;
	hfp /= 8;
	hbp /= 8;

	crtc.color_mode_256 = 1;
	h_screen_off = hactive;
	h_adjust = 1;
	
	break;
    case 16:
	hactive /= 8;
	hblank /= 8;
	hfp /= 8;
	hbp /= 8;

	h_screen_off = hactive * 2;
	h_adjust = 1;
	
	crtc.color_mode_256 = 0;
	
	if (screen->fb[0].depth == 15)
	    crtc.graphics_mode_32k = 1;
	else
	    crtc.graphics_mode_64k = 1;
	break;
    case 24:
	hactive /= 8;
	hblank /= 8;
	hfp /= 8;
	hbp /= 8;
	
	h_screen_off = hactive * 3;
	h_adjust = 1;

	crtc.color_mode_256 = 0;
	
	/* SR6 */
	crtc.graphics_mode_true = 1;
	/* SR7 */
	crtc.direct_color_24bit = 0;
	/* SR9 */
	crtc.true_color_32bpp = 0;
	/* SRB */
	crtc.true_color_order = 1;
	break;
    case 32:
	hactive /= 8;
	hblank /= 8;
	hfp /= 8;
	hbp /= 8;
	
	h_screen_off = hactive * 4;
	h_adjust = 1;

	crtc.color_mode_256 = 0;
	
	/* SR6 */
	crtc.graphics_mode_true = 1;
	/* SR7 */
	crtc.direct_color_24bit = 0;
	/* SR9 */
	crtc.true_color_32bpp = 1;
	/* SRB */
	crtc.true_color_order = 1;
	break;
    default:
	FatalError("sisEnable: unsupported bpp: %d\n",
		   screen->fb[0].bitsPerPixel);
    }
	
    sisGetClock (pixel, &crtc);
    
    crtc.high_speed_dac_0 = crtc.high_speed_dac_1 = pixel > 135000000;
    
    sisEngThresh (&crtc, pixel, screen->fb[0].bitsPerPixel);
    
    /*
     * Compute horizontal register values from timings
     */
    h_total = hactive + hblank - 5;
    h_display_end = hactive - 1;
    h_blank_start = h_display_end;
    h_blank_end = h_blank_start + hblank;
    
    h_sync_start = hactive + hfp + h_adjust;
    h_sync_end = h_sync_start + hblank - hbp - hfp;

    crtc_set_h_total(&crtc, h_total);
    crtc_set_h_display_end (&crtc, h_display_end);
    crtc_set_h_blank_start (&crtc, h_blank_start);
    crtc_set_h_blank_end (&crtc, h_blank_end);
    crtc_set_h_sync_start (&crtc, h_sync_start);
    crtc_set_h_sync_end (&crtc, h_sync_end);
    crtc_set_screen_off (&crtc, h_screen_off);

    v_total = vactive + vblank - 2;
    v_retrace_start = vactive + vfp - 1;
    v_retrace_end = v_retrace_start + vblank - vbp - vfp;
    v_display_end = vactive - 1;
    v_blank_start = vactive - 1;
    v_blank_end = v_blank_start + vblank /* - 1 */;
    
    crtc_set_v_total(&crtc, v_total);
    crtc_set_v_retrace_start (&crtc, v_retrace_start);
    crtc.v_retrace_end_0_3 = v_retrace_end;
    crtc_set_v_display_end (&crtc, v_display_end);
    crtc_set_v_blank_start (&crtc, v_blank_start);
    crtc.v_blank_end_0_7 = v_blank_end;
    
#if 0
    crtc.h_blank_start_0_7 = 0x6a;
    crtc._h_blank_end = 0x9a;
    crtc.h_sync_start_0_7 = 0x6b;
    crtc._h_sync_end = 0x9a;
    
    crtc.v_retrace_start_0_7 = 0x7d;
    crtc._v_retrace_end = 0x23;
    crtc.v_blank_start_0_7 = 0x7d;
    crtc.v_blank_end_0_7 = 0x84;

    crtc.crt_cpu_threshold_control_0 = 0xdf;	/* SR8 */
    crtc.crt_cpu_threshold_control_1 = 0x00;	/* SR9 */
    crtc.extended_clock_generator = 0x40;	/* SR13 */

    crtc.cursor_h_start_0_7 = 0x83;
    crtc.cursor_v_start_0_7 = 0x6c;

    crtc.internal_vclk_0 = 0x68;
    crtc.internal_vclk_1 = 0xc4;
    crtc.misc_control_7 = 0x70;
#endif
    
    _sisSetCrtc (sisc, &crtc);
    return TRUE;
}

static Bool
sisDPMS (ScreenPtr pScreen, int mode)
{
    KdScreenPriv(pScreen);
    sisCardInfo(pScreenPriv);
    union ddc_and_power_control_u   _ddc_and_power_control_u;

    ddc_and_power_control = sisc->save.crtc.ddc_and_power_control;

    kbd_cursor_activate = 0;
    video_memory_activate = 0;
    vga_standby = 0;
    vga_suspend = 0;
    acpi_enable = 0;
    switch (mode) {
    case KD_DPMS_NORMAL:
	break;
    case KD_DPMS_STANDBY:
	vga_standby = 1;
	break;
    case KD_DPMS_SUSPEND:
	vga_suspend = 1;
	break;
    case KD_DPMS_POWERDOWN:
	acpi_enable = 1;
	break;
    }
    PutSrtc (sisc, 0x11, ddc_and_power_control);
    return TRUE;
}

void
sisDisable (ScreenPtr pScreen)
{
}

static void
sisRestore (KdCardInfo *card)
{
    SisCardInfo	*sisc = (SisCardInfo *) card->driver;
    
    memcpy (sisc->frameBuffer, sisc->save.text_save, SIS_TEXT_SAVE);
    _sisSetCrtc (sisc, &sisc->save.crtc);
    PutSrtc (sisc, 0xb, sisc->save.srb);
    PutSrtc (sisc, 0x5, sisc->save.sr5);
}

static void
sisScreenFini (KdScreenInfo *screen)
{
    SisScreenInfo   *siss = (SisScreenInfo *) screen->driver;
    
    xfree (siss);
    screen->driver = 0;
}

static void
sisCardFini (KdCardInfo *card)
{
    SisCardInfo	*sisc = (SisCardInfo *) card->driver;
    
    KdUnmapDevice (sisc->frameBuffer, sisc->memory);
    KdUnmapDevice (sisc->registers, sizeof (SisRec));
}

KdCardFuncs	sisFuncs = {
    sisCardInit,
    sisScreenInit,
    0,
    sisPreserve,
    sisEnable,
    sisDPMS,
    sisDisable,
    sisRestore,
    sisScreenFini,
    sisCardFini,
    sisCursorInit,
    sisCursorEnable,
    sisCursorDisable,
    sisCursorFini,
    0,
    sisDrawInit,
    sisDrawEnable,
    sisDrawSync,
    sisDrawDisable,
    sisDrawFini,
    sisGetColors,
    sisPutColors,
};
