/* * s3dacs.c: * * RAMDAC definitions for the S3-SDAC (86C716), S3-GENDAC, and Trio64. * * These contain S3-specific code. */ #include #include "libvga.h" #include "timing.h" #include "vgaregs.h" #include "driver.h" /* for __svgalib_driver_report */ #include "ramdac.h" /* SDAC/GENDAC registers */ #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) #define SDAC_COMMAND 0 /* Register offsets into state. */ #define GENDAC_COMMAND 0 #define SDAC_PLL_WRITEINDEX 1 #define SDAC_PLL_READINDEX 2 #define SDAC_PLL_M 3 /* f2 programmed clock */ #define SDAC_PLL_N1_N2 4 #define SDAC_PLL_CONTROL 5 #define SDAC_STATESIZE 6 /* 6 registers. */ #define GENDAC_STATESIZE 6 #endif #if defined(INCLUDE_S3_SDAC_DAC_TEST) || defined(INCLUDE_S3_GENDAC_DAC_TEST) static int GENDAC_SDAC_probe(void) { /* Taken from XFree86, accel/s3.c. */ /* Return 1 if GENDAC found, 2 if SDAC, 0 otherwise. */ /* probe for S3 GENDAC or SDAC */ /* * S3 GENDAC and SDAC have two fixed read only PLL clocks * CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61 * CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62 * which can be used to detect GENDAC and SDAC since there is no chip-id * for the GENDAC. * * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values * for CLK0 f0 and f1 always returns 0x7f (but is documented "read only") */ unsigned char saveCR55, savelut[6]; int i; long clock01, clock23; saveCR55 = __svgalib_inCR(0x55); __svgalib_outbCR(0x55, saveCR55 & ~1); outb(0x3c7, 0); for (i = 0; i < 2 * 3; i++) /* save first two LUT entries */ savelut[i] = inb(0x3c9); outb(0x3c8, 0); for (i = 0; i < 2 * 3; i++) /* set first two LUT entries to zero */ outb(0x3c9, 0); __svgalib_outbCR(0x55, saveCR55 | 1); outb(0x3c7, 0); for (i = clock01 = 0; i < 4; i++) clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff); for (i = clock23 = 0; i < 4; i++) clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff); __svgalib_outbCR(0x55, saveCR55 & ~1); outb(0x3c8, 0); for (i = 0; i < 2 * 3; i++) /* restore first two LUT entries */ outb(0x3c9, savelut[i]); __svgalib_outbCR(0x55, saveCR55); if (clock01 == 0x28613d62 || (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) { inb(0x3c8); /* dactopel */ inb(0x3c6); inb(0x3c6); inb(0x3c6); /* the forth read will show the SDAC chip ID and revision */ if (((i = inb(0x3c6)) & 0xf0) == 0x70) { return 2; /* SDAC found. */ } else { return 1; /* GENDAC found. */ } inb(0x3c8); /* dactopel */ } return 0; } #endif #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) static void GENDAC_SDAC_init(void) { unsigned char val; int m, n, n1, n2, MCLK; val = __svgalib_inCR(0x55); __svgalib_outbCR(0x55, val | 0x01); outb(0x3C7, 10); /* Read MCLK. */ m = inb(0x3C9); n = inb(0x3C9); __svgalib_outbCR(0x55, val); /* Restore CR55. */ m &= 0x7f; n1 = n & 0x1f; n2 = (n >> 5) & 0x03; /* Calculate MCLK in kHz. */ MCLK = 14318 * (m + 2) / (n1 + 2) / (1 << n2); if (__svgalib_driver_report) printf("svgalib: S3-GENDAC/SDAC: MCLK = %d.%03d MHz\n", MCLK / 1000, MCLK % 1000); } #endif #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) || defined(INCLUDE_S3_TRIO64_DAC) /* * From XFree86 common_hw/S3gendac.c and S3gendac.h. * * Progaming of the S3 gendac programable clocks, from the S3 Gendac * programing documentation by S3 Inc. * Jon Tombs * * Returns nonzero if success, 0 if failure. */ #define BASE_FREQ 14.31818 /* MHz */ #define DEBUG_FINDCLOCK 0 static int S3dacsFindClock(int freq_in, int min_n2, int freq_min, int freq_max, int *best_m_out, int *best_n1_out, int *best_n2_out) { double ffreq_in, ffreq_min, ffreq_max; double ffreq_out, diff, best_diff; unsigned int m; unsigned char n1, n2; unsigned char best_n1 = 16 + 2, best_n2 = 2, best_m = 125 + 2; #if DEBUG_FINDCLOCK printf("S3dacsFindClock: Trying to match clock of %0.3f MHz\n", freq_in / 1000.0); #endif ffreq_in = freq_in / 1000.0 / BASE_FREQ; ffreq_min = freq_min / 1000.0 / BASE_FREQ; ffreq_max = freq_max / 1000.0 / BASE_FREQ; /* Check if getting freq_in is possible at all */ if (freq_in < freq_min / 8) { #if DEBUG_FINDCLOCK printf("S3dacsFindClock: %0.3f MHz is too low (lowest is %0.3f MHz)\n", freq_in / 1000.0, freq_min / 1000.0 / 8); #endif return 0; } if (freq_in > freq_max / (1 << min_n2)) { #if DEBUG_FINDCLOCK printf("S3dacsFindClock: %0.3f MHz is too high (highest is %0.3f MHz)\n", freq_in / 1000.0, freq_max / 1000.0 / (1 << min_n2)); #endif return 0; } /* work out suitable timings */ best_diff = ffreq_in; for (n2 = min_n2; n2 <= 3; n2++) { for (n1 = 1 + 2; n1 <= 31 + 2; n1++) { m = (int) (ffreq_in * n1 * (1 << n2) + 0.5); if (m < 1 + 2 || m > 127 + 2) continue; ffreq_out = (double) (m) / (double) (n1); if ((ffreq_out >= ffreq_min) && (ffreq_out <= ffreq_max)) { diff = ffreq_in - ffreq_out / (1 << n2); if (diff < 0.0) diff = -diff; if (diff < best_diff) { best_diff = diff; best_m = m; best_n1 = n1; best_n2 = n2; } } } } #if DEBUG_FINDCLOCK printf("S3dacsFindClock: clock wanted %1.6f MHz, found %1.6f MHz (m %d, n1 %d, n2 %d)\n", freq_in / 1000.0, best_m / ((double) best_n1 * (1 << best_n2)) * BASE_FREQ, best_m, best_n1, best_n2); #endif *best_m_out = best_m; *best_n1_out = best_n1; *best_n2_out = best_n2; return 1; } #endif #if defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) static int GENDAC_SDAC_match_programmable_clock(int desiredclock) { int min_m, min_n1, n2; /* Note: For ICS5342, min_n2 parameter should be one. */ if (!S3dacsFindClock(desiredclock, 0, 100000, 250000, &min_m, &min_n1, &n2)) return 0; return ((float) (min_m) / (float) (min_n1) / (1 << n2)) * BASE_FREQ * 1000; } #if 0 /* Retained for reference. */ static void setdacpll(reg, data1, data2) int reg; unsigned char data1; unsigned char data2; { unsigned char tmp, tmp1; int vgaCRIndex = vgaIOBase + 4; int vgaCRReg = vgaIOBase + 5; /* set RS2 via CR55, yuck */ tmp = __svgalib_inCR(0x55) & 0xFC; __svgalib_outCR(tmp | 0x01); tmp1 = inb(GENDAC_INDEX); outb(GENDAC_INDEX, reg); outb(GENDAC_DATA, data1); outb(GENDAC_DATA, data2); /* Now clean up our mess */ outb(GENDAC_INDEX, tmp1); __svgalib_outbCR(0x55, tmp); } #endif static void GENDAC_SDAC_initialize_clock_state(unsigned char *regs, int freq) { int min_m, min_n1, n2; int n, m; if (!S3dacsFindClock(freq, 0, 100000, 250000, &min_m, &min_n1, &n2)) { printf("Bad dot clock %0.3f MHz.\n", freq / 1000.0); return; } n = (min_n1 - 2) | (n2 << 5); m = min_m - 2; regs[SDAC_PLL_M] = m; regs[SDAC_PLL_N1_N2] = n; if (__svgalib_driver_report) printf("Initializing DAC PLL values; 0x%02X, 0x%02X.\n", m, n); } static void GENDAC_SDAC_savestate(unsigned char *regs) { unsigned char tmp; tmp = __svgalib_inCR(0x55); __svgalib_outbCR(0x55, tmp | 1); regs[SDAC_COMMAND] = inb(0x3c6); regs[SDAC_PLL_WRITEINDEX] = inb(0x3c8); /* PLL write index */ regs[SDAC_PLL_READINDEX] = inb(0x3c7); /* PLL read index */ outb(0x3c7, 2); /* index to f2 reg */ regs[SDAC_PLL_M] = inb(0x3c9); /* f2 PLL M divider */ regs[SDAC_PLL_N1_N2] = inb(0x3c9); /* f2 PLL N1/N2 divider */ outb(0x3c7, 0x0e); /* index to PLL control */ regs[SDAC_PLL_CONTROL] = inb(0x3c9); /* PLL control */ __svgalib_outbCR(0x55, tmp & ~1); } static void GENDAC_SDAC_restorestate(const unsigned char *regs) { unsigned char tmp; /* set RS2 via CR55, yuck */ tmp = __svgalib_inCR(0x55) & 0xFC; __svgalib_outbCR(0x55, tmp | 0x01); #ifdef DEBUG do { int m, n1, n2, clk; m = regs[SDAC_PLL_M] & 0x7f; n1 = regs[SDAC_PLL_N1_N2] & 0x1f; n2 = (regs[SDAC_PLL_N1_N2] & 0x60) >> 5; clk = 14318 * (m + 2) / (n1 + 2) / (1 << n2); printf("SDAC.restorestate, setting clock 0x%02X 0x%02X (%d.%3dMHz)\n", regs[SDAC_PLL_M], regs[SDAC_PLL_N1_N2], clk / 1000, clk % 1000); } while (0); #endif outb(0x3c6, regs[SDAC_COMMAND]); outb(0x3c8, 2); /* index to f2 reg */ outb(0x3c9, regs[SDAC_PLL_M]); /* f2 PLL M divider */ outb(0x3c9, regs[SDAC_PLL_N1_N2]); /* f2 PLL N1/N2 divider */ outb(0x3c8, 0x0e); /* index to PLL control */ outb(0x3c9, regs[SDAC_PLL_CONTROL]); /* PLL control */ outb(0x3c8, regs[SDAC_PLL_WRITEINDEX]); /* PLL write index */ outb(0x3c7, regs[SDAC_PLL_READINDEX]); /* PLL read index */ __svgalib_outbCR(0x55, tmp); } #endif /* defined(INCLUDE_S3_SDAC_DAC) || defined(INCLUDE_S3_GENDAC_DAC) */ /* * SDAC: 16-bit DAC, 110 MHz raw clock limit. * * The 135 MHz version supports pixel multiplexing in 8bpp modes with a * halved raw clock. (SL: at least mine doesn't.) */ #ifdef INCLUDE_S3_SDAC_DAC_TEST static int SDAC_probe(void) { return GENDAC_SDAC_probe() == 2; } #else #define SDAC_probe 0 #endif #ifdef INCLUDE_S3_SDAC_DAC static int SDAC_map_clock(int bpp, int pixelclock) { switch (bpp) { case 4: case 8: #ifdef SDAC_8BPP_PIXMUX /* SL: AFAIK it doesn't work */ if (pixelclock >= 67500) /* Use pixel multiplexing. */ return pixelclock / 2; #endif break; case 24: return pixelclock * 3 / 2; case 32: return pixelclock * 2; } return pixelclock; } static int SDAC_map_horizontal_crtc(int bpp, int pixelclock, int htiming) { switch (bpp) { case 16: return htiming * 2; case 24: return htiming * 3; case 32: return htiming * 4; } return htiming; } static void SDAC_initializestate(unsigned char *regs, int bpp, int colormode, int pixelclock) { int pixmux; /* SDAC command register. */ pixmux = 0; switch (colormode) { case CLUT8_6: #ifdef SDAC_8BPP_PIXMUX if (pixelclock >= 67500) pixmux = 0x10; #endif break; case RGB16_555: pixmux = 0x30; break; case RGB16_565: pixmux = 0x50; break; case RGB24_888_B: /* Use 0x40 for 3 VCLK/pixel. Change SDAC_map_clock and CR67 as well. */ pixmux = 0x90; break; case RGB32_888_B: pixmux = 0x70; break; } regs[SDAC_COMMAND] = pixmux; GENDAC_SDAC_initialize_clock_state(regs, SDAC_map_clock(bpp, pixelclock)); } static void SDAC_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) { dacspeed = __svgalib_setDacSpeed(dacspeed, 110000); /* most can do 135MHz. */ cardspecs->maxPixelClock4bpp = dacspeed; cardspecs->maxPixelClock8bpp = dacspeed; cardspecs->maxPixelClock16bpp = dacspeed; cardspecs->maxPixelClock24bpp = dacspeed * 2 / 3; cardspecs->maxPixelClock32bpp = dacspeed / 2; cardspecs->mapClock = SDAC_map_clock; cardspecs->matchProgrammableClock = GENDAC_SDAC_match_programmable_clock; cardspecs->mapHorizontalCrtc = SDAC_map_horizontal_crtc; cardspecs->flags |= CLOCK_PROGRAMMABLE; } DacMethods __svgalib_S3_SDAC_methods = { S3_SDAC, "S3-SDAC (86C716)", DAC_HAS_PROGRAMMABLE_CLOCKS, SDAC_probe, GENDAC_SDAC_init, SDAC_qualify_cardspecs, GENDAC_SDAC_savestate, GENDAC_SDAC_restorestate, SDAC_initializestate, SDAC_STATESIZE }; #endif /* S3-GENDAC, 8-bit DAC. */ #ifdef INCLUDE_S3_GENDAC_DAC_TEST static int GENDAC_probe(void) { return GENDAC_SDAC_probe() == 1; } #else #define GENDAC_probe 0 #endif #ifdef INCLUDE_S3_GENDAC_DAC static int GENDAC_map_clock(int bpp, int pixelclock) { if (bpp == 16) return pixelclock * 2; if (bpp == 24) return pixelclock * 3; if (bpp == 32) return pixelclock * 4; return pixelclock; } static int GENDAC_map_horizontal_crtc(int bpp, int pixelclock, int htiming) { /* XXXX Not sure. */ if (bpp == 24) return htiming * 3; if (bpp == 16) return htiming * 2; return htiming; } static void GENDAC_initializestate(unsigned char *regs, int bpp, int colormode, int pixelclock) { int daccomm; /* DAC command register. */ daccomm = 0; if (colormode == RGB16_555) daccomm = 0x20; else if (colormode == RGB16_565) daccomm = 0x60; else if (colormode == RGB24_888_B) daccomm = 0x40; regs[GENDAC_COMMAND] = daccomm; GENDAC_SDAC_initialize_clock_state(regs, GENDAC_map_clock(bpp, pixelclock)); } static void GENDAC_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) { dacspeed = __svgalib_setDacSpeed(dacspeed, 110000); cardspecs->maxPixelClock4bpp = dacspeed; cardspecs->maxPixelClock8bpp = dacspeed; cardspecs->maxPixelClock16bpp = dacspeed / 2; cardspecs->maxPixelClock24bpp = dacspeed / 3; cardspecs->maxPixelClock32bpp = 0; cardspecs->mapClock = GENDAC_map_clock; cardspecs->matchProgrammableClock = GENDAC_SDAC_match_programmable_clock; cardspecs->mapHorizontalCrtc = GENDAC_map_horizontal_crtc; cardspecs->flags |= CLOCK_PROGRAMMABLE; } DacMethods __svgalib_S3_GENDAC_methods = { S3_GENDAC, "S3-GENDAC (86C708)", DAC_HAS_PROGRAMMABLE_CLOCKS, GENDAC_probe, GENDAC_SDAC_init, GENDAC_qualify_cardspecs, GENDAC_SDAC_savestate, GENDAC_SDAC_restorestate, GENDAC_initializestate, GENDAC_STATESIZE }; #endif #ifdef INCLUDE_S3_TRIO64_DAC /* S3-Trio64, 16-bit integrated DAC. */ #define TRIO64_SR15 0 #define TRIO64_SR18 1 #define TRIO64_PLL_N1_N2 2 #define TRIO64_PLL_M 3 #define TRIO64_CR67 4 #define TRIO64_SRB 5 #define TRIO64_STATESIZE 6 /* Note: s3.c also defines CR67, but doesn't use it for the Trio64. */ extern int s3Mclk; static int Trio64_get_mclk(void) { unsigned char sr8; int m, n, n1, n2; outb(0x3c4, 0x08); sr8 = inb(0x3c5); outb(0x3c5, 0x06); outb(0x3c4, 0x11); m = inb(0x3c5); outb(0x3c4, 0x10); n = inb(0x3c5); outb(0x3c4, 0x08); outb(0x3c5, sr8); m &= 0x7f; n1 = n & 0x1f; n2 = (n >> 5) & 0x03; /* Calculate MCLK in kHz. */ return ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100; } #if 0 static void Trio64_set_mclk(int khz) /* Doesn't work. Locks computer up. Why? */ { int sr8; int min_m, min_n1, n2; if (!S3dacsFindClock(khz, 0, 40000, 70000, &min_m, &min_n1, &n2)) { printf("Bad MCLK %0.3f MHz.\n", khz / 1000.0); return; } printf("%0.3f MHz MCLK, m = %d, n = %d, r = %d\n", khz / 1000.0, min_m - 2, min_n1 - 2, n2); outb(0x3C4, 0x08); sr8 = inb(0x3C5); outb(0x3C5, 0x06); /* Unlock. */ outb(0x3c4, 0x15); outb(0x3c5, inb(0x3c5) & ~0x20); /* MCLK. */ __svgalib_outSR(0x10, (min_n1 - 2) | (n2 << 5)); __svgalib_outSR(0x11, min_m - 2); outb(0x3c4, 0x15); outb(0x3c5, inb(0x3c5) | 0x20); outb(0x3c5, inb(0x3c5) & ~0x20); __svgalib_outSR(0x08, sr8); } #endif static void Trio64_init(void) { int mclk; mclk = Trio64_get_mclk(); if (__svgalib_driver_report) printf("svgalib: RAMDAC: Trio64: MCLK = %0.3f MHz\n", mclk / 1000.0); s3Mclk = mclk; } static int Trio64_map_clock(int bpp, int pixelclock) { if (bpp == 8 && pixelclock >= 67500) /* use pixel doubling */ return pixelclock / 2; if (bpp == 24) return pixelclock * 3; return pixelclock; } static int Trio64_map_horizontal_crtc(int bpp, int pixelclock, int htiming) { if (bpp == 16) return htiming * 2; /* Normal mapping for 8bpp and 32bpp. */ return htiming; } static void Trio64_initialize_clock_state(unsigned char *regs, int freq) { int min_m, min_n1, n2; int n, m; if (!S3dacsFindClock(freq, 0, 130000, 270000, &min_m, &min_n1, &n2)) { printf("Bad dot clock %0.3f MHz.\n", freq / 1000.0); return; } n = (min_n1 - 2) | (n2 << 5); m = min_m - 2; regs[TRIO64_PLL_M] = m; regs[TRIO64_PLL_N1_N2] = n; } static int Trio64_match_programmable_clock(int desiredclock) { int min_m, min_n1, n2; if (!S3dacsFindClock(desiredclock, 0, 130000, 270000, &min_m, &min_n1, &n2)) return 0; return ((float) (min_m) / (float) (min_n1) / (1 << n2)) * BASE_FREQ * 1000; } static void Trio64_initializestate(unsigned char *regs, int bpp, int colormode, int pixelclock) { int pixmux, reserved_CR67_1; regs[TRIO64_SR15] &= ~0x50; regs[TRIO64_SR18] &= ~0x80; pixmux = 0; reserved_CR67_1 = 0; if (bpp == 8 && pixelclock >= 67500) { pixmux = 0x10; reserved_CR67_1 = 2; regs[TRIO64_SR15] |= 0x50; regs[TRIO64_SR18] |= 0x80; } else if (bpp == 16) { /* moderegs[S3_CR33] |= 0x08; *//* done in s3.c. */ if (colormode == RGB16_555) pixmux = 0x30; else pixmux = 0x50; reserved_CR67_1 = 2; } else if (colormode == RGB24_888_B) { /* remember to adjust SRB as well. */ pixmux = 0x00; } else if (colormode == RGB32_888_B) { pixmux = 0xD0; /* 32-bit color, 2 VCLKs/pixel. */ reserved_CR67_1 = 2; } regs[TRIO64_CR67] = pixmux | reserved_CR67_1; Trio64_initialize_clock_state(regs, pixelclock); } static void Trio64_savestate(unsigned char *regs) { unsigned char sr8; outb(0x3C4, 0x08); sr8 = inb(0x3C5); outb(0x3C5, 0x06); /* Unlock. */ regs[TRIO64_SR15] = __svgalib_inSR(0x15); regs[TRIO64_SR18] = __svgalib_inSR(0x18); regs[TRIO64_PLL_N1_N2] = __svgalib_inSR(0x12); regs[TRIO64_PLL_M] = __svgalib_inSR(0x13); regs[TRIO64_CR67] = __svgalib_inCR(0x67); __svgalib_outSR(0x08, sr8); } static void Trio64_restorestate(const unsigned char *regs) { unsigned char sr8, tmp; outb(0x3C4, 0x08); sr8 = inb(0x3C5); outb(0x3C5, 0x06); /* Unlock. */ __svgalib_outCR(0x67, regs[TRIO64_CR67]); __svgalib_outSR(0x15, regs[TRIO64_SR15]); __svgalib_outSR(0x18, regs[TRIO64_SR18]); /* Clock. */ __svgalib_outSR(0x12, regs[TRIO64_PLL_N1_N2]); __svgalib_outSR(0x13, regs[TRIO64_PLL_M]); #if 0 /* * XFree86 XF86_S3 (common_hw/gendac.c) has this, but it looks * incorrect, it should flip the bit by writing to 0x3c5, not * 0x3c4. */ outb(0x3c4, 0x15); tmp = inb(0x3c5); outb(0x3c4, tmp & ~0x20); outb(0x3c4, tmp | 0x20); outb(0x3c4, tmp & ~0x20); #else outb(0x3c4, 0x15); tmp = inb(0x3c5); outb(0x3c5, tmp & ~0x20); outb(0x3c5, tmp | 0x20); outb(0x3c5, tmp & ~0x20); #endif __svgalib_outSR(0x08, sr8); } static void Trio64_qualify_cardspecs(CardSpecs * cardspecs, int dacspeed) { if (dacspeed) { if (__svgalib_driver_report) printf("svgalib: using 'dacspeed' not recommended for this RAMDAC.\n"); cardspecs->maxPixelClock4bpp = dacspeed; cardspecs->maxPixelClock8bpp = 135000; cardspecs->maxPixelClock16bpp = dacspeed; cardspecs->maxPixelClock24bpp = 0; /* dacspeed / 3; *//* How to program? */ cardspecs->maxPixelClock32bpp = 50000; } else { cardspecs->maxPixelClock4bpp = 80000; cardspecs->maxPixelClock8bpp = 135000; cardspecs->maxPixelClock16bpp = 80000; cardspecs->maxPixelClock24bpp = 0; /* 25000; *//* How to program? */ cardspecs->maxPixelClock32bpp = 50000; } cardspecs->mapClock = Trio64_map_clock; cardspecs->matchProgrammableClock = Trio64_match_programmable_clock; cardspecs->mapHorizontalCrtc = Trio64_map_horizontal_crtc; cardspecs->flags |= CLOCK_PROGRAMMABLE; } DacMethods __svgalib_Trio64_methods = { TRIO64, "S3-Trio64 internal DAC", DAC_HAS_PROGRAMMABLE_CLOCKS, NULL, /* probe */ Trio64_init, Trio64_qualify_cardspecs, Trio64_savestate, Trio64_restorestate, Trio64_initializestate, TRIO64_STATESIZE }; #endif