diff options
Diffstat (limited to 'hw/kdrive/trio/s3.c')
-rw-r--r-- | hw/kdrive/trio/s3.c | 1298 |
1 files changed, 1298 insertions, 0 deletions
diff --git a/hw/kdrive/trio/s3.c b/hw/kdrive/trio/s3.c new file mode 100644 index 000000000..3233173cb --- /dev/null +++ b/hw/kdrive/trio/s3.c @@ -0,0 +1,1298 @@ +/* + * $Id$ + * + * 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: $ */ + +#include "s3.h" + +#define REGISTERS_OFFSET (0x1000000) +#define PACKED_OFFSET (0x8100) + +/* + * 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 */ + }, + + { 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, + 24, 144, 304, /* horizontal 56.604 KHz */ + 3, 29, 38, /* vertical 70.227 Hz */ + 40, 2, 1, /* 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, + 16, 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 */ + }, + + { 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])) + +CARD8 +_s3ReadIndexRegister (volatile CARD8 *base, CARD8 index) +{ + CARD8 ret; + *base = index; + ret = *(base + 1); + DRAW_DEBUG ((DEBUG_CRTC, " 0x%3x 0x%02x -> 0x%02x", + ((int) base) & 0xfff, index, ret)); + return ret; +} + +void +_s3WriteIndexRegister (volatile CARD8 *base, CARD8 index, CARD8 value) +{ + DRAW_DEBUG ((DEBUG_CRTC, " 0x%3x 0x%02x <- 0x%02x", + ((int) base) & 0xfff, index, value)); + *base = index; + *(base + 1) = value; +} + +/* + * Map the S3 card and detect its configuration. Do not touch the card + */ + +static void +_s3LoadCrtc (S3Ptr s3, S3Crtc *crtc) +{ + crtc->h_total_0_7 = GetCrtc (s3, 0x00); + crtc->h_display_end_0_7 = GetCrtc (s3, 0x01); + crtc->h_blank_start_0_7 = GetCrtc (s3, 0x02); + crtc->_h_blank_end = GetCrtc (s3, 0x03); + crtc->h_sync_start_0_7 = GetCrtc (s3, 0x04); + crtc->_h_sync_end = GetCrtc (s3, 0x05); + crtc->v_total_0_7 = GetCrtc (s3, 0x06); + crtc->crtc_overflow = GetCrtc (s3, 0x07); + crtc->preset_row_scan = GetCrtc (s3, 0x08); + crtc->_max_scan_line = GetCrtc (s3, 0x09); + + crtc->start_address_8_15 = GetCrtc (s3, 0x0c); + crtc->start_address_0_7 = GetCrtc (s3, 0x0d); + + crtc->v_retrace_start_0_7 = GetCrtc (s3, 0x10); + crtc->_v_retrace_end = GetCrtc (s3, 0x11); + crtc->v_display_end_0_7 = GetCrtc (s3, 0x12); + crtc->screen_off_0_7 = GetCrtc (s3, 0x13); + + crtc->v_blank_start_0_7 = GetCrtc (s3, 0x15); + crtc->v_blank_end_0_7 = GetCrtc (s3, 0x16); + + crtc->line_compare_0_7 = GetCrtc (s3, 0x18); + + crtc->memory_configuration = GetCrtc (s3, 0x31); + + crtc->misc_1 = GetCrtc (s3, 0x3a); + crtc->h_start_fifo_fetch_0_7 = GetCrtc (s3, 0x3b); + + crtc->mode_control = GetCrtc (s3, 0x42); + + crtc->hardware_cursor_mode = GetCrtc (s3, 0x45); + crtc->cursor_address_8_15 = GetCrtc (s3, 0x4C); + crtc->cursor_address_0_7 = GetCrtc (s3, 0x4D); + + crtc->extended_system_control_1 = GetCrtc (s3, 0x50); + crtc->extended_system_control_2 = GetCrtc (s3, 0x51); + + crtc->extended_memory_control = GetCrtc (s3, 0x53); + + crtc->extended_ramdac_control = GetCrtc (s3, 0x55); + + crtc->extended_horizontal_overflow = GetCrtc (s3, 0x5d); + crtc->extended_vertical_overflow = GetCrtc (s3, 0x5e); + + crtc->l_parm_0_7 = GetCrtc (s3, 0x62); + + crtc->extended_misc_control = GetCrtc (s3, 0x65); + + crtc->extended_misc_control_2 = GetCrtc (s3, 0x67); + + crtc->configuration_3 = GetCrtc (s3, 0x68); + + crtc->extended_system_control_3 = GetCrtc (s3, 0x69); + + crtc->extended_bios_5 = GetCrtc (s3, 0x6d); + + crtc->extended_sequencer_b = GetSrtc (s3, 0x0b); + crtc->extended_sequencer_d = GetSrtc (s3, 0x0d); + crtc->dclk_value_low = GetSrtc (s3, 0x12); + crtc->dclk_value_high = GetSrtc (s3, 0x13); + crtc->control_2 = GetSrtc (s3, 0x15); + crtc->ramdac_control = GetSrtc (s3, 0x18); + + crtc->dclk_value_low_savage = GetSrtc (s3, 0x36); + crtc->dclk_pll_m0_savage_0_7 = GetSrtc (s3, 0x37); + crtc->dclk_pll_m1_savage_0_7 = GetSrtc (s3, 0x38); + crtc->extended_seq_39 = GetSrtc (s3, 0x39); + + fprintf (stderr, "SR1A 0x%x SR1B 0x%x\n", + GetSrtc (s3, 0x1a), + GetSrtc (s3, 0x1b)); + fprintf (stderr, "SR36 0x%x SR37 0x%x SR38 0x%x SR39 0x%x\n", + GetSrtc (s3, 0x36), + GetSrtc (s3, 0x37), + GetSrtc (s3, 0x38), + GetSrtc (s3, 0x39)); +/* combine values */ + + switch (crtc_ge_screen_width(crtc)) { + case 0: + if (crtc->enable_two_page) + crtc->ge_screen_pitch = 2048; + else + crtc->ge_screen_pitch = 1024; + break; + case 1: + crtc->ge_screen_pitch = 640; + break; + case 2: + /* ignore magic 1600x1200x4 mode */ + crtc->ge_screen_pitch = 800; + break; + case 3: + crtc->ge_screen_pitch = 1280; + break; + case 4: + crtc->ge_screen_pitch = 1152; + break; + case 5: + crtc->ge_screen_pitch = 0; /* reserved */ + break; + case 6: + crtc->ge_screen_pitch = 1600; + break; + case 7: + crtc->ge_screen_pitch = 0; /* reserved */ + break; + } + switch (crtc->pixel_length) { + case 0: + crtc->bits_per_pixel = 8; + crtc->pixel_width = (crtc_h_display_end(crtc) + 1) * 8; + break; + case 1: + crtc->bits_per_pixel = 16; + crtc->pixel_width = (crtc_h_display_end(crtc) + 1) * 4; + break; + case 3: + crtc->bits_per_pixel = 32; + crtc->pixel_width = (crtc_h_display_end(crtc) + 1) * 8; + break; + } + crtc->double_pixel_mode = 0; + switch (crtc->color_mode) { + case 0x0: + crtc->depth = 8; break; + case 0x1: + crtc->depth = 8; crtc->double_pixel_mode = 1; break; + case 0x3: + crtc->depth = 15; break; + case 0x5: + crtc->depth = 16; break; + case 0x7: + crtc->depth = 24; break; /* unused */ + case 0xd: + crtc->depth = 24; break; + } +} + +static void +_s3SetBlank (S3Ptr s3, Bool blank) +{ + CARD8 clock_mode; + + DRAW_DEBUG ((DEBUG_S3INIT, "3c4 at 0x%x\n", &s3->crt_vga_3c4)); + clock_mode = _s3ReadIndexRegister (&s3->crt_vga_3c4, 0x01); + if (blank) + clock_mode |= 0x20; + else + clock_mode &= ~0x20; + _s3WaitVRetrace (s3); + _s3WriteIndexRegister (&s3->crt_vga_3c4, 0x01, clock_mode); + DRAW_DEBUG ((DEBUG_S3INIT, "blank is set to 0x%x", clock_mode)); +} + +static void +_s3SetDepth (S3Ptr s3, S3Crtc *crtc) +{ + CARD8 save_3c2; + _s3SetBlank (s3, TRUE); + PutCrtc(s3, 0x38, 0x48); + PutCrtc(s3, 0x39, 0xA0); + PutCrtc(s3, 0x00, crtc->h_total_0_7); + PutCrtc(s3, 0x01, crtc->h_display_end_0_7); + PutCrtc(s3, 0x02, crtc->h_blank_start_0_7); + PutCrtc(s3, 0x03, crtc->_h_blank_end); + PutCrtc(s3, 0x04, crtc->h_sync_start_0_7); + PutCrtc(s3, 0x05, crtc->_h_sync_end); + PutCrtc(s3, 0x06, crtc->v_total_0_7); + PutCrtc(s3, 0x07, crtc->crtc_overflow); + PutCrtc(s3, 0x09, crtc->_max_scan_line); + PutCrtc(s3, 0x0c, crtc->start_address_8_15); + PutCrtc(s3, 0x0d, crtc->start_address_0_7); + PutCrtc(s3, 0x10, crtc->v_retrace_start_0_7); + PutCrtc(s3, 0x11, crtc->_v_retrace_end); + PutCrtc(s3, 0x12, crtc->v_display_end_0_7); + PutCrtc(s3, 0x13, crtc->screen_off_0_7); + PutCrtc(s3, 0x15, crtc->v_blank_start_0_7); + PutCrtc(s3, 0x16, crtc->v_blank_end_0_7); + PutCrtc(s3, 0x18, crtc->line_compare_0_7); + PutCrtc(s3, 0x31, crtc->memory_configuration); + PutCrtc(s3, 0x3a, crtc->misc_1); + PutCrtc(s3, 0x3b, crtc->h_start_fifo_fetch_0_7); + PutCrtc(s3, 0x42, crtc->mode_control); + PutCrtc(s3, 0x45, crtc->hardware_cursor_mode); + PutCrtc(s3, 0x4c, crtc->cursor_address_8_15); + PutCrtc(s3, 0x4d, crtc->cursor_address_0_7); + PutCrtc(s3, 0x50, crtc->extended_system_control_1); + PutCrtc(s3, 0x51, crtc->extended_system_control_2); + PutCrtc(s3, 0x53, crtc->extended_memory_control); + PutCrtc(s3, 0x55, crtc->extended_ramdac_control); + PutCrtc(s3, 0x5d, crtc->extended_horizontal_overflow); + PutCrtc(s3, 0x5e, crtc->extended_vertical_overflow); + PutCrtc(s3, 0x62, crtc->l_parm_0_7); + PutCrtc(s3, 0x65, crtc->extended_misc_control); + PutCrtc(s3, 0x67, crtc->extended_misc_control_2); + PutCrtc(s3, 0x68, crtc->configuration_3); + PutCrtc(s3, 0x69, crtc->extended_system_control_3); + PutCrtc(s3, 0x6d, crtc->extended_bios_5); + PutCrtc(s3, 0x39, 0x00); + PutCrtc(s3, 0x38, 0x00); + PutSrtc(s3, 0x0b, crtc->extended_sequencer_b); + PutSrtc(s3, 0x0d, crtc->extended_sequencer_d); + /* + * Move new dclk/mclk values into PLL + */ + save_3c2 = s3->crt_vga_3cc; + DRAW_DEBUG ((DEBUG_S3INIT, "save_3c2 0x%x", save_3c2)); + s3->crt_vga_3c2 = 0x0f; + + PutSrtc (s3, 0x36, crtc->dclk_value_low_savage); + PutSrtc (s3, 0x37, crtc->dclk_pll_m0_savage_0_7); + PutSrtc (s3, 0x38, crtc->dclk_pll_m1_savage_0_7); + PutSrtc (s3, 0x39, crtc->extended_seq_39); + PutSrtc(s3, 0x12, crtc->dclk_value_low); + PutSrtc(s3, 0x13, crtc->dclk_value_high); + + DRAW_DEBUG ((DEBUG_S3INIT, "Set PLL load enable, frobbing clk_load...")); + crtc->dfrq_en = 1; + PutSrtc(s3, 0x15, crtc->control_2); + PutSrtc(s3, 0x18, crtc->ramdac_control); + + DRAW_DEBUG ((DEBUG_S3INIT, "Clk load frobbed, restoring 3c2 to 0x%x", save_3c2)); + s3->crt_vga_3c2 = save_3c2; + + DRAW_DEBUG ((DEBUG_S3INIT, "Enabling display")); + _s3SetBlank (s3, FALSE); +} + +void +_s3RestoreCrtc (S3Ptr s3, S3Crtc *crtc) +{ + _s3SetDepth (s3, crtc); +} + +s3Reset (S3CardInfo *s3c) +{ + S3Ptr s3 = s3c->s3; + S3Save *save = &s3c->save; + CARD8 *cursor_base; + + LockS3 (s3c); + + _s3UnlockExt (s3); + + _s3RestoreCrtc (s3, &save->crtc); + + /* set foreground */ + /* Reset cursor color stack pointers */ + (void) GetCrtc(s3, 0x45); + PutCrtc(s3, 0x4a, save->cursor_fg); + /* XXX for deeper screens? */ + + /* set background */ + /* Reset cursor color stack pointers */ + (void) GetCrtc(s3, 0x45); + PutCrtc(s3, 0x4b, save->cursor_bg); + + _s3LockExt (s3); + + /* 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; + + _s3WriteIndexRegister (&s3->crt_vga_3c4, 0x01, save->clock_mode); + PutSrtc(s3, 0x08, save->locksrtc); + PutCrtc(s3, 0x39, save->lock2); + PutCrtc(s3, 0x38, save->lock1); + + UnlockS3 (s3c); +} + +void +s3Save (S3CardInfo *s3c) +{ + S3Ptr s3 = s3c->s3; + S3Save *save = &s3c->save; + S3Crtc newCrtc; + CARD8 t1, t2; + CARD8 *cursor_base; + + LockS3 (s3c); + + save->alt_mix = s3->alt_mix; + save->write_mask = s3->write_mask; + save->fg = s3->fg; + save->bg = s3->bg; + + save->lock1 = GetCrtc(s3, 0x38); + save->lock2 = GetCrtc(s3, 0x39); + save->locksrtc = GetSrtc (s3, 0x08); + + /* unlock srtc registers to read them */ + PutSrtc (s3, 0x08, 0x06); + + save->clock_mode = _s3ReadIndexRegister (&s3->crt_vga_3c4, 0x01); + + _s3UnlockExt (s3); + save->cursor_fg = GetCrtc(s3, 0x4a); + save->cursor_bg = GetCrtc(s3, 0x4b); + + _s3LoadCrtc (s3, &save->crtc); + + _s3LockExt (s3); + + UnlockS3 (s3c); +} +Bool +s3CardInit (KdCardInfo *card) +{ + S3CardInfo *s3c; + S3Ptr s3; + int size; + CARD8 *registers; + CARD32 s3FrameBuffer; + CARD32 s3Registers; + CARD8 *temp_buffer; + CARD32 max_memory; + + DRAW_DEBUG ((DEBUG_S3INIT, "s3CardInit")); + s3c = (S3CardInfo *) xalloc (sizeof (S3CardInfo)); + if (!s3c) + { + DRAW_DEBUG ((DEBUG_FAILURE, "can't alloc s3 card info")); + 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; + s3c->savage = TRUE; + } + else + { + s3FrameBuffer = card->attr.address[0]; + s3Registers = s3FrameBuffer + REGISTERS_OFFSET; + max_memory = 4 * 1024 * 1024; + s3c->savage = FALSE; + } + + fprintf (stderr, "S3 at 0x%x/0x%x\n", s3Registers, s3FrameBuffer); + 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; + +#if 0 + s3->crt_vga_3c3 = 1; /* wake up part from deep sleep */ + s3->crt_vga_3c2 = 0x01 | 0x02 | 0x0c; + + s3->crt_vga_3c4 = 0x58; + s3->crt_vga_3c5 = 0x10 | 0x3; +#endif + + /* + * 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); + + fprintf (stderr, "Frame buffer 0x%x\n", s3c->memory); + + DRAW_DEBUG ((DEBUG_S3INIT, "Detected frame buffer %d", s3c->memory)); + + KdUnmapDevice (temp_buffer, 4096 * 1024); + + 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; + + DRAW_DEBUG ((DEBUG_S3INIT, "s3ScreenInit")); + 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; + + DRAW_DEBUG ((DEBUG_S3INIT, "Requested parameters %dx%dx%d", + screen->width, screen->height, screen->rate)); + for (i = 0, t = s3Timings; i < NUM_S3_TIMINGS; i++, t++) + { + DRAW_DEBUG ((DEBUG_S3INIT, "Available parameters %dx%dx%d", + t->horizontal, t->vertical, t->rate)); + 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; +#if 0 + s3GetClock (t, &m, &n, &r, + s3c->savage ? 511 : 127, + s3c->savage ? 127 : 31, + s3c->savage ? 4 : 3); +#else + s3GetClock (t, &m, &n, &r, 127, 31, 3); +#endif +#if 1 + 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 (s3c->savage) + { + if (screen->depth > 16 && S3_CLOCK(m,n,r) > S3_MAX_CLOCK) + screen->depth = 16; + } + else + { + if (screen->depth > 8 && S3_CLOCK(m,n,r) > S3_MAX_CLOCK) + screen->depth = 8; + } + + 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; + + DRAW_DEBUG ((DEBUG_S3INIT, "depth %d bits %d", screen->depth, screen->bitsPerPixel)); + + DRAW_DEBUG ((DEBUG_S3INIT, "Screen size %dx%d memory %d", + screen->width, screen->height, s3c->memory)); + DRAW_DEBUG ((DEBUG_S3INIT, "frame buffer 0x%x cursor 0x%x offscreen 0x%x", + s3c->frameBuffer, s3s->cursor_base, s3s->offscreen)); + DRAW_DEBUG ((DEBUG_S3INIT, "offscreen %dx%d+%d+%d", + s3s->offscreen_width, s3s->offscreen_height, + s3s->offscreen_x, s3s->offscreen_y)); + + 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; + + s3Save (s3c); +} + +/* + * 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); + + S3Crtc crtcR, *crtc; + 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 v_total; + int v_retrace_start; + int v_retrace_end; + int v_display_end; + int v_blank_start; + int v_blank_end; + + int h_adjust; + int h_sync_extend_; + int h_blank_extend_; + int i; + CARD16 cursor_address; + S3Timing *t; + int m, n, r; + + DRAW_DEBUG ((DEBUG_S3INIT, "s3Enable")); + + DRAW_DEBUG ((DEBUG_S3INIT, "requested bpp %d current %d", + pScreenPriv->bitsPerPixel, s3c->save.crtc.bits_per_pixel)); + + 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; + + crtcR = s3c->save.crtc; + crtc = &crtcR; + +#if 0 + if (s3c->savage) + { + m = ((int) crtc->dclk_pll_m_savage_8 << 8) | crtc->dclk_pll_m0_savage_0_7; + n = (crtc->dclk_pll_n_savage_6 << 6) | crtc->dclk_pll_n_savage_0_5; + r = (crtc->dclk_pll_r_savage_2 << 2) | crtc->dclk_pll_r_savage_0_1; + fprintf (stderr, "old clock %d, %d, %d\n", m, n, r); + s3GetClock (t, &m, &n, &r, 127, 31, 3); +#if 0 + s3GetClock (t, &m, &n, &r, 511, 127, 4); + crtc->dclk_pll_m0_savage_0_7 = m; + crtc->dclk_pll_m1_savage_0_7 = m; + crtc->dclk_pll_m_savage_8 = m >> 8; + crtc->dclk_pll_n_savage_0_5 = n; + crtc->dclk_pll_n_savage_6 = n >> 6; + crtc->dclk_pll_r_savage_0_1 = r; + crtc->dclk_pll_r_savage_2 = r >> 2; + crtc->dclk_pll_select_savage = 1; +#endif + fprintf (stderr, "new clock %d, %d, %d\n", m, n, r); + } + else + { + s3GetClock (t, &m, &n, &r, 127, 31, 3); + crtc->dclk_pll_m_trio = m; + crtc->dclk_pll_n_trio = n; + crtc->dclk_pll_r_trio = r; + } + + if (!s3c->savage) + { + crtc->alt_refresh_count = 0x02; + crtc->enable_alt_refresh = 1; + } + else + { + crtc->alt_refresh_count = 1; + crtc->enable_alt_refresh = 0; + } + crtc->enable_256_or_more = 1; + + DRAW_DEBUG ((DEBUG_S3INIT, "memory_bus_size %d\n", crtc->memory_bus_size)); + if (!s3c->savage) + crtc->memory_bus_size = 1; + + if (!s3c->savage) + crtc->dclk_over_2 = 0; + crtc->dclk_invert = 0; + crtc->enable_clock_double = 0; + crtc->delay_blank = 0; + if (!s3c->savage) + crtc->extended_bios_5 = 0; + /* + * Compute character lengths for horizontal timing values + */ + switch (screen->bitsPerPixel) { + case 8: + hactive = screen->width / 8; + hblank /= 8; + hfp /= 8; + hbp /= 8; + h_screen_off = hactive; + crtc->pixel_length = 0; + crtc->color_mode = 0; + /* + * Set up for double-pixel mode, switch color modes, + * divide the dclk and delay h blank by 2 dclks + */ + if (S3_CLOCK(m, n, r) > S3_MAX_CLOCK) + { + DRAW_DEBUG ((DEBUG_S3INIT, "S3 clock %g > 80MHz, using pixel double mode", + S3_CLOCK(crtc->dclk_pll_m, crtc->dclk_pll_n, + crtc->dclk_pll_r))); + crtc->color_mode = 1; + crtc->dclk_over_2 = 1; + crtc->enable_clock_double = 1; + crtc->delay_blank = 2; + crtc->extended_bios_5 = 2; + } + h_adjust = 1; + break; + case 16: + if (s3c->savage) + { + hactive = screen->width / 4; + hblank /= 4; + hfp /= 4; + hbp /= 4; + h_screen_off = hactive * 2; + crtc->pixel_length = 1; + if (S3_CLOCK(m,n,r) > S3_MAX_CLOCK) + { + if (crtc->depth == 15) + crtc->color_mode = 3; + else + crtc->color_mode = 5; + crtc->dclk_over_2 = 1; + crtc->enable_clock_double = 1; + crtc->delay_blank = 2; + } + else + { + if (crtc->depth == 15) + crtc->color_mode = 2; + else + crtc->color_mode = 4; + crtc->dclk_over_2 = 0; + crtc->enable_clock_double = 0; + } + h_adjust = 1; + } + else + { + hactive = screen->width / 4; + hblank /= 4; + hfp /= 4; + hbp /= 4; + h_screen_off = hactive; + crtc->pixel_length = 1; + crtc->extended_bios_5 = 2; + if (crtc->depth == 15) + crtc->color_mode = 3; + else + crtc->color_mode = 5; + h_adjust = 2; + } + break; + case 32: + hactive = screen->width / 8; + hblank /= 8; + hfp /= 8; + hbp /= 8; + h_screen_off = hactive * 4; + crtc->pixel_length = 3; + crtc->color_mode = 0xd; + h_adjust = 1; + break; + } +#endif + + /* + * X server starts frame buffer at top of memory + */ + DRAW_DEBUG ((DEBUG_S3INIT, "Old start address 0x%x\n", + crtc_start_address (crtc))); + crtc_set_start_address (crtc, 0); + +#if 0 + /* + * 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_adjust; + h_sync_start = hactive + hfp + h_adjust; + h_sync_end = h_sync_start + hblank - hbp - hfp; + h_start_fifo_fetch = h_total - 5; + + DRAW_DEBUG ((DEBUG_S3INIT, "blank_end 0x%x sync_end 0x%x sync_start 0x%x\n", + h_blank_end, h_sync_end, h_sync_start)); + + 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; + + DRAW_DEBUG ((DEBUG_S3INIT, "blank_end 0x%x sync_end 0x%x extend %d\n", + h_blank_end, h_sync_end, h_sync_extend_)); + + 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); + crtc_set_h_start_fifo_fetch (crtc, h_start_fifo_fetch); + crtc->h_sync_extend = h_sync_extend_; + crtc->h_blank_extend = h_blank_extend_; + + + v_total = vactive + vblank - 2; + v_retrace_start = vactive + vfp - 1; + v_retrace_end = v_retrace_start + vblank - vbp - 1; + 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; +#endif + + /* + * Set cursor + */ + if (!screen->softCursor) + { + cursor_address = (s3s->cursor_base - screen->frameBuffer) / 1024; + + crtc->cursor_address_0_7 = cursor_address; + crtc->cursor_address_8_15 = cursor_address >> 8; + crtc->hardware_cursor_ms_x11 = 0; + crtc->hardware_cursor_enable = 1; + } + else + crtc->hardware_cursor_enable = 0; + + /* + * Set accelerator + */ + switch (screen->width) { + case 640: crtc_set_ge_screen_width(crtc,1); break; + case 800: crtc_set_ge_screen_width(crtc,2); break; + case 1024: crtc_set_ge_screen_width(crtc,0); break; + case 1152: crtc_set_ge_screen_width(crtc,4); break; + case 1280: crtc_set_ge_screen_width(crtc,3); break; + case 1600: crtc_set_ge_screen_width(crtc,6); break; + } + + /* + * Set depth values + */ + crtc->bits_per_pixel = screen->bitsPerPixel; + crtc->depth = screen->depth; + + crtc->l_parm_0_7 = screen->width / 4; /* Undocumented. */ + + crtc->disable_v_retrace_int = 1; /* don't let retrace interrupt */ + + DRAW_DEBUG ((DEBUG_S3INIT, "new h total %d display_end %d", + crtc_h_total(crtc), + crtc_h_display_end(crtc))); + DRAW_DEBUG ((DEBUG_S3INIT, " sync_start %d sync_end %d (%d)", + crtc_h_sync_start(crtc), + crtc_h_sync_end(crtc), h_sync_end)); + + DRAW_DEBUG ((DEBUG_S3INIT, " blank_start %d blank_end %d", + crtc_h_blank_start(crtc), + crtc_h_blank_end(crtc))); + + DRAW_DEBUG ((DEBUG_S3INIT, " screen_off %d start_fifo %d", + crtc_screen_off(crtc), crtc_h_start_fifo_fetch(crtc))); + + DRAW_DEBUG ((DEBUG_S3INIT, " active %d blank %d fp %d bp %d", + hactive, hblank, hfp, hbp)); + + DRAW_DEBUG ((DEBUG_S3INIT, "new v total %d display_end %d", + crtc_v_total(crtc), + crtc_v_display_end(crtc))); + DRAW_DEBUG ((DEBUG_S3INIT, " retrace_start %d retrace_end %d (%d)", + crtc_v_retrace_start(crtc), + crtc->v_retrace_end, + v_retrace_end)); + DRAW_DEBUG ((DEBUG_S3INIT, " blank_start %d blank_end %d", + crtc_v_blank_start(crtc), + crtc->v_blank_end_0_7)); + + DRAW_DEBUG ((DEBUG_S3INIT, " active %d blank %d fp %d bp %d", + vactive, vblank, vfp, vbp)); + + /* + * Set DPMS to normal + */ + crtc->hsync_control = 0; + crtc->vsync_control = 0; + + LockS3 (s3c); + _s3SetDepth (s3c->s3, crtc); + UnlockS3 (s3c); +} + +void +s3Disable (ScreenPtr pScreen) +{ +} + +void +s3Restore (KdCardInfo *card) +{ + S3CardInfo *s3c = card->driver; + + s3Reset (s3c); +} + +void +_s3SetSync (S3CardInfo *s3c, int hsync, int vsync) +{ + /* this abuses the macros defined to access the crtc structure */ + union extended_sequencer_d_u _extended_sequencer_d_u; + S3Ptr s3 = s3c->s3; + + extended_sequencer_d = s3c->save.crtc.extended_sequencer_d; + hsync_control = hsync; + vsync_control = vsync; + PutSrtc (s3, 0x0d, extended_sequencer_d); +} + +Bool +s3DPMS (ScreenPtr pScreen, int mode) +{ + KdScreenPriv(pScreen); + s3CardInfo(pScreenPriv); + + switch (mode) { + case KD_DPMS_NORMAL: + _s3SetSync (s3c, 0, 0); + _s3SetBlank (s3c->s3, FALSE); + break; + case KD_DPMS_STANDBY: + _s3SetBlank (s3c->s3, TRUE); + _s3SetSync (s3c, 1, 0); + break; + case KD_DPMS_SUSPEND: + _s3SetBlank (s3c->s3, TRUE); + _s3SetSync (s3c, 0, 1); + break; + case KD_DPMS_POWERDOWN: + _s3SetBlank (s3c->s3, 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); +/* DeleteCriticalSection (&s3c->lock); */ + 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, +}; |