diff options
Diffstat (limited to 'hw/kdrive/savage/s3.c')
-rw-r--r-- | hw/kdrive/savage/s3.c | 997 |
1 files changed, 997 insertions, 0 deletions
diff --git a/hw/kdrive/savage/s3.c b/hw/kdrive/savage/s3.c new file mode 100644 index 000000000..bf3e2896a --- /dev/null +++ b/hw/kdrive/savage/s3.c @@ -0,0 +1,997 @@ +/* + * $Id$ + * + * Copyright 1999 SuSE, 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 SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * 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. + * + * Author: Keith Packard, SuSE, Inc. + */ +/* $XFree86: $ */ + +#include "s3.h" + +#define REGISTERS_OFFSET (0x1000000) +#define PACKED_OFFSET (0x8100) +#define IOMAP_OFFSET (0x8000) + +/* + * Clock synthesis: + * + * f_out = f_ref * ((M + 2) / ((N + 2) * (1 << R))) + * + * Constraints: + * + * 1. 135MHz <= f_ref * ((M + 2) / (N + 2)) <= 270 MHz + * 2. N >= 1 + * + * Vertical refresh rate = clock / ((hsize + hblank) * (vsize + vblank)) + * Horizontal refresh rate = clock / (hsize + hblank) + */ + +#define DEFAULT_S3_TIMING 1 + +S3Timing s3Timings[] = { + /* FP BP BLANK */ + /* M N R blank bios5 */ + { 640, 480, 60, + 16, 48, 160, /* horizontal 31.321 KHz */ + 10, 33, 45, /* vertical 59.568 Hz */ + 26, 0, 3, /* pixel 25.057 MHz */ + }, + + { 800, 600, 85, + 32, 152, 248, /* horizontal 53.673 KHz */ + 1, 27, 31, /* vertical 85.060 Hz */ + 108, 5, 2, /* pixel 56.249 MHz */ + }, + { 800, 600, 75, + 16, 160, 256, /* horizontal 46.891 KHz */ + 1, 21, 25, /* vertical 75.025 Hz */ + 81, 4, 2, /* pixel 49.516 MHz */ + }, + { 800, 600, 72, + 56, 64, 240, /* horizontal 48.186 KHz */ + 37, 23, 66, /* vertical 72.351 Hz */ + 26, 0, 2, /* pixel 50.113 MHz */ + }, + { 800, 600, 60, + 48, 80, 256, + 1, 23, 28, + 0, 0, 0, + }, + { 1024, 768, 85, + 48, 208, 352, /* horizontal 68.676 KHz */ + 1, 36, 40, /* vertical 84.996 Hz */ + 64, 3, 1, /* pixel 94.499 MHz */ + }, + { 1024, 768, 75, + 16, 176, 288, /* horizontal 60.022 KHz */ + 1, 28, 32, /* vertical 75.028 Hz */ + 20, 0, 1, /* pixel 78.749 MHz */ + }, + { 1024, 768, 70, + 32, 136, 304, /* horizontal 56.604 KHz */ + 2, 30, 38, /* vertical 70.227 Hz */ + 124, 1, 3, /* pixel 75.170 MHz */ + }, + { 1024, 768, 66, + 24, 144, 304, /* horizontal 53.234 KHz */ + 3, 29, 38, /* vertical 66.047 Hz */ + 77, 6, 1, /* pixel 70.695 MHz */ + }, + + { 1152, 900, 85, + 48, 208, 384, /* horizontal 79.900 KHz */ + 1, 32, 38, /* vertical 85.181 Hz */ + 118, 5, 1, /* pixel 122.726 MHz */ + }, + { 1152, 900, 75, + 32, 208, 384, /* horizontal 70.495 Khz */ + 1, 32, 38, /* vertical 75.154 Hz */ + 119, 6, 1, /* pixel 108.280 MHz */ + }, + { 1152, 900, 70, + 32, 208, 384, /* horizontal 65.251 KHz */ + 2, 32, 38, /* vertical 69.564 Hz */ + 12, 0, 0, /* pixel 100.226 MHz */ + }, + { 1152, 900, 66, + 32, 208, 384, /* horizontal 61.817 KHz */ + 1, 32, 38, /* vertical 65.903 Hz */ + 124, 17, 0, /* pixel 94.951 MHz */ + }, + { 1280, 1024, 85, + 32, 248, 416, /* horizontal 90.561 KHz */ + 1, 40, 45, /* vertical 84.717 Hz */ + 116, 9, 0, /* pixel 153.593 MHz */ + }, + { 1280, 1024, 75, + 16, 248, 408, /* horizontal 80.255 KHz */ + 1, 38, 42, /* vertical 75.285 Hz */ + 111, 4, 1, /* pixel 134.828 MHz */ + }, + { 1280, 1024, 70, + 32, 248, 400, /* horizontal 74.573 KHz */ + 0, 36, 39, /* vertical 70.153 Hz */ + 68, 2, 1, /* pixel 125.283 MHz */ + }, + { 1280, 1024, 66, + 32, 248, 400, /* horizontal 70.007 KHz */ + 0, 36, 39, /* vertical 65.858 Hz */ + 113, 5, 1, /* pixel 117.612 MHz */ + }, + { 1280, 1024, 60, + 56, 240, 408, /* horizontal 70.007 KHz */ + 1, 38, 42, /* vertical 65.858 Hz */ + 113, 5, 1, /* pixel 117.612 MHz */ + }, + { 1600, 1200, 85, + 64, 304, 560, /* horizontal 106.059 KHz */ + 1, 46, 50, /* vertical 84.847 Hz */ + 126, 6, 0, /* pixel 229.088 MHz */ + }, + { 1600, 1200, 75, + 64, 304, 560, /* horizontal 93.748 KHz */ + 1, 46, 50, /* vertical 74.999 Hz */ + 97, 5, 0, /* pixel 202.497 MHz */ + }, + { 1600, 1200, 70, + 56, 304, 588, /* horizontal 87.524 KHz */ + 1, 46, 50, /* vertical 70.019 Hz */ + 105, 6, 0, /* pixel 191.503 MHz */ + }, + { 1600, 1200, 65, + 56, 308, 524, /* horizontal 80.050 KHz */ + 1, 38, 42, /* vertical 64.453 Hz */ + 93, 6, 0, /* pixel 170.026 MHz */ + }, +}; + +#define NUM_S3_TIMINGS (sizeof (s3Timings) / sizeof (s3Timings[0])) + +static void +_s3SetBlank (S3Ptr s3, S3Vga *s3vga, Bool blank) +{ + CARD8 clock_mode; + + s3SetImm(s3vga, s3_screen_off, blank ? 1 : 0); +} + +static void +_s3SetDepth (S3Ptr s3, S3Vga *s3vga) +{ + CARD8 save_3c2; + _s3SetBlank (s3, s3vga, TRUE); + VgaFlush(&s3vga->card); + VgaSetImm (&s3vga->card, s3_clock_load_imm, 1); + VgaSetImm(&s3vga->card, s3_clock_load_imm, 0); + _s3SetBlank (s3, s3vga, FALSE); +} + +Bool +s3CardInit (KdCardInfo *card) +{ + S3CardInfo *s3c; + S3Ptr s3; + S3Vga *s3vga; + int size; + CARD8 *registers; + CARD32 s3FrameBuffer; + CARD32 s3Registers; + CARD8 *temp_buffer; + CARD32 max_memory; + VGA32 save_linear_window_size; + VGA32 save_enable_linear; + VGA32 save_register_lock_2; + + s3c = (S3CardInfo *) xalloc (sizeof (S3CardInfo)); + if (!s3c) + { + goto bail0; + } + + memset (s3c, '\0', sizeof (S3CardInfo)); + + card->driver = s3c; + + if (card->attr.naddr > 1) + { + s3FrameBuffer = card->attr.address[1]; + s3Registers = card->attr.address[0]; + max_memory = 32 * 1024 * 1024; + } + else + { + s3FrameBuffer = card->attr.address[0]; + s3Registers = s3FrameBuffer + REGISTERS_OFFSET; + max_memory = 16 * 1024 * 1024; + } + +#ifdef DEBUG + fprintf (stderr, "S3 at 0x%x/0x%x\n", s3Registers, s3FrameBuffer); +#endif + registers = KdMapDevice (s3Registers, + sizeof (S3) + PACKED_OFFSET); + if (!registers) + { + ErrorF ("Can't map s3 device\n"); + goto bail2; + } + s3 = (S3Ptr) (registers + PACKED_OFFSET); + s3c->registers = registers; + s3c->s3 = s3; + + s3vga = &s3c->s3vga; + s3RegInit (s3vga, (VGAVOL8 *) (registers + IOMAP_OFFSET)); + + save_register_lock_2 = s3Get (s3vga, s3_register_lock_2); + s3SetImm (s3vga, s3_register_lock_2, 0xa0); + save_linear_window_size = s3Get (s3vga, s3_linear_window_size); + save_enable_linear = s3Get (s3vga, s3_enable_linear); + s3Set (s3vga, s3_linear_window_size, 3); + s3Set (s3vga, s3_enable_linear, 1); + VgaFlush (&s3vga->card); + VgaFinish (&s3vga->card); + + /* + * Can't trust S3 register value for frame buffer amount, must compute + */ + temp_buffer = KdMapDevice (s3FrameBuffer, max_memory); + + s3c->memory = KdFrameBufferSize (temp_buffer, max_memory); + + s3Set (s3vga, s3_linear_window_size, save_linear_window_size); + s3Set (s3vga, s3_enable_linear, save_enable_linear); + VgaFlush (&s3vga->card); + s3SetImm (s3vga, s3_register_lock_2, save_register_lock_2); + VgaFinish (&s3vga->card); +#ifdef DEBUG + fprintf (stderr, "Frame buffer 0x%x\n", s3c->memory); +#endif + KdUnmapDevice (temp_buffer, max_memory); + + if (!s3c->memory) + { + ErrorF ("Can't detect s3 frame buffer at 0x%x\n", s3FrameBuffer); + goto bail3; + } + + s3c->frameBuffer = KdMapDevice (s3FrameBuffer, s3c->memory); + if (!s3c->frameBuffer) + { + ErrorF ("Can't map s3 frame buffer\n"); + goto bail3; + } + + card->driver = s3c; + + return TRUE; +bail3: + KdUnmapDevice ((void *) s3, sizeof (S3)); +bail2: +bail1: + xfree (s3c); +bail0: + return FALSE; +} + +Bool +s3ScreenInit (KdScreenInfo *screen) +{ + KdCardInfo *card = screen->card; + S3CardInfo *s3c = (S3CardInfo *) card->driver; + S3ScreenInfo *s3s; + int screen_size; + int memory; + int requested_memory; + int v_total, h_total; + int byte_width; + int pixel_width; + int m, n, r; + int i; + S3Timing *t; + + s3s = (S3ScreenInfo *) xalloc (sizeof (S3ScreenInfo)); + if (!s3s) + return FALSE; + + memset (s3s, '\0', sizeof (S3ScreenInfo)); + + if (!screen->width || !screen->height) + { + screen->width = 800; + screen->height = 600; + screen->rate = 72; + } + if (!screen->depth) + screen->depth = 8; + + for (i = 0, t = s3Timings; i < NUM_S3_TIMINGS; i++, t++) + { + if (t->horizontal >= screen->width && + t->vertical >= screen->height && + (!screen->rate || t->rate <= screen->rate)) + break; + } + if (i == NUM_S3_TIMINGS) + t = &s3Timings[DEFAULT_S3_TIMING]; + screen->rate = t->rate; + screen->width = t->horizontal; + screen->height = t->vertical; + s3GetClock (S3ModeClock(t), &m, &n, &r, 511, 127, 4); +#ifdef DEBUG + fprintf (stderr, "computed %d,%d,%d (%d) provided %d,%d,%d (%d)\n", + m, n, r, S3_CLOCK(m,n,r), + t->dac_m, t->dac_n, t->dac_r, + S3_CLOCK(t->dac_m, t->dac_n, t->dac_r)); +#endif + /* + * Can only operate in pixel-doubled mode at 8 bits per pixel + */ + if (screen->depth > 16 && S3_CLOCK(m,n,r) > S3_MAX_CLOCK) + screen->depth = 16; + + for (;;) + { + if (screen->depth >= 24) + { + screen->depth = 24; + screen->bitsPerPixel = 32; + } + else if (screen->depth >= 16) + { + screen->depth = 16; + screen->bitsPerPixel = 16; + } + else if (screen->depth >= 15) + { + screen->depth = 15; + screen->bitsPerPixel = 16; + } + else + { + screen->depth = 8; + screen->bitsPerPixel = 8; + } + + /* Normalize width to supported values */ + + if (screen->width >= 1600) + screen->width = 1600; + else if (screen->width >= 1280) + screen->width = 1280; + else if (screen->width >= 1152) + screen->width = 1152; + else if (screen->width >= 1024) + screen->width = 1024; + else if (screen->width >= 800) + screen->width = 800; + else + screen->width = 640; + + byte_width = screen->width * (screen->bitsPerPixel >> 3); + pixel_width = screen->width; + screen->pixelStride = pixel_width; + screen->byteStride = byte_width; + + screen_size = byte_width * screen->height; + + if (screen_size <= s3c->memory) + break; + + /* + * Fix requested depth and geometry until it works + */ + if (screen->depth > 16) + screen->depth = 16; + else if (screen->depth > 8) + screen->depth = 8; + else if (screen->width > 1152) + { + screen->width = 1152; + screen->height = 900; + } + else if (screen->width > 1024) + { + screen->width = 1024; + screen->height = 768; + } + else if (screen->width > 800) + { + screen->width = 800; + screen->height = 600; + } + else if (screen->width > 640) + { + screen->width = 640; + screen->height = 480; + } + else + { + xfree (s3s); + return FALSE; + } + } + + memory = s3c->memory - screen_size; + + /* + * Stick frame buffer at start of memory + */ + screen->frameBuffer = s3c->frameBuffer; + + /* + * Stick cursor at end of memory + */ + if (memory >= 2048) + { + s3s->cursor_base = s3c->frameBuffer + (s3c->memory - 2048); + memory -= 2048; + } + else + s3s->cursor_base = 0; + + /* + * Use remaining memory for off-screen storage, but only use + * one piece (either right or bottom). + */ + if (memory >= byte_width * S3_TILE_SIZE) + { + s3s->offscreen = s3c->frameBuffer + screen_size; + s3s->offscreen_x = 0; + s3s->offscreen_y = screen_size / byte_width; + s3s->offscreen_width = pixel_width; + s3s->offscreen_height = memory / byte_width; + memory -= s3s->offscreen_height * byte_width; + } + else if (pixel_width - screen->width >= S3_TILE_SIZE) + { + s3s->offscreen = s3c->frameBuffer + screen->width; + s3s->offscreen_x = screen->width; + s3s->offscreen_y = 0; + s3s->offscreen_width = pixel_width - screen->width; + s3s->offscreen_height = screen->height; + } + else + s3s->offscreen = 0; + + switch (screen->depth) { + case 8: + screen->visuals = ((1 << StaticGray) | + (1 << GrayScale) | + (1 << StaticColor) | + (1 << PseudoColor) | + (1 << TrueColor) | + (1 << DirectColor)); + screen->blueMask = 0x00; + screen->greenMask = 0x00; + screen->redMask = 0x00; + break; + case 15: + screen->visuals = (1 << TrueColor); + screen->blueMask = 0x001f; + screen->greenMask = 0x03e0; + screen->redMask = 0x7c00; + break; + case 16: + screen->visuals = (1 << TrueColor); + screen->blueMask = 0x001f; + screen->greenMask = 0x07e0; + screen->redMask = 0xf800; + break; + case 24: + screen->visuals = (1 << TrueColor); + screen->blueMask = 0x0000ff; + screen->greenMask = 0x00ff00; + screen->redMask = 0xff0000; + break; + } + + screen->driver = s3s; + + return TRUE; +} + +void +s3Preserve (KdCardInfo *card) +{ + S3CardInfo *s3c = card->driver; + S3Ptr s3 = s3c->s3; + S3Vga *s3vga = &s3c->s3vga; + S3Save *save = &s3c->save; + CARD8 t1, t2; + CARD8 *cursor_base; + + s3Save (s3vga); + _s3SetBlank (s3, s3vga, TRUE); + /* + * Preserve the first part of the frame buffer which holds + * the text mode fonts and data + */ + s3Set (s3vga, s3_linear_window_size, 3); + s3Set (s3vga, s3_enable_linear, 1); + VgaFlush (&s3vga->card); + memcpy (save->text_save, s3c->frameBuffer, S3_TEXT_SAVE); + /* + * Preserve graphics engine state + */ + save->alt_mix = s3->alt_mix; + save->write_mask = s3->write_mask; + save->fg = s3->fg; + save->bg = s3->bg; + _s3SetBlank (s3, s3vga, FALSE); +} + +/* + * Enable the card for rendering. Manipulate the initial settings + * of the card here. + */ +void +s3Enable (ScreenPtr pScreen) +{ + KdScreenPriv(pScreen); + KdCardInfo *card = pScreenPriv->card; + KdScreenInfo *screen = pScreenPriv->screen; + s3CardInfo (pScreenPriv); + s3ScreenInfo (pScreenPriv); + + S3Vga *s3vga = &s3c->s3vga; + int hactive, hblank, hfp, hbp; + int vactive, vblank, vfp, vbp; + int hsize; + + 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_start_fifo_fetch; + + int primary_stream_l1; + + int v_total; + int v_retrace_start; + int v_retrace_end; + int v_display_end; + int v_blank_start; + int v_blank_end; + + int h_blank_start_adjust; + int h_blank_end_adjust; + int h_sync_extend; + int h_blank_extend; + int i; + CARD16 cursor_address; + S3Timing *t; + int m, n, r; + Bool clock_double; + + for (i = 0; i < NUM_S3_TIMINGS; i++) + { + t = &s3Timings[i]; + + if (t->horizontal == screen->width && + t->vertical == screen->height && + t->rate <= screen->rate) + break; + } + if (i == NUM_S3_TIMINGS) + t = &s3Timings[DEFAULT_S3_TIMING]; + + hfp = t->hfp; + hbp = t->hbp; + hblank = t->hblank; + hactive = t->horizontal; + + vfp = t->vfp; + vbp = t->vbp; + vblank = t->vblank; + vactive = t->vertical; + + m = s3Get (s3vga, s3_dclk_m); + n = s3Get (s3vga, s3_dclk_n); + r = s3Get (s3vga, s3_dclk_r); +#ifdef DEBUG + fprintf (stderr, "old clock %d, %d, %d\n", m, n, r); +#endif + clock_double = FALSE; + s3GetClock (S3ModeClock(t), &m, &n, &r, 511, 127, 4); + if (S3_CLOCK(m,n,r) > S3_MAX_CLOCK) + clock_double = TRUE; + s3Set (s3vga, s3_clock_select, 3); + s3Set (s3vga, s3_dclk_m, m); + s3Set (s3vga, s3_dclk_n, n); + s3Set (s3vga, s3_dclk_r, r); +#ifdef DEBUG + fprintf (stderr, "new clock %d, %d, %d\n", m, n, r); +#endif + + s3Set (s3vga, s3_select_graphics_mode, 1); + s3Set (s3vga, s3_enable_blinking, 0); + s3Set (s3vga, s3_enable_vga_16bit, 0); + s3Set (s3vga, s3_enhanced_memory_mapping, 1); + s3Set (s3vga, s3_enable_sff, 1); + s3Set (s3vga, s3_enable_2d_access, 1); + s3Set (s3vga, s3_byte_mode, 1); + s3Set (s3vga, s3_max_scan_line, 0); + s3Set (s3vga, s3_linear_window_size, 3); + s3Set (s3vga, s3_enable_linear, 1); + s3Set (s3vga, s3_enable_2d_3d, 1); + s3Set (s3vga, s3_refresh_control, 1); + s3Set (s3vga, s3_disable_pci_read_bursts, 0); + s3Set (s3vga, s3_pci_retry_enable, 1); + s3Set (s3vga, s3_enable_256, 1); +#if 1 + s3Set (s3vga, s3_border_select, 1); /* eliminate white border */ +#else + s3Set (s3vga, s3_border_select, 0); /* eliminate white border */ +#endif + s3Set (s3vga, s3_disable_v_retrace_int, 1); + s3Set (s3vga, s3_horz_sync_neg, 0); + s3Set (s3vga, s3_vert_sync_neg, 0); + + s3Set (s3vga, s3_dot_clock_8, 1); + s3Set (s3vga, s3_enable_write_plane, 0xf); + s3Set (s3vga, s3_extended_memory_access, 1); + s3Set (s3vga, s3_sequential_addressing_mode, 1); + s3Set (s3vga, s3_select_chain_4_mode, 1); +#if 1 + s3Set (s3vga, s3_linear_addressing_control, 1); +#else + s3Set (s3vga, s3_linear_addressing_control, 0); +#endif + s3Set (s3vga, s3_enable_8_bit_luts, 1); + + s3Set (s3vga, s3_dclk_invert, 0); + s3Set (s3vga, s3_enable_clock_double, 0); + + s3Set (s3vga, s3_cpu_timeout, 0x1f); + s3Set (s3vga, s3_fifo_fetch_timing, 1); + s3Set (s3vga, s3_fifo_drain_delay, 7); + + + s3Set (s3vga, s3_delay_h_enable, 0); + s3Set (s3vga, s3_sdclk_skew, 0); + + /* + * Compute character lengths for horizontal timing values + */ + h_blank_start_adjust = 0; + h_blank_end_adjust = 0; + switch (screen->bitsPerPixel) { + case 8: + hactive = screen->width / 8; + hblank /= 8; + hfp /= 8; + hbp /= 8; + h_screen_off = hactive; + s3Set (s3vga, s3_2d_graphics_engine_timeout, 0x1f); + s3Set (s3vga, s3_pixel_length, 0); + s3Set (s3vga, s3_color_mode, 0); + /* + * Set up for double-pixel mode, switch color modes, + * divide the dclk and delay h blank by 2 dclks + */ + if (clock_double) + { + s3Set (s3vga, s3_color_mode, 1); + s3Set (s3vga, s3_dclk_over_2, 1); + s3Set (s3vga, s3_enable_clock_double, 1); + s3Set (s3vga, s3_border_select, 0); +#if 0 + s3Set (s3vga, s3_delay_blank, 2); + s3Set (s3vga, s3_delay_h_enable, 2); + crtc->extended_bios_5 = 2; +#endif + h_blank_start_adjust = -1; + h_blank_end_adjust = 0; + } + break; + case 16: + hactive = screen->width / 8; + hblank /= 8; + hfp /= 8; + hbp /= 8; + h_screen_off = hactive * 2; + s3Set (s3vga, s3_pixel_length, 1); + s3Set (s3vga, s3_2d_graphics_engine_timeout, 0x14); + if (clock_double) + { + if (screen->depth == 15) + s3Set (s3vga, s3_color_mode, 3); + else + s3Set (s3vga, s3_color_mode, 5); + s3Set (s3vga, s3_dclk_over_2, 1); + s3Set (s3vga, s3_enable_clock_double, 1); + s3Set (s3vga, s3_border_select, 0); + } + else + { + if (screen->depth == 15) + s3Set (s3vga, s3_color_mode, 2); + else + s3Set (s3vga, s3_color_mode, 4); + s3Set (s3vga, s3_dclk_over_2, 0); + s3Set (s3vga, s3_enable_clock_double, 0); + s3Set (s3vga, s3_delay_blank, 0); + } + break; + case 32: + hactive = screen->width / 8; + hblank /= 8; + hfp /= 8; + hbp /= 8; + h_screen_off = hactive * 4; + s3Set (s3vga, s3_pixel_length, 3); + s3Set (s3vga, s3_color_mode, 0xd); + s3Set (s3vga, s3_2d_graphics_engine_timeout, 0x07); + break; + } + + /* + * X server starts frame buffer at top of memory + */ + s3Set (s3vga, s3_start_address, 0); + + /* + * Compute horizontal register values from timings + */ + h_total = hactive + hblank - 5; + h_display_end = hactive - 1; + + h_sync_start = hactive + hfp; + h_sync_end = hactive + hblank - hbp; + /* + * pad the blank values narrow a bit and use the border_select to + * eliminate the remaining border; don't know why, but it doesn't + * work in the documented fashion + */ + h_blank_start = hactive + 1 + h_blank_start_adjust; + h_blank_end = hactive + hblank - 2 + h_blank_end_adjust; + /* + * The manual says h_total - 5, but the + * bios does differently... + */ + if (screen->width >= 1600) + h_start_fifo_fetch = h_total - 24; + else if (screen->width >= 1280) + h_start_fifo_fetch = h_total - 19; + else if (screen->width >= 1024) + h_start_fifo_fetch = h_total - 14; + else if (screen->width >= 800) + h_start_fifo_fetch = h_total - 10; + else + h_start_fifo_fetch = h_total - 5; + + if (h_blank_end - h_blank_start >= 0x40) + h_blank_extend = 1; + else + h_blank_extend = 0; + + if (h_sync_end - h_sync_start >= 0x20) + h_sync_extend = 1; + else + h_sync_extend = 0; + + primary_stream_l1 = (screen->width * screen->bitsPerPixel / (8 * 8)) - 1; + +#ifdef DEBUG + fprintf (stderr, "h_total %d h_display_end %d\n", + h_total, h_display_end); + fprintf (stderr, "h_sync_start %d h_sync_end %d h_sync_extend %d\n", + h_sync_start, h_sync_end, h_sync_extend); + fprintf (stderr, "h_blank_start %d h_blank_end %d h_blank_extend %d\n", + h_blank_start, h_blank_end, h_blank_extend); +#endif + + s3Set (s3vga, s3_h_total, h_total); + s3Set (s3vga, s3_h_display_end, h_display_end); + s3Set (s3vga, s3_h_blank_start, h_blank_start); + s3Set (s3vga, s3_h_blank_end, h_blank_end); + s3Set (s3vga, s3_h_sync_start, h_sync_start); + s3Set (s3vga, s3_h_sync_end, h_sync_end); + s3Set (s3vga, s3_screen_offset, h_screen_off); + s3Set (s3vga, s3_h_start_fifo_fetch, h_start_fifo_fetch); + s3Set (s3vga, s3_h_sync_extend, h_sync_extend); + s3Set (s3vga, s3_h_blank_extend, h_blank_extend); + + s3Set (s3vga, s3_primary_stream_l1, primary_stream_l1); + + v_total = vactive + vblank - 2; + v_display_end = vactive - 1; + +#if 0 + v_blank_start = vactive - 1; + v_blank_end = v_blank_start + vblank - 1; +#else + v_blank_start = vactive - 1; + v_blank_end = v_blank_start + vblank - 1; +#endif + + v_retrace_start = vactive + vfp; + v_retrace_end = vactive + vblank - vbp; + + s3Set (s3vga, s3_v_total, v_total); + s3Set (s3vga, s3_v_retrace_start, v_retrace_start); + s3Set (s3vga, s3_v_retrace_end, v_retrace_end); + s3Set (s3vga, s3_v_display_end, v_display_end); + s3Set (s3vga, s3_v_blank_start, v_blank_start); + s3Set (s3vga, s3_v_blank_end, v_blank_end); + + if (vactive >= 1024) + s3Set (s3vga, s3_line_compare, 0x7ff); + else + s3Set (s3vga, s3_line_compare, 0x3ff); + + /* + * Set cursor + */ + if (!screen->softCursor) + { + cursor_address = (s3s->cursor_base - screen->frameBuffer) / 1024; + + s3Set (s3vga, s3_cursor_address, cursor_address); + s3Set (s3vga, s3_cursor_ms_x11, 0); + s3Set (s3vga, s3_cursor_enable, 1); + } + else + s3Set (s3vga, s3_cursor_enable, 0); + + /* + * Set accelerator + */ + switch (screen->width) { + case 640: s3Set (s3vga, s3_ge_screen_width, 1); break; + case 800: s3Set (s3vga, s3_ge_screen_width, 2); break; + case 1024: s3Set (s3vga, s3_ge_screen_width, 0); break; + case 1152: s3Set (s3vga, s3_ge_screen_width, 4); break; + case 1280: s3Set (s3vga, s3_ge_screen_width, 3); break; + case 1600: s3Set (s3vga, s3_ge_screen_width, 6); break; + } + +#if 0 + crtc->l_parm_0_7 = screen->width / 4; /* Undocumented. */ +#endif + + /* + * Set DPMS to normal + */ + s3Set (s3vga, s3_hsync_control, 0); + s3Set (s3vga, s3_vsync_control, 0); + + _s3SetDepth (s3c->s3, s3vga); +} + +void +s3Disable (ScreenPtr pScreen) +{ +} + +void +s3Restore (KdCardInfo *card) +{ + S3CardInfo *s3c = card->driver; + S3Ptr s3 = s3c->s3; + S3Vga *s3vga = &s3c->s3vga; + S3Save *save = &s3c->save; + CARD8 *cursor_base; + + /* graphics engine state */ + s3->alt_mix = save->alt_mix; + s3->write_mask = save->write_mask; + s3->fg = save->fg; + s3->bg = save->bg; + /* XXX should save and restore real values? */ + s3->scissors_tl = 0x00000000; + s3->scissors_br = 0x0fff0fff; + + _s3SetBlank (s3, s3vga, TRUE); + VgaRestore (&s3vga->card); + s3Set (s3vga, s3_linear_window_size, 3); + s3Set (s3vga, s3_enable_linear, 1); + VgaFlush (&s3vga->card); + memcpy (s3c->frameBuffer, save->text_save, S3_TEXT_SAVE); + s3Reset (s3vga); + _s3SetBlank (s3, s3vga, FALSE); +} + +void +_s3SetSync (S3CardInfo *s3c, int hsync, int vsync) +{ + /* this abuses the macros defined to access the crtc structure */ + S3Ptr s3 = s3c->s3; + S3Vga *s3vga = &s3c->s3vga; + + s3Set (s3vga, s3_hsync_control, hsync); + s3Set (s3vga, s3_vsync_control, vsync); + VgaFlush (&s3vga->card); +} + +Bool +s3DPMS (ScreenPtr pScreen, int mode) +{ + KdScreenPriv(pScreen); + s3CardInfo(pScreenPriv); + S3Vga *s3vga = &s3c->s3vga; + + switch (mode) { + case KD_DPMS_NORMAL: + _s3SetSync (s3c, 0, 0); + _s3SetBlank (s3c->s3, s3vga, FALSE); + break; + case KD_DPMS_STANDBY: + _s3SetBlank (s3c->s3, s3vga, TRUE); + _s3SetSync (s3c, 1, 0); + break; + case KD_DPMS_SUSPEND: + _s3SetBlank (s3c->s3, s3vga, TRUE); + _s3SetSync (s3c, 0, 1); + break; + case KD_DPMS_POWERDOWN: + _s3SetBlank (s3c->s3, s3vga, TRUE); + _s3SetSync (s3c, 1, 1); + break; + } + return TRUE; +} + +void +s3ScreenFini (KdScreenInfo *screen) +{ + S3ScreenInfo *s3s = (S3ScreenInfo *) screen->driver; + + xfree (s3s); + screen->driver = 0; +} + +void +s3CardFini (KdCardInfo *card) +{ + S3CardInfo *s3c = (S3CardInfo *) card->driver; + + KdUnmapDevice (s3c->frameBuffer, s3c->memory); + KdUnmapDevice (s3c->registers, sizeof (S3) + PACKED_OFFSET); + xfree (s3c); + card->driver = 0; +} + +KdCardFuncs s3Funcs = { + s3CardInit, + s3ScreenInit, + s3Preserve, + s3Enable, + s3DPMS, + s3Disable, + s3Restore, + s3ScreenFini, + s3CardFini, + s3CursorInit, + s3CursorEnable, + s3CursorDisable, + s3CursorFini, + s3RecolorCursor, + s3DrawInit, + s3DrawEnable, + s3DrawDisable, + s3DrawFini, + s3GetColors, + s3PutColors, +}; |