196cdd0b9Skiyohara/* 296cdd0b9Skiyohara * Copyright (c) 2009 KIYOHARA Takashi 396cdd0b9Skiyohara * All rights reserved. 496cdd0b9Skiyohara * 596cdd0b9Skiyohara * Redistribution and use in source and binary forms, with or without 696cdd0b9Skiyohara * modification, are permitted provided that the following conditions 796cdd0b9Skiyohara * are met: 896cdd0b9Skiyohara * 1. Redistributions of source code must retain the above copyright 996cdd0b9Skiyohara * notice, this list of conditions and the following disclaimer. 1096cdd0b9Skiyohara * 2. Redistributions in binary form must reproduce the above copyright 1196cdd0b9Skiyohara * notice, this list of conditions and the following disclaimer in the 1296cdd0b9Skiyohara * documentation and/or other materials provided with the distribution. 1396cdd0b9Skiyohara * 1496cdd0b9Skiyohara * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1596cdd0b9Skiyohara * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 1696cdd0b9Skiyohara * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 1796cdd0b9Skiyohara * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 1896cdd0b9Skiyohara * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 1996cdd0b9Skiyohara * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 2096cdd0b9Skiyohara * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2196cdd0b9Skiyohara * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 2296cdd0b9Skiyohara * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 2396cdd0b9Skiyohara * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2496cdd0b9Skiyohara * POSSIBILITY OF SUCH DAMAGE. 2596cdd0b9Skiyohara * 2696cdd0b9Skiyohara */ 2796cdd0b9Skiyohara#ifdef HAVE_CONFIG_H 2896cdd0b9Skiyohara#include "config.h" 2996cdd0b9Skiyohara#endif 3096cdd0b9Skiyohara 3196cdd0b9Skiyohara#include "xf86.h" 3296cdd0b9Skiyohara#include "xf86_OSproc.h" 33a9060c92Schristos#include <X11/Xos.h> 3496cdd0b9Skiyohara 3596cdd0b9Skiyohara#include "compiler.h" 3696cdd0b9Skiyohara 3796cdd0b9Skiyohara#include "s3.h" 3896cdd0b9Skiyohara 3996cdd0b9Skiyohara#define GENDAC_INDEX 0x3C8 4096cdd0b9Skiyohara#define GENDAC_DATA 0x3C9 4196cdd0b9Skiyohara 4296cdd0b9Skiyohara 4396cdd0b9Skiyoharastatic void S3GENDACSetClock(ScrnInfoPtr, long, int, int, int, int, int, int, 4496cdd0b9Skiyohara int, long, long); 4596cdd0b9Skiyoharastatic void S3GENDACCalcClock(long, int, int, int, int, int, long, long, 4696cdd0b9Skiyohara unsigned char *, unsigned char *); 4796cdd0b9Skiyoharastatic void S3GENDACSetPLL(ScrnInfoPtr, int, unsigned char, unsigned char); 4896cdd0b9Skiyohara 4996cdd0b9Skiyohara 5096cdd0b9Skiyoharastatic void xf86dactopel(void); 5196cdd0b9Skiyohara 5296cdd0b9Skiyoharastatic void 5396cdd0b9Skiyoharaxf86dactopel() 5496cdd0b9Skiyohara{ 5596cdd0b9Skiyohara outb(0x3C8,0); 5696cdd0b9Skiyohara return; 5796cdd0b9Skiyohara} 5896cdd0b9Skiyohara 5996cdd0b9Skiyohara 6096cdd0b9SkiyoharaBool S3GENDACProbe(ScrnInfoPtr pScrn) 6196cdd0b9Skiyohara{ 6296cdd0b9Skiyohara /* probe for S3 GENDAC/SDAC */ 6396cdd0b9Skiyohara /* 6496cdd0b9Skiyohara * S3 GENDAC and SDAC have two fixed read only PLL clocks 6596cdd0b9Skiyohara * CLK0 f0: 25.255MHz M-byte 0x28 N-byte 0x61 6696cdd0b9Skiyohara * CLK0 f1: 28.311MHz M-byte 0x3d N-byte 0x62 6796cdd0b9Skiyohara * which can be used to detect GENDAC and SDAC since there is no chip-id 6896cdd0b9Skiyohara * for the GENDAC. 6996cdd0b9Skiyohara * 7096cdd0b9Skiyohara * NOTE: for the GENDAC on a MIRO 10SD (805+GENDAC) reading PLL values 7196cdd0b9Skiyohara * for CLK0 f0 and f1 always returns 0x7f 7296cdd0b9Skiyohara * (but is documented "read only") 7396cdd0b9Skiyohara */ 7496cdd0b9Skiyohara 7596cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 7696cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 7796cdd0b9Skiyohara unsigned char saveCR55, saveCR45, saveCR43, savelut[6]; 7896cdd0b9Skiyohara unsigned int i; /* don't use signed int, UW2.0 compiler bug */ 7996cdd0b9Skiyohara long clock01, clock23; 8096cdd0b9Skiyohara int found = 0; 8196cdd0b9Skiyohara 8296cdd0b9Skiyohara if (!S3_864_SERIES()) /* need? */ 8396cdd0b9Skiyohara return FALSE; 8496cdd0b9Skiyohara 8596cdd0b9Skiyohara outb(vgaCRIndex, 0x43); 8696cdd0b9Skiyohara saveCR43 = inb(vgaCRReg); 8796cdd0b9Skiyohara outb(vgaCRReg, saveCR43 & ~0x02); 8896cdd0b9Skiyohara 8996cdd0b9Skiyohara outb(vgaCRIndex, 0x45); 9096cdd0b9Skiyohara saveCR45 = inb(vgaCRReg); 9196cdd0b9Skiyohara outb(vgaCRReg, saveCR45 & ~0x20); 9296cdd0b9Skiyohara 9396cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 9496cdd0b9Skiyohara saveCR55 = inb(vgaCRReg); 9596cdd0b9Skiyohara outb(vgaCRReg, saveCR55 & ~1); 9696cdd0b9Skiyohara 9796cdd0b9Skiyohara outb(0x3c7,0); 9896cdd0b9Skiyohara for(i = 0; i < 2 * 3; i++) /* save first two LUT entries */ 9996cdd0b9Skiyohara savelut[i] = inb(0x3c9); 10096cdd0b9Skiyohara outb(0x3c8,0); 10196cdd0b9Skiyohara for(i = 0; i < 2 * 3; i++) /* set first two LUT entries to zero */ 10296cdd0b9Skiyohara outb(0x3c9, 0); 10396cdd0b9Skiyohara 10496cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 10596cdd0b9Skiyohara outb(vgaCRReg, saveCR55 | 1); 10696cdd0b9Skiyohara 10796cdd0b9Skiyohara outb(0x3c7,0); 10896cdd0b9Skiyohara for(i = clock01 = 0; i < 4; i++) 10996cdd0b9Skiyohara clock01 = (clock01 << 8) | (inb(0x3c9) & 0xff); 11096cdd0b9Skiyohara for(i = clock23 = 0; i < 4; i++) 11196cdd0b9Skiyohara clock23 = (clock23 << 8) | (inb(0x3c9) & 0xff); 11296cdd0b9Skiyohara 11396cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 11496cdd0b9Skiyohara outb(vgaCRReg, saveCR55 & ~1); 11596cdd0b9Skiyohara 11696cdd0b9Skiyohara outb(0x3c8,0); 11796cdd0b9Skiyohara for(i = 0; i < 2 * 3; i++) /* restore first two LUT entries */ 11896cdd0b9Skiyohara outb(0x3c9, savelut[i]); 11996cdd0b9Skiyohara 12096cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 12196cdd0b9Skiyohara outb(vgaCRReg, saveCR55); 12296cdd0b9Skiyohara 12396cdd0b9Skiyohara if (clock01 == 0x28613d62 || 12496cdd0b9Skiyohara (clock01 == 0x7f7f7f7f && clock23 != 0x7f7f7f7f)) { 12596cdd0b9Skiyohara xf86dactopel(); 12696cdd0b9Skiyohara inb(0x3c6); 12796cdd0b9Skiyohara inb(0x3c6); 12896cdd0b9Skiyohara inb(0x3c6); 12996cdd0b9Skiyohara 13096cdd0b9Skiyohara /* the fourth read will show the SDAC chip ID and revision */ 13196cdd0b9Skiyohara if (((i = inb(0x3c6)) & 0xf0) == 0x70) 13296cdd0b9Skiyohara found = SDAC_RAMDAC; 13396cdd0b9Skiyohara else 13496cdd0b9Skiyohara found = GENDAC_RAMDAC; 13596cdd0b9Skiyohara saveCR43 &= ~0x02; 13696cdd0b9Skiyohara saveCR45 &= ~0x20; 13796cdd0b9Skiyohara xf86dactopel(); 13896cdd0b9Skiyohara } 13996cdd0b9Skiyohara 14096cdd0b9Skiyohara outb(vgaCRIndex, 0x45); 14196cdd0b9Skiyohara outb(vgaCRReg, saveCR45); 14296cdd0b9Skiyohara 14396cdd0b9Skiyohara outb(vgaCRIndex, 0x43); 14496cdd0b9Skiyohara outb(vgaCRReg, saveCR43); 14596cdd0b9Skiyohara 14696cdd0b9Skiyohara if (found) { 14796cdd0b9Skiyohara RamDacInit(pScrn, pS3->RamDacRec); 14896cdd0b9Skiyohara pS3->RamDac = RamDacHelperCreateInfoRec(); 14996cdd0b9Skiyohara pS3->RamDac->RamDacType = found; 15096cdd0b9Skiyohara return TRUE; 15196cdd0b9Skiyohara } 15296cdd0b9Skiyohara return FALSE; 15396cdd0b9Skiyohara} 15496cdd0b9Skiyohara 15596cdd0b9Skiyoharavoid S3GENDAC_PreInit(ScrnInfoPtr pScrn) 15696cdd0b9Skiyohara{ 15796cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 15896cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 15996cdd0b9Skiyohara unsigned char saveCR55; 16096cdd0b9Skiyohara int m, n, n1, n2, mclk; 16196cdd0b9Skiyohara 16296cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 16396cdd0b9Skiyohara saveCR55 = inb(vgaCRReg); 16496cdd0b9Skiyohara outb(vgaCRReg, saveCR55 | 1); 16596cdd0b9Skiyohara 16696cdd0b9Skiyohara outb(0x3C7, 10); /* read MCLK */ 16796cdd0b9Skiyohara m = inb(0x3C9); 16896cdd0b9Skiyohara n = inb(0x3C9); 16996cdd0b9Skiyohara 17096cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 17196cdd0b9Skiyohara outb(vgaCRReg, saveCR55); 17296cdd0b9Skiyohara 17396cdd0b9Skiyohara m &= 0x7f; 17496cdd0b9Skiyohara n1 = n & 0x1f; 17596cdd0b9Skiyohara n2 = (n >> 5) & 0x03; 17696cdd0b9Skiyohara mclk = ((1431818 * (m + 2)) / (n1 + 2) / (1 << n2) + 50) / 100; 17796cdd0b9Skiyohara 17896cdd0b9Skiyohara pS3->mclk = mclk; 17996cdd0b9Skiyohara xf86DrvMsg(pScrn->scrnIndex, X_PROBED, "MCLK %1.3f MHz\n", 18096cdd0b9Skiyohara mclk / 1000.0); 18196cdd0b9Skiyohara} 18296cdd0b9Skiyohara 18396cdd0b9Skiyoharavoid S3GENDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode) 18496cdd0b9Skiyohara{ 18596cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 18696cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 18796cdd0b9Skiyohara int daccomm = 0; /* GENDAC command */ 18896cdd0b9Skiyohara unsigned char blank, tmp; 18996cdd0b9Skiyohara 19096cdd0b9Skiyohara S3GENDACSetClock(pScrn, mode->Clock * (pScrn->depth >> 3), 2, 1, 1, 31, 19196cdd0b9Skiyohara 0, 3, 1, 100000, 250000); 19296cdd0b9Skiyohara 19396cdd0b9Skiyohara outb(0x3C4, 1); 19496cdd0b9Skiyohara blank = inb(0x3C5); 19596cdd0b9Skiyohara outb(0x3C5, blank | 0x20); /* blank the screen */ 19696cdd0b9Skiyohara 19796cdd0b9Skiyohara switch (pScrn->depth) 19896cdd0b9Skiyohara { 19996cdd0b9Skiyohara case 8: /* 8-bit color, 1VCLK/pixel */ 20096cdd0b9Skiyohara break; 20196cdd0b9Skiyohara 20296cdd0b9Skiyohara case 15: /* 15-bit color, 2VCLK/pixel */ 20396cdd0b9Skiyohara daccomm = 0x20; 20496cdd0b9Skiyohara break; 20596cdd0b9Skiyohara 20696cdd0b9Skiyohara case 16: /* 16-bit color, 2VCLK/pixel */ 20796cdd0b9Skiyohara daccomm = 0x60; 20896cdd0b9Skiyohara break; 20996cdd0b9Skiyohara 21096cdd0b9Skiyohara case 32: /* 32-bit color, 3VCLK/pixel */ 21196cdd0b9Skiyohara daccomm = 0x40; 21296cdd0b9Skiyohara } 21396cdd0b9Skiyohara 21496cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 21596cdd0b9Skiyohara tmp = inb(vgaCRReg) | 1; 21696cdd0b9Skiyohara outb(vgaCRReg, tmp); 21796cdd0b9Skiyohara 21896cdd0b9Skiyohara outb(0x3c6, daccomm); /* set GENDAC mux mode */ 21996cdd0b9Skiyohara 22096cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 22196cdd0b9Skiyohara tmp = inb(vgaCRReg) & ~1; 22296cdd0b9Skiyohara outb(vgaCRReg, tmp); 22396cdd0b9Skiyohara 22496cdd0b9Skiyohara outb(0x3C4, 1); 22596cdd0b9Skiyohara outb(0x3C5, blank); /* unblank the screen */ 22696cdd0b9Skiyohara} 22796cdd0b9Skiyohara 22896cdd0b9Skiyoharavoid S3SDAC_Init(ScrnInfoPtr pScrn, DisplayModePtr mode) 22996cdd0b9Skiyohara{ 23096cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 23196cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 23296cdd0b9Skiyohara int pixmux = 0; /* SDAC command and CR67 */ 23396cdd0b9Skiyohara int blank_delay = 0; /* CR6D */ 23496cdd0b9Skiyohara int invert_vclk = 0; /* CR66 bit 0 */ 23596cdd0b9Skiyohara unsigned char blank, tmp; 23696cdd0b9Skiyohara 23796cdd0b9Skiyohara#if 0 23896cdd0b9Skiyohara S3GENDACSetClock(pScrn, 23996cdd0b9Skiyohara (pScrn->depth == 32) ? mode->Clock * 2 : mode->Clock, 24096cdd0b9Skiyohara 2, 1, 1, 31, 0, 3, 1, 100000, 250000); 24196cdd0b9Skiyohara#else 24296cdd0b9Skiyohara /* XXXX: for prep */ 24396cdd0b9Skiyohara long freq; 24496cdd0b9Skiyohara 24596cdd0b9Skiyohara switch (pScrn->depth) { 24696cdd0b9Skiyohara case 32: 24796cdd0b9Skiyohara freq = mode->Clock * 2; /* XXXX: frem xfree86 3.x */ 24896cdd0b9Skiyohara break; 24996cdd0b9Skiyohara case 16: 25096cdd0b9Skiyohara freq = mode->Clock / 2; 25196cdd0b9Skiyohara break; 25296cdd0b9Skiyohara default: 25396cdd0b9Skiyohara freq = mode->Clock; 25496cdd0b9Skiyohara break; 25596cdd0b9Skiyohara } 25696cdd0b9Skiyohara S3GENDACSetClock(pScrn, freq, 25796cdd0b9Skiyohara 2, 1, 1, 31, 0, 3, 1, 100000, 250000); 25896cdd0b9Skiyohara#endif 25996cdd0b9Skiyohara 26096cdd0b9Skiyohara outb(vgaCRIndex, 0x42);/* select the clock */ 26196cdd0b9Skiyohara tmp = inb(vgaCRReg) & 0xf0; 26296cdd0b9Skiyohara outb(vgaCRReg, tmp | 0x02); 26396cdd0b9Skiyohara usleep(150000); 26496cdd0b9Skiyohara 26596cdd0b9Skiyohara outb(0x3C4, 1); 26696cdd0b9Skiyohara blank = inb(0x3C5); 26796cdd0b9Skiyohara outb(0x3C5, blank | 0x20); /* blank the screen */ 26896cdd0b9Skiyohara 26996cdd0b9Skiyohara switch (pScrn->depth) 27096cdd0b9Skiyohara { 27196cdd0b9Skiyohara case 8: /* 8-bit color, 1 VCLK/pixel */ 27296cdd0b9Skiyohara pixmux = 0x10; 27396cdd0b9Skiyohara blank_delay = 2; 27496cdd0b9Skiyohara invert_vclk = 1; 27596cdd0b9Skiyohara break; 27696cdd0b9Skiyohara 27796cdd0b9Skiyohara case 15: /* 15-bit color, 1VCLK/pixel */ 27896cdd0b9Skiyohara pixmux = 0x30; 27996cdd0b9Skiyohara blank_delay = 2; 28096cdd0b9Skiyohara break; 28196cdd0b9Skiyohara 28296cdd0b9Skiyohara case 16: /* 16-bit color, 1VCLK/pixel */ 28396cdd0b9Skiyohara pixmux = 0x50; 28496cdd0b9Skiyohara blank_delay = 2; 28596cdd0b9Skiyohara break; 28696cdd0b9Skiyohara 28796cdd0b9Skiyohara case 32: /* 32-bit color, 2VCLK/pixel */ 28896cdd0b9Skiyohara pixmux = 0x70; 28996cdd0b9Skiyohara blank_delay = 2; 29096cdd0b9Skiyohara } 29196cdd0b9Skiyohara 29296cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 29396cdd0b9Skiyohara tmp = inb(vgaCRReg) | 1; 29496cdd0b9Skiyohara outb(vgaCRReg, tmp); 29596cdd0b9Skiyohara 29696cdd0b9Skiyohara outb(vgaCRIndex, 0x67); 29796cdd0b9Skiyohara outb(vgaCRReg, pixmux | invert_vclk); /* set S3 mux mode */ 29896cdd0b9Skiyohara outb(0x3c6, pixmux); /* set SDAC mux mode */ 29996cdd0b9Skiyohara 30096cdd0b9Skiyohara outb(vgaCRIndex, 0x6D); 30196cdd0b9Skiyohara outb(vgaCRReg, blank_delay); /* set blank delay */ 30296cdd0b9Skiyohara 30396cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 30496cdd0b9Skiyohara tmp = inb(vgaCRReg) & ~1; 30596cdd0b9Skiyohara outb(vgaCRReg, tmp); 30696cdd0b9Skiyohara 30796cdd0b9Skiyohara outb(0x3C4, 1); 30896cdd0b9Skiyohara outb(0x3C5, blank); /* unblank the screen */ 30996cdd0b9Skiyohara} 31096cdd0b9Skiyohara 31196cdd0b9Skiyoharavoid S3GENDAC_Save(ScrnInfoPtr pScrn) 31296cdd0b9Skiyohara{ 31396cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 31496cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 31596cdd0b9Skiyohara S3RegPtr save = &pS3->SavedRegs; 31696cdd0b9Skiyohara unsigned char tmp; 31796cdd0b9Skiyohara 31896cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 31996cdd0b9Skiyohara tmp = inb(vgaCRReg); 32096cdd0b9Skiyohara outb(vgaCRReg, tmp | 1); 32196cdd0b9Skiyohara 32296cdd0b9Skiyohara save->dacregs[0] = inb(0x3c6); /* Enhanced command register */ 32396cdd0b9Skiyohara save->dacregs[2] = inb(0x3c8); /* PLL write index */ 32496cdd0b9Skiyohara save->dacregs[1] = inb(0x3c7); /* PLL read index */ 32596cdd0b9Skiyohara outb(0x3c7, 2); /* index to f2 reg */ 32696cdd0b9Skiyohara save->dacregs[3] = inb(0x3c9); /* f2 PLL M divider */ 32796cdd0b9Skiyohara save->dacregs[4] = inb(0x3c9); /* f2 PLL N1/N2 divider */ 32896cdd0b9Skiyohara outb(0x3c7, 0x0e); /* index to PLL control */ 32996cdd0b9Skiyohara save->dacregs[5] = inb(0x3c9); /* PLL control */ 33096cdd0b9Skiyohara 33196cdd0b9Skiyohara outb(vgaCRReg, tmp & ~1); 33296cdd0b9Skiyohara} 33396cdd0b9Skiyohara 33496cdd0b9Skiyoharavoid S3GENDAC_Restore(ScrnInfoPtr pScrn) 33596cdd0b9Skiyohara{ 33696cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 33796cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 33896cdd0b9Skiyohara S3RegPtr restore = &pS3->SavedRegs; 33996cdd0b9Skiyohara unsigned char tmp; 34096cdd0b9Skiyohara 34196cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 34296cdd0b9Skiyohara tmp = inb(vgaCRReg); 34396cdd0b9Skiyohara outb(vgaCRReg, tmp | 1); 34496cdd0b9Skiyohara 34596cdd0b9Skiyohara outb(0x3c6, restore->dacregs[0]); /* Enhanced command register */ 34696cdd0b9Skiyohara outb(0x3c8, 2); /* index to f2 reg */ 34796cdd0b9Skiyohara outb(0x3c9, restore->dacregs[3]); /* f2 PLL M divider */ 34896cdd0b9Skiyohara outb(0x3c9, restore->dacregs[4]); /* f2 PLL N1/N2 divider */ 34996cdd0b9Skiyohara outb(0x3c8, 0x0e); /* index to PLL control */ 35096cdd0b9Skiyohara outb(0x3c9, restore->dacregs[5]); /* PLL control */ 35196cdd0b9Skiyohara outb(0x3c8, restore->dacregs[2]); /* PLL write index */ 35296cdd0b9Skiyohara outb(0x3c7, restore->dacregs[1]); /* PLL read index */ 35396cdd0b9Skiyohara 35496cdd0b9Skiyohara outb(vgaCRReg, tmp & ~1); 35596cdd0b9Skiyohara} 35696cdd0b9Skiyohara 35796cdd0b9Skiyoharastatic void 35896cdd0b9SkiyoharaS3GENDACSetClock(ScrnInfoPtr pScrn, long freq, int clk, int min_m, int min_n1, 35996cdd0b9Skiyohara int max_n1, int min_n2, int max_n2, int pll_type, long freq_min, 36096cdd0b9Skiyohara long freq_max) 36196cdd0b9Skiyohara{ 36296cdd0b9Skiyohara unsigned char m, n; 36396cdd0b9Skiyohara 36496cdd0b9Skiyohara S3GENDACCalcClock(freq, min_m, min_n1, max_n1, min_n2, max_n2, 36596cdd0b9Skiyohara freq_min, freq_max, &m, &n); 36696cdd0b9Skiyohara 36796cdd0b9Skiyohara /* XXX for pll_type == GENDAC */ 36896cdd0b9Skiyohara S3GENDACSetPLL(pScrn, clk, m, n); 36996cdd0b9Skiyohara} 37096cdd0b9Skiyohara 37196cdd0b9Skiyohara/* This function is copy from S3GENDACCalcClock() */ 37296cdd0b9Skiyoharastatic void 37396cdd0b9SkiyoharaS3GENDACCalcClock(long freq, int min_m, int min_n1, int max_n1, int min_n2, 37496cdd0b9Skiyohara int max_n2, long freq_min, long freq_max, 37596cdd0b9Skiyohara unsigned char *mdiv, unsigned char *ndiv) 37696cdd0b9Skiyohara{ 37796cdd0b9Skiyohara double ffreq, ffreq_min, ffreq_max; 37896cdd0b9Skiyohara double div, diff, best_diff; 37996cdd0b9Skiyohara unsigned int m; 38096cdd0b9Skiyohara unsigned char n1, n2, best_n1=18, best_n2=2, best_m=127; 38196cdd0b9Skiyohara 38296cdd0b9Skiyohara#define BASE_FREQ 14.31818 38396cdd0b9Skiyohara ffreq = freq / 1000.0 / BASE_FREQ; 38496cdd0b9Skiyohara ffreq_min = freq_min / 1000.0 / BASE_FREQ; 38596cdd0b9Skiyohara ffreq_max = freq_max / 1000.0 / BASE_FREQ; 38696cdd0b9Skiyohara 38796cdd0b9Skiyohara if (ffreq < ffreq_min / (1 << max_n2)) { 38896cdd0b9Skiyohara ErrorF("invalid frequency %1.3f Mhz [freq >= %1.3f Mhz]\n", 38996cdd0b9Skiyohara ffreq*BASE_FREQ, ffreq_min*BASE_FREQ / (1 << max_n2)); 39096cdd0b9Skiyohara ffreq = ffreq_min / (1 << max_n2); 39196cdd0b9Skiyohara } 39296cdd0b9Skiyohara if (ffreq > ffreq_max / (1 << min_n2)) { 39396cdd0b9Skiyohara ErrorF("invalid frequency %1.3f Mhz [freq <= %1.3f Mhz]\n", 39496cdd0b9Skiyohara ffreq*BASE_FREQ, ffreq_max*BASE_FREQ / (1 << min_n2)); 39596cdd0b9Skiyohara ffreq = ffreq_max / (1<<min_n2); 39696cdd0b9Skiyohara } 39796cdd0b9Skiyohara 39896cdd0b9Skiyohara /* work out suitable timings */ 39996cdd0b9Skiyohara 40096cdd0b9Skiyohara best_diff = ffreq; 40196cdd0b9Skiyohara 40296cdd0b9Skiyohara for (n2 = min_n2; n2 <= max_n2; n2++) { 40396cdd0b9Skiyohara for (n1 = min_n1 + 2; n1 <= max_n1 + 2; n1++) { 40496cdd0b9Skiyohara m = (int)(ffreq * n1 * (1 << n2) + 0.5); 40596cdd0b9Skiyohara if (m < min_m + 2 || m > 127 + 2) 40696cdd0b9Skiyohara continue; 40796cdd0b9Skiyohara div = (double)(m) / (double)(n1); 40896cdd0b9Skiyohara if ((div >= ffreq_min) && (div <= ffreq_max)) { 40996cdd0b9Skiyohara diff = ffreq - div / (1 << n2); 41096cdd0b9Skiyohara if (diff < 0.0) 41196cdd0b9Skiyohara diff = -diff; 41296cdd0b9Skiyohara if (diff < best_diff) { 41396cdd0b9Skiyohara best_diff = diff; 41496cdd0b9Skiyohara best_m = m; 41596cdd0b9Skiyohara best_n1 = n1; 41696cdd0b9Skiyohara best_n2 = n2; 41796cdd0b9Skiyohara } 41896cdd0b9Skiyohara } 41996cdd0b9Skiyohara } 42096cdd0b9Skiyohara } 42196cdd0b9Skiyohara 42296cdd0b9Skiyohara if (max_n1 == 63) 42396cdd0b9Skiyohara *ndiv = (best_n1 - 2) | (best_n2 << 6); 42496cdd0b9Skiyohara else 42596cdd0b9Skiyohara *ndiv = (best_n1 - 2) | (best_n2 << 5); 42696cdd0b9Skiyohara *mdiv = best_m - 2; 42796cdd0b9Skiyohara} 42896cdd0b9Skiyohara 42996cdd0b9Skiyoharastatic void 43096cdd0b9SkiyoharaS3GENDACSetPLL(ScrnInfoPtr pScrn, int clk, unsigned char m, unsigned char n) 43196cdd0b9Skiyohara{ 43296cdd0b9Skiyohara S3Ptr pS3 = S3PTR(pScrn); 43396cdd0b9Skiyohara unsigned char tmp, tmp1; 43496cdd0b9Skiyohara int vgaCRIndex = pS3->vgaCRIndex, vgaCRReg = pS3->vgaCRReg; 43596cdd0b9Skiyohara 43696cdd0b9Skiyohara /* set RS2 via CR55, yuck */ 43796cdd0b9Skiyohara outb(vgaCRIndex, 0x55); 43896cdd0b9Skiyohara tmp = inb(vgaCRReg) & 0xFC; 43996cdd0b9Skiyohara outb(vgaCRReg, tmp | 0x01); 44096cdd0b9Skiyohara tmp1 = inb(GENDAC_INDEX); 44196cdd0b9Skiyohara 44296cdd0b9Skiyohara outb(GENDAC_INDEX, clk); 44396cdd0b9Skiyohara outb(GENDAC_DATA, m); 44496cdd0b9Skiyohara outb(GENDAC_DATA, n); 44596cdd0b9Skiyohara 44696cdd0b9Skiyohara /* Now clean up our mess */ 44796cdd0b9Skiyohara outb(GENDAC_INDEX, tmp1); 44896cdd0b9Skiyohara outb(vgaCRReg, tmp); 44996cdd0b9Skiyohara} 450