172b676d7Smrg/*
272b676d7Smrg * DAC helper functions (Save/Restore, MemClk, etc)
372b676d7Smrg *
472b676d7Smrg * Copyright (C) 2001-2005 by Thomas Winischhofer, Vienna, Austria.
572b676d7Smrg *
672b676d7Smrg * Redistribution and use in source and binary forms, with or without
772b676d7Smrg * modification, are permitted provided that the following conditions
872b676d7Smrg * are met:
972b676d7Smrg * 1) Redistributions of source code must retain the above copyright
1072b676d7Smrg *    notice, this list of conditions and the following disclaimer.
1172b676d7Smrg * 2) Redistributions in binary form must reproduce the above copyright
1272b676d7Smrg *    notice, this list of conditions and the following disclaimer in the
1372b676d7Smrg *    documentation and/or other materials provided with the distribution.
1472b676d7Smrg * 3) The name of the author may not be used to endorse or promote products
1572b676d7Smrg *    derived from this software without specific prior written permission.
1672b676d7Smrg *
1772b676d7Smrg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1872b676d7Smrg * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1972b676d7Smrg * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2072b676d7Smrg * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2172b676d7Smrg * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2272b676d7Smrg * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2372b676d7Smrg * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2472b676d7Smrg * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2572b676d7Smrg * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2672b676d7Smrg * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2772b676d7Smrg *
2872b676d7Smrg * Author:  	Thomas Winischhofer <thomas@winischhofer.net>
2972b676d7Smrg *
3072b676d7Smrg * --------------------------------------------------------------------------
3172b676d7Smrg *
3272b676d7Smrg * SiS_compute_vclk(), SiSCalcClock() and parts of SiSMclk():
3372b676d7Smrg *
3472b676d7Smrg * Copyright (C) 1998, 1999 by Alan Hourihane, Wigan, England
3572b676d7Smrg * Written by:
3672b676d7Smrg *	 Alan Hourihane <alanh@fairlite.demon.co.uk>,
3772b676d7Smrg *       Mike Chapman <mike@paranoia.com>,
3872b676d7Smrg *       Juanjo Santamarta <santamarta@ctv.es>,
3972b676d7Smrg *       Mitani Hiroshi <hmitani@drl.mei.co.jp>,
4072b676d7Smrg *       David Thomas <davtom@dream.org.uk>,
4172b676d7Smrg *	 Thomas Winischhofer <thomas@winischhofer.net>.
4272b676d7Smrg *
4372b676d7Smrg * Licensed under the following terms:
4472b676d7Smrg *
4572b676d7Smrg * Permission to use, copy, modify, distribute, and sell this software and its
4672b676d7Smrg * documentation for any purpose is hereby granted without fee, provided that
4772b676d7Smrg * the above copyright notice appears in all copies and that both that copyright
4872b676d7Smrg * notice and this permission notice appear in supporting documentation, and
4972b676d7Smrg * and that the name of the copyright holder not be used in advertising
5072b676d7Smrg * or publicity pertaining to distribution of the software without specific,
5172b676d7Smrg * written prior permission. The copyright holder makes no representations
5272b676d7Smrg * about the suitability of this software for any purpose.  It is provided
5372b676d7Smrg * "as is" without expressed or implied warranty.
5472b676d7Smrg *
5572b676d7Smrg * THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
5672b676d7Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
5772b676d7Smrg * EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
5872b676d7Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
5972b676d7Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
6072b676d7Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
6172b676d7Smrg * PERFORMANCE OF THIS SOFTWARE.
6272b676d7Smrg */
6372b676d7Smrg
6472b676d7Smrg#ifdef HAVE_CONFIG_H
6572b676d7Smrg#include "config.h"
6672b676d7Smrg#endif
6772b676d7Smrg
6872b676d7Smrg#include "sis.h"
6972b676d7Smrg#define SIS_NEED_inSISREG
7072b676d7Smrg#define SIS_NEED_inSISREGW
7172b676d7Smrg#define SIS_NEED_inSISREGL
7272b676d7Smrg#define SIS_NEED_outSISREG
7372b676d7Smrg#define SIS_NEED_outSISREGW
7472b676d7Smrg#define SIS_NEED_outSISREGL
7572b676d7Smrg#define SIS_NEED_inSISIDXREG
7672b676d7Smrg#define SIS_NEED_outSISIDXREG
7772b676d7Smrg#define SIS_NEED_orSISIDXREG
7872b676d7Smrg#define SIS_NEED_andSISIDXREG
7972b676d7Smrg#define SIS_NEED_MYMMIO
8072b676d7Smrg#include "sis_regs.h"
8172b676d7Smrg#include "sis_dac.h"
8272b676d7Smrg
8372b676d7Smrgstatic void SiSSave(ScrnInfoPtr pScrn, SISRegPtr sisReg);
8472b676d7Smrgstatic void SiSRestore(ScrnInfoPtr pScrn, SISRegPtr sisReg);
8572b676d7Smrgstatic void SiS300Save(ScrnInfoPtr pScrn, SISRegPtr sisReg);
8672b676d7Smrgstatic void SiS300Restore(ScrnInfoPtr pScrn, SISRegPtr sisReg);
8772b676d7Smrgstatic void SiS315Save(ScrnInfoPtr pScrn, SISRegPtr sisReg);
8872b676d7Smrgstatic void SiS315Restore(ScrnInfoPtr pScrn, SISRegPtr sisReg);
8972b676d7Smrgstatic void SiS301Save(ScrnInfoPtr pScrn, SISRegPtr sisReg);
9072b676d7Smrgstatic void SiS301BSave(ScrnInfoPtr pScrn, SISRegPtr sisReg);
9172b676d7Smrgstatic void SiSLVDSChrontelSave(ScrnInfoPtr pScrn, SISRegPtr sisReg);
9272b676d7Smrgstatic void SiS301Restore(ScrnInfoPtr pScrn, SISRegPtr sisReg);
9372b676d7Smrgstatic void SiS301BRestore(ScrnInfoPtr pScrn, SISRegPtr sisReg);
9472b676d7Smrgstatic void SiSLVDSChrontelRestore(ScrnInfoPtr pScrn, SISRegPtr sisReg);
9572b676d7Smrgstatic void SiS301LoadPalette(ScrnInfoPtr pScrn, int numColors,
9672b676d7Smrg                      int *indicies, LOCO *colors, int myshift);
9772b676d7Smrgstatic void SetBlock(CARD16 port, CARD8 from, CARD8 to, CARD8 *DataPtr);
9872b676d7Smrg
9972b676d7SmrgUChar       SiSGetCopyROP(int rop);
10072b676d7SmrgUChar       SiSGetPatternROP(int rop);
10172b676d7Smrg
10272b676d7Smrgstatic const UShort ch700xidx[] = {
10372b676d7Smrg      0x00,0x07,0x08,0x0a,0x0b,0x04,0x09,0x20,0x21,0x18,0x19,0x1a,
10472b676d7Smrg      0x1b,0x1c,0x1d,0x1e,0x1f,  /* 0x0e,  - Don't save the power register */
10572b676d7Smrg      0x01,0x03,0x06,0x0d,0x11,0x13,0x14,0x15,0x17,0x22,0x23,0x24
10672b676d7Smrg   };
10772b676d7Smrg
10872b676d7Smrgstatic const UShort ch701xidx[] = {
10972b676d7Smrg      0x1c,0x5f,0x64,0x6f,0x70,0x71,0x72,0x73,0x74,0x76,0x78,0x7d,
11072b676d7Smrg      0x67,0x68,0x69,0x6a,0x6b,0x1e,0x00,0x01,0x02,0x04,0x03,0x05,
11172b676d7Smrg      0x06,0x07,0x08,0x15,0x1f,0x0c,0x0d,0x0e,0x0f,0x10,0x66
11272b676d7Smrg   };
11372b676d7Smrg
11472b676d7Smrgint SiS_compute_vclk(
11572b676d7Smrg        int Clock,
11672b676d7Smrg        int *out_n,
11772b676d7Smrg        int *out_dn,
11872b676d7Smrg        int *out_div,
11972b676d7Smrg        int *out_sbit,
12072b676d7Smrg        int *out_scale)
12172b676d7Smrg{
12272b676d7Smrg    float f,x,y,t, error, min_error;
12372b676d7Smrg    int n, dn, best_n=0, best_dn=0;
12472b676d7Smrg
12572b676d7Smrg    /*
12672b676d7Smrg     * Rules
12772b676d7Smrg     *
12872b676d7Smrg     * VCLK = 14.318 * (Divider/Post Scalar) * (Numerator/DeNumerator)
12972b676d7Smrg     * Factor = (Divider/Post Scalar)
13072b676d7Smrg     * Divider is 1 or 2
13172b676d7Smrg     * Post Scalar is 1, 2, 3, 4, 6 or 8
13272b676d7Smrg     * Numberator ranged from 1 to 128
13372b676d7Smrg     * DeNumerator ranged from 1 to 32
13472b676d7Smrg     * a. VCO = VCLK/Factor, suggest range is 150 to 250 Mhz
13572b676d7Smrg     * b. Post Scalar selected from 1, 2, 4 or 8 first.
13672b676d7Smrg     * c. DeNumerator selected from 2.
13772b676d7Smrg     *
13872b676d7Smrg     * According to rule a and b, the VCO ranges that can be scaled by
13972b676d7Smrg     * rule b are:
14072b676d7Smrg     *      150    - 250    (Factor = 1)
14172b676d7Smrg     *       75    - 125    (Factor = 2)
14272b676d7Smrg     *       37.5  -  62.5  (Factor = 4)
14372b676d7Smrg     *       18.75 -  31.25 (Factor = 8)
14472b676d7Smrg     *
14572b676d7Smrg     * The following ranges use Post Scalar 3 or 6:
14672b676d7Smrg     *      125    - 150    (Factor = 1.5)
14772b676d7Smrg     *       62.5  -  75    (Factor = 3)
14872b676d7Smrg     *       31.25 -  37.5  (Factor = 6)
14972b676d7Smrg     *
15072b676d7Smrg     * Steps:
15172b676d7Smrg     * 1. divide the Clock by 2 until the Clock is less or equal to 31.25.
15272b676d7Smrg     * 2. if the divided Clock is range from 18.25 to 31.25, than
15372b676d7Smrg     *    the Factor is 1, 2, 4 or 8.
15472b676d7Smrg     * 3. if the divided Clock is range from 15.625 to 18.25, than
15572b676d7Smrg     *    the Factor is 1.5, 3 or 6.
15672b676d7Smrg     * 4. select the Numberator and DeNumberator with minimum deviation.
15772b676d7Smrg     *
15872b676d7Smrg     * ** this function can select VCLK ranged from 18.75 to 250 Mhz
15972b676d7Smrg     */
16072b676d7Smrg
16172b676d7Smrg    f = (float) Clock;
16272b676d7Smrg    f /= 1000.0;
16372b676d7Smrg    if((f > 250.0) || (f < 18.75))
16472b676d7Smrg       return 0;
16572b676d7Smrg
16672b676d7Smrg    min_error = f;
16772b676d7Smrg    y = 1.0;
16872b676d7Smrg    x = f;
16972b676d7Smrg    while(x > 31.25) {
17072b676d7Smrg       y *= 2.0;
17172b676d7Smrg       x /= 2.0;
17272b676d7Smrg    }
17372b676d7Smrg    if(x >= 18.25) {
17472b676d7Smrg       x *= 8.0;
17572b676d7Smrg       y = 8.0 / y;
17672b676d7Smrg    } else if(x >= 15.625) {
17772b676d7Smrg       x *= 12.0;
17872b676d7Smrg       y = 12.0 / y;
17972b676d7Smrg    }
18072b676d7Smrg
18172b676d7Smrg    t = y;
18272b676d7Smrg    if(t == (float) 1.5) {
18372b676d7Smrg       *out_div = 2;
18472b676d7Smrg       t *= 2.0;
18572b676d7Smrg    } else {
18672b676d7Smrg       *out_div = 1;
18772b676d7Smrg    }
18872b676d7Smrg    if(t > (float) 4.0) {
18972b676d7Smrg       *out_sbit = 1;
19072b676d7Smrg       t /= 2.0;
19172b676d7Smrg    } else {
19272b676d7Smrg       *out_sbit = 0;
19372b676d7Smrg    }
19472b676d7Smrg
19572b676d7Smrg    *out_scale = (int) t;
19672b676d7Smrg
19772b676d7Smrg    for(dn = 2; dn <= 32; dn++) {
19872b676d7Smrg       for(n = 1; n <= 128; n++) {
19972b676d7Smrg          error = x;
20072b676d7Smrg          error -= ((float) 14.318 * (float) n / (float) dn);
20172b676d7Smrg          if(error < (float) 0)
20272b676d7Smrg             error = -error;
20372b676d7Smrg          if(error < min_error) {
20472b676d7Smrg             min_error = error;
20572b676d7Smrg             best_n = n;
20672b676d7Smrg             best_dn = dn;
20772b676d7Smrg          }
20872b676d7Smrg       }
20972b676d7Smrg    }
21072b676d7Smrg    *out_n = best_n;
21172b676d7Smrg    *out_dn = best_dn;
21272b676d7Smrg    PDEBUG(ErrorF("SiS_compute_vclk: Clock=%d, n=%d, dn=%d, div=%d, sbit=%d,"
21372b676d7Smrg                    " scale=%d\n", Clock, best_n, best_dn, *out_div,
21472b676d7Smrg                    *out_sbit, *out_scale));
21572b676d7Smrg    return 1;
21672b676d7Smrg}
21772b676d7Smrg
21872b676d7Smrgvoid
21972b676d7SmrgSiSCalcClock(ScrnInfoPtr pScrn, int clock, int max_VLD, unsigned int *vclk)
22072b676d7Smrg{
22172b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
22272b676d7Smrg    int M, N, P , PSN, VLD , PSNx ;
22372b676d7Smrg    int bestM=0, bestN=0, bestP=0, bestPSN=0, bestVLD=0;
22472b676d7Smrg    double abest = 42.0;
22572b676d7Smrg    double target;
22672b676d7Smrg    double Fvco, Fout;
22772b676d7Smrg    double error, aerror;
22872b676d7Smrg
22972b676d7Smrg    /*
23072b676d7Smrg     *  fd = fref*(Numerator/Denumerator)*(Divider/PostScaler)
23172b676d7Smrg     *
23272b676d7Smrg     *  M       = Numerator [1:128]
23372b676d7Smrg     *  N       = DeNumerator [1:32]
23472b676d7Smrg     *  VLD     = Divider (Vco Loop Divider) : divide by 1, 2
23572b676d7Smrg     *  P       = Post Scaler : divide by 1, 2, 3, 4
23672b676d7Smrg     *  PSN     = Pre Scaler (Reference Divisor Select)
23772b676d7Smrg     *
23872b676d7Smrg     * result in vclk[]
23972b676d7Smrg     */
24072b676d7Smrg#define Midx    0
24172b676d7Smrg#define Nidx    1
24272b676d7Smrg#define VLDidx  2
24372b676d7Smrg#define Pidx    3
24472b676d7Smrg#define PSNidx  4
24572b676d7Smrg#define Fref 14318180
24672b676d7Smrg/* stability constraints for internal VCO -- MAX_VCO also determines
24772b676d7Smrg * the maximum Video pixel clock */
24872b676d7Smrg#define MIN_VCO      Fref
24972b676d7Smrg#define MAX_VCO      135000000
25072b676d7Smrg#define MAX_VCO_5597 353000000
25172b676d7Smrg#define MAX_PSN      0          /* no pre scaler for this chip */
25272b676d7Smrg#define TOLERANCE    0.01       /* search smallest M and N in this tolerance */
25372b676d7Smrg
25472b676d7Smrg  int M_min = 2;
25572b676d7Smrg  int M_max = 128;
25672b676d7Smrg
25772b676d7Smrg  target = clock * 1000;
25872b676d7Smrg
25972b676d7Smrg  if(pSiS->Chipset == PCI_CHIP_SIS5597 || pSiS->Chipset == PCI_CHIP_SIS6326) {
26072b676d7Smrg
26172b676d7Smrg     int low_N = 2;
26272b676d7Smrg     int high_N = 5;
26372b676d7Smrg
26472b676d7Smrg     PSN = 1;
26572b676d7Smrg     P = 1;
26672b676d7Smrg     if(target < MAX_VCO_5597 / 2)  P = 2;
26772b676d7Smrg     if(target < MAX_VCO_5597 / 3)  P = 3;
26872b676d7Smrg     if(target < MAX_VCO_5597 / 4)  P = 4;
26972b676d7Smrg     if(target < MAX_VCO_5597 / 6)  P = 6;
27072b676d7Smrg     if(target < MAX_VCO_5597 / 8)  P = 8;
27172b676d7Smrg
27272b676d7Smrg     Fvco = P * target;
27372b676d7Smrg
27472b676d7Smrg     for(N = low_N; N <= high_N; N++) {
27572b676d7Smrg
27672b676d7Smrg         double M_desired = Fvco / Fref * N;
27772b676d7Smrg         if(M_desired > M_max * max_VLD)  continue;
27872b676d7Smrg
27972b676d7Smrg         if(M_desired > M_max) {
28072b676d7Smrg            M = M_desired / 2 + 0.5;
28172b676d7Smrg            VLD = 2;
28272b676d7Smrg         } else {
28372b676d7Smrg            M = Fvco / Fref * N + 0.5;
28472b676d7Smrg            VLD = 1;
28572b676d7Smrg         }
28672b676d7Smrg
28772b676d7Smrg         Fout = (double)Fref * (M * VLD)/(N * P);
28872b676d7Smrg
28972b676d7Smrg         error = (target - Fout) / target;
29072b676d7Smrg         aerror = (error < 0) ? -error : error;
29172b676d7Smrg         if(aerror < abest) {
29272b676d7Smrg            abest = aerror;
29372b676d7Smrg            bestM = M;
29472b676d7Smrg            bestN = N;
29572b676d7Smrg            bestP = P;
29672b676d7Smrg            bestPSN = PSN;
29772b676d7Smrg            bestVLD = VLD;
29872b676d7Smrg         }
29972b676d7Smrg     }
30072b676d7Smrg
30172b676d7Smrg  } else {
30272b676d7Smrg
30372b676d7Smrg     for(PSNx = 0; PSNx <= MAX_PSN ; PSNx++) {
30472b676d7Smrg
30572b676d7Smrg        int low_N, high_N;
30672b676d7Smrg        double FrefVLDPSN;
30772b676d7Smrg
30872b676d7Smrg        PSN = !PSNx ? 1 : 4;
30972b676d7Smrg
31072b676d7Smrg        low_N = 2;
31172b676d7Smrg        high_N = 32;
31272b676d7Smrg
31372b676d7Smrg        for(VLD = 1 ; VLD <= max_VLD ; VLD++) {
31472b676d7Smrg
31572b676d7Smrg           FrefVLDPSN = (double)Fref * VLD / PSN;
31672b676d7Smrg
31772b676d7Smrg	   for(N = low_N; N <= high_N; N++) {
31872b676d7Smrg              double tmp = FrefVLDPSN / N;
31972b676d7Smrg
32072b676d7Smrg              for(P = 1; P <= 4; P++) {
32172b676d7Smrg                 double Fvco_desired = target * ( P );
32272b676d7Smrg                 double M_desired = Fvco_desired / tmp;
32372b676d7Smrg
32472b676d7Smrg                 /* Which way will M_desired be rounded?
32572b676d7Smrg                  *  Do all three just to be safe.
32672b676d7Smrg                  */
32772b676d7Smrg                 int M_low = M_desired - 1;
32872b676d7Smrg                 int M_hi = M_desired + 1;
32972b676d7Smrg
33072b676d7Smrg                 if(M_hi < M_min || M_low > M_max) continue;
33172b676d7Smrg
33272b676d7Smrg		 if(M_low < M_min)  M_low = M_min;
33372b676d7Smrg
33472b676d7Smrg		 if(M_hi > M_max)   M_hi = M_max;
33572b676d7Smrg
33672b676d7Smrg                 for(M = M_low; M <= M_hi; M++) {
33772b676d7Smrg                    Fvco = tmp * M;
33872b676d7Smrg                    if(Fvco <= MIN_VCO) continue;
33972b676d7Smrg                    if(Fvco > MAX_VCO)  break;
34072b676d7Smrg
34172b676d7Smrg                    Fout = Fvco / ( P );
34272b676d7Smrg
34372b676d7Smrg                    error = (target - Fout) / target;
34472b676d7Smrg                    aerror = (error < 0) ? -error : error;
34572b676d7Smrg                    if(aerror < abest) {
34672b676d7Smrg                       abest = aerror;
34772b676d7Smrg                       bestM = M;
34872b676d7Smrg                       bestN = N;
34972b676d7Smrg                       bestP = P;
35072b676d7Smrg                       bestPSN = PSN;
35172b676d7Smrg                       bestVLD = VLD;
35272b676d7Smrg                    }
35372b676d7Smrg#ifdef TWDEBUG
35472b676d7Smrg                    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO,3,
35572b676d7Smrg			       "Freq. selected: %.2f MHz, M=%d, N=%d, VLD=%d, P=%d, PSN=%d\n",
35672b676d7Smrg                               (float)(clock / 1000.), M, N, P, VLD, PSN);
35772b676d7Smrg                    xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO,3,
35872b676d7Smrg			       "Freq. set: %.2f MHz\n", Fout / 1.0e6);
35972b676d7Smrg#endif
36072b676d7Smrg                 }
36172b676d7Smrg              }
36272b676d7Smrg           }
36372b676d7Smrg        }
36472b676d7Smrg     }
36572b676d7Smrg  }
36672b676d7Smrg
36772b676d7Smrg  vclk[Midx]   = bestM;
36872b676d7Smrg  vclk[Nidx]   = bestN;
36972b676d7Smrg  vclk[VLDidx] = bestVLD;
37072b676d7Smrg  vclk[Pidx]   = bestP;
37172b676d7Smrg  vclk[PSNidx] = bestPSN;
37272b676d7Smrg}
37372b676d7Smrg
37472b676d7Smrgstatic void
37572b676d7SmrgSiSSave(ScrnInfoPtr pScrn, SISRegPtr sisReg)
37672b676d7Smrg{
37772b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
37872b676d7Smrg    int i, max;
37972b676d7Smrg
38072b676d7Smrg    PDEBUG(xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3, "SiSSave()\n"));
38172b676d7Smrg
38272b676d7Smrg#ifdef UNLOCK_ALWAYS
38372b676d7Smrg    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
38472b676d7Smrg#endif
38572b676d7Smrg
38672b676d7Smrg    switch(pSiS->Chipset) {
38772b676d7Smrg        case PCI_CHIP_SIS5597:
38872b676d7Smrg           max=0x3C;
38972b676d7Smrg           break;
39072b676d7Smrg        case PCI_CHIP_SIS6326:
39172b676d7Smrg        case PCI_CHIP_SIS530:
39272b676d7Smrg           max=0x3F;
39372b676d7Smrg           break;
39472b676d7Smrg        default:
39572b676d7Smrg           max=0x37;
39672b676d7Smrg    }
39772b676d7Smrg
39872b676d7Smrg    /* Save extended SR registers */
39972b676d7Smrg    for(i = 0x00; i <= max; i++) {
40072b676d7Smrg       inSISIDXREG(SISSR, i, sisReg->sisRegs3C4[i]);
40172b676d7Smrg#ifdef TWDEBUG
40272b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
40372b676d7Smrg		 "SR%02X - %02X \n", i, sisReg->sisRegs3C4[i]);
40472b676d7Smrg#endif
40572b676d7Smrg    }
40672b676d7Smrg
40772b676d7Smrg#ifdef TWDEBUG
40872b676d7Smrg    for(i = 0x00; i <= 0x3f; i++) {
40972b676d7Smrg       inSISIDXREG(SISCR, i, max);
41072b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
41172b676d7Smrg		 "CR%02X - %02X \n", i, max);
41272b676d7Smrg    }
41372b676d7Smrg#endif
41472b676d7Smrg
41572b676d7Smrg    /* Save lock (will not be restored in SiSRestore()!) */
41672b676d7Smrg    inSISIDXREG(SISCR, 0x80, sisReg->sisRegs3D4[0x80]);
41772b676d7Smrg
41872b676d7Smrg    sisReg->sisRegs3C2 = inSISREG(SISMISCR);	 /* Misc */
41972b676d7Smrg
42072b676d7Smrg    /* Save TV registers */
42172b676d7Smrg    if((pSiS->Chipset == PCI_CHIP_SIS6326) && (pSiS->SiS6326Flags & SIS6326_HASTV)) {
42272b676d7Smrg       outSISIDXREG(SISCR, 0x80, 0x86);
42372b676d7Smrg       for(i = 0x00; i <= 0x44; i++) {
42472b676d7Smrg          sisReg->sis6326tv[i] = SiS6326GetTVReg(pScrn, i);
42572b676d7Smrg#ifdef TWDEBUG
42672b676d7Smrg          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
42772b676d7Smrg		 "VR%02X - %02X \n", i,sisReg->sis6326tv[i]);
42872b676d7Smrg#endif
42972b676d7Smrg       }
43072b676d7Smrg    }
43172b676d7Smrg}
43272b676d7Smrg
43372b676d7Smrgstatic void
43472b676d7SmrgSiSRestore(ScrnInfoPtr pScrn, SISRegPtr sisReg)
43572b676d7Smrg{
43672b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
43772b676d7Smrg    int i, max;
43872b676d7Smrg    UChar tmp;
43972b676d7Smrg
44072b676d7Smrg    PDEBUG(xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 4, "SiSRestore()\n"));
44172b676d7Smrg
44272b676d7Smrg#ifdef UNLOCK_ALWAYS
44372b676d7Smrg    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
44472b676d7Smrg#endif
44572b676d7Smrg
44672b676d7Smrg    switch(pSiS->Chipset) {
44772b676d7Smrg        case PCI_CHIP_SIS5597:
44872b676d7Smrg           max = 0x3C;
44972b676d7Smrg           break;
45072b676d7Smrg        case PCI_CHIP_SIS6326:
45172b676d7Smrg	case PCI_CHIP_SIS530:
45272b676d7Smrg           max = 0x3F;
45372b676d7Smrg           break;
45472b676d7Smrg        default:
45572b676d7Smrg           max = 0x37;
45672b676d7Smrg    }
45772b676d7Smrg
45872b676d7Smrg    /* Disable TV on 6326 before restoring */
45972b676d7Smrg    if((pSiS->Chipset == PCI_CHIP_SIS6326) && (pSiS->SiS6326Flags & SIS6326_HASTV)) {
46072b676d7Smrg       outSISIDXREG(SISCR, 0x80, 0x86);
46172b676d7Smrg       tmp = SiS6326GetTVReg(pScrn, 0x00);
46272b676d7Smrg       tmp &= ~0x04;
46372b676d7Smrg       SiS6326SetTVReg(pScrn, 0x00, tmp);
46472b676d7Smrg    }
46572b676d7Smrg
46672b676d7Smrg    /* Restore other extended SR registers */
46772b676d7Smrg    for(i = 0x06; i <= max; i++) {
46872b676d7Smrg       if((i == 0x13) || (i == 0x2a) || (i == 0x2b)) continue;
46972b676d7Smrg       outSISIDXREG(SISSR, i, sisReg->sisRegs3C4[i]);
47072b676d7Smrg    }
47172b676d7Smrg
47272b676d7Smrg    /* Now restore VCLK (with correct SR38 setting) */
47372b676d7Smrg    outSISIDXREG(SISSR, 0x13, sisReg->sisRegs3C4[0x13]);
47472b676d7Smrg    outSISIDXREG(SISSR, 0x2a, sisReg->sisRegs3C4[0x2a]);
47572b676d7Smrg    outSISIDXREG(SISSR, 0x2b, sisReg->sisRegs3C4[0x2b]);
47672b676d7Smrg
47772b676d7Smrg    /* Misc */
47872b676d7Smrg    outSISREG(SISMISCW, sisReg->sisRegs3C2);
47972b676d7Smrg
48072b676d7Smrg    /* MemClock needs this to take effect */
48172b676d7Smrg    outSISIDXREG(SISSR, 0x00, 0x01);    /* Synchronous Reset */
48272b676d7Smrg    usleep(10000);
48372b676d7Smrg    outSISIDXREG(SISSR, 0x00, 0x03);    /* End Reset */
48472b676d7Smrg
48572b676d7Smrg    /* Restore TV registers */
48672b676d7Smrg    pSiS->SiS6326Flags &= ~SIS6326_TVON;
48772b676d7Smrg    if((pSiS->Chipset == PCI_CHIP_SIS6326) && (pSiS->SiS6326Flags & SIS6326_HASTV)) {
48872b676d7Smrg       for(i = 0x01; i <= 0x44; i++) {
48972b676d7Smrg          SiS6326SetTVReg(pScrn, i, sisReg->sis6326tv[i]);
49072b676d7Smrg#ifdef TWDEBUG
49172b676d7Smrg          xf86DrvMsg(pScrn->scrnIndex, X_INFO,
49272b676d7Smrg		"VR%02x restored to %02x\n",
49372b676d7Smrg		i, sisReg->sis6326tv[i]);
49472b676d7Smrg#endif
49572b676d7Smrg       }
49672b676d7Smrg       tmp = SiS6326GetXXReg(pScrn, 0x13);
49772b676d7Smrg       SiS6326SetXXReg(pScrn, 0x13, 0xfa);
49872b676d7Smrg       tmp = SiS6326GetXXReg(pScrn, 0x14);
49972b676d7Smrg       SiS6326SetXXReg(pScrn, 0x14, 0xc8);
50072b676d7Smrg       if(!(sisReg->sisRegs3C4[0x0D] & 0x04)) {
50172b676d7Smrg	  tmp = SiS6326GetXXReg(pScrn, 0x13);
50272b676d7Smrg	  SiS6326SetXXReg(pScrn, 0x13, 0xf6);
50372b676d7Smrg	  tmp = SiS6326GetXXReg(pScrn, 0x14);
50472b676d7Smrg	  SiS6326SetXXReg(pScrn, 0x14, 0xbf);
50572b676d7Smrg       }
50672b676d7Smrg       if(sisReg->sis6326tv[0] & 0x04) pSiS->SiS6326Flags |= SIS6326_TVON;
50772b676d7Smrg    }
50872b676d7Smrg}
50972b676d7Smrg
51072b676d7Smrg/* Save SiS 300 series register contents */
51172b676d7Smrgstatic void
51272b676d7SmrgSiS300Save(ScrnInfoPtr pScrn, SISRegPtr sisReg)
51372b676d7Smrg{
51472b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
51572b676d7Smrg    int i;
51672b676d7Smrg
51772b676d7Smrg    PDEBUG(xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3, "SiS300Save()\n"));
51872b676d7Smrg
51972b676d7Smrg#ifdef UNLOCK_ALWAYS
52072b676d7Smrg    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
52172b676d7Smrg#endif
52272b676d7Smrg
52372b676d7Smrg    /* Save SR registers */
52472b676d7Smrg    for(i = 0x00; i <= 0x3D; i++) {
52572b676d7Smrg       inSISIDXREG(SISSR, i, sisReg->sisRegs3C4[i]);
52672b676d7Smrg#ifdef TWDEBUG
52772b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
52872b676d7Smrg		 "SR%02X - %02X \n", i,sisReg->sisRegs3C4[i]);
52972b676d7Smrg#endif
53072b676d7Smrg    }
53172b676d7Smrg
53272b676d7Smrg    /* Save CR registers */
53372b676d7Smrg    for(i = 0x00; i < 0x40; i++)  {
53472b676d7Smrg       inSISIDXREG(SISCR, i, sisReg->sisRegs3D4[i]);
53572b676d7Smrg#ifdef TWDEBUG
53672b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
53772b676d7Smrg		"CR%02X Contents - %02X \n", i,sisReg->sisRegs3D4[i]);
53872b676d7Smrg#endif
53972b676d7Smrg    }
54072b676d7Smrg
54172b676d7Smrg    /* Save Misc register */
54272b676d7Smrg    sisReg->sisRegs3C2 = inSISREG(SISMISCR);
54372b676d7Smrg
54472b676d7Smrg    /* Save FQBQ and GUI timer settings */
54572b676d7Smrg    if(pSiS->Chipset == PCI_CHIP_SIS630) {
5461fd23544Smrg       sisReg->sisRegsPCI50 = sis_pci_read_host_bridge_u32(0x50);
5471fd23544Smrg       sisReg->sisRegsPCIA0 = sis_pci_read_host_bridge_u32(0xA0);
54872b676d7Smrg#ifdef TWDEBUG
54972b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
55072b676d7Smrg		"PCI Config 50 = %lx\n", sisReg->sisRegsPCI50);
55172b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
55272b676d7Smrg		"PCI Config A0 = %lx\n", sisReg->sisRegsPCIA0);
55372b676d7Smrg#endif
55472b676d7Smrg    }
55572b676d7Smrg
55672b676d7Smrg    /* Save panel link/video bridge registers */
55772b676d7Smrg#ifndef TWDEBUG
55872b676d7Smrg    if(!pSiS->UseVESA) {
55972b676d7Smrg#endif
56072b676d7Smrg       if(pSiS->VBFlags2 & (VB2_LVDS | VB2_CHRONTEL))
56172b676d7Smrg          SiSLVDSChrontelSave(pScrn, sisReg);
56272b676d7Smrg       else if(pSiS->VBFlags2 & VB2_301)
56372b676d7Smrg          SiS301Save(pScrn, sisReg);
56472b676d7Smrg       else if(pSiS->VBFlags2 & VB2_30xBLV)
56572b676d7Smrg          SiS301BSave(pScrn, sisReg);
56672b676d7Smrg#ifndef TWDEBUG
56772b676d7Smrg    }
56872b676d7Smrg#endif
56972b676d7Smrg
57072b676d7Smrg    /* Save Mode number */
57172b676d7Smrg    sisReg->BIOSModeSave = SiS_GetSetModeID(pScrn,0xFF);
57272b676d7Smrg
57372b676d7Smrg#ifdef TWDEBUG
57472b676d7Smrg    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
57572b676d7Smrg	"BIOS mode ds:449 = 0x%x\n", sisReg->BIOSModeSave);
57672b676d7Smrg#endif
57772b676d7Smrg}
57872b676d7Smrg
57972b676d7Smrg/* Restore SiS300 series register contents */
58072b676d7Smrgstatic void
58172b676d7SmrgSiS300Restore(ScrnInfoPtr pScrn, SISRegPtr sisReg)
58272b676d7Smrg{
58372b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
58472b676d7Smrg    int i,temp;
58572b676d7Smrg    CARD32 temp1, temp2;
58672b676d7Smrg
58772b676d7Smrg    PDEBUG(xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 4, "SiS300Restore()\n"));
58872b676d7Smrg
58972b676d7Smrg#ifdef UNLOCK_ALWAYS
59072b676d7Smrg    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
59172b676d7Smrg#endif
59272b676d7Smrg
59372b676d7Smrg    /* Wait for accelerator to finish on-going drawing operations. */
59472b676d7Smrg    inSISIDXREG(SISSR, 0x1E, temp);
59572b676d7Smrg    if(temp & (0x40|0x10|0x02))  {
59672b676d7Smrg       while ( (SIS_MMIO_IN16(pSiS->IOBase, 0x8242) & 0xE000) != 0xE000){};
59772b676d7Smrg       while ( (SIS_MMIO_IN16(pSiS->IOBase, 0x8242) & 0xE000) != 0xE000){};
59872b676d7Smrg       while ( (SIS_MMIO_IN16(pSiS->IOBase, 0x8242) & 0xE000) != 0xE000){};
59972b676d7Smrg    }
60072b676d7Smrg
60172b676d7Smrg    if(!(pSiS->UseVESA)) {
60272b676d7Smrg       if(pSiS->VBFlags2 & VB2_LVDS) {
60372b676d7Smrg	  SiSRegInit(pSiS->SiS_Pr, pSiS->RelIO + 0x30);
60474c14cd6Smrg	  SiSSetLVDSetc(pSiS->SiS_Pr);
60572b676d7Smrg	  SiS_GetVBType(pSiS->SiS_Pr);
60672b676d7Smrg	  SiS_UnLockCRT2(pSiS->SiS_Pr);
60772b676d7Smrg	  SiS_DisableBridge(pSiS->SiS_Pr);
60872b676d7Smrg       }
60972b676d7Smrg    }
61072b676d7Smrg
61172b676d7Smrg    /* Restore extended CR registers */
61272b676d7Smrg    for(i = 0x19; i < 0x40; i++) {
61372b676d7Smrg       outSISIDXREG(SISCR, i, sisReg->sisRegs3D4[i]);
61472b676d7Smrg    }
61572b676d7Smrg
61672b676d7Smrg    if(pSiS->Chipset != PCI_CHIP_SIS300)  {
61772b676d7Smrg       UChar val;
61872b676d7Smrg       inSISIDXREG(SISCR, 0x1A, val);
61972b676d7Smrg       if(val == sisReg->sisRegs3D4[0x19])
62072b676d7Smrg	  outSISIDXREG(SISCR, 0x1A, sisReg->sisRegs3D4[0x19]);
62172b676d7Smrg       inSISIDXREG(SISCR,0x19,val);
62272b676d7Smrg       if(val == sisReg->sisRegs3D4[0x1A])
62372b676d7Smrg	  outSISIDXREG(SISCR, 0x19, sisReg->sisRegs3D4[0x1A]);
62472b676d7Smrg    }
62572b676d7Smrg
62672b676d7Smrg    /* Set (and leave) PCI_IO_ENABLE on if accelerators are on */
62772b676d7Smrg    if(sisReg->sisRegs3C4[0x1e] & 0x50) {
62872b676d7Smrg       sisReg->sisRegs3C4[0x20] |= 0x20;
62972b676d7Smrg       outSISIDXREG(SISSR, 0x20, sisReg->sisRegs3C4[0x20]);
63072b676d7Smrg    }
63172b676d7Smrg
63272b676d7Smrg    /* If TQ is switched on, don't switch it off ever again!
63372b676d7Smrg     * Therefore, always restore registers with TQ enabled.
63472b676d7Smrg     */
63572b676d7Smrg    if((!pSiS->NoAccel) && (pSiS->TurboQueue)) {
63672b676d7Smrg       temp = (pScrn->videoRam/64) - 8;
63772b676d7Smrg       sisReg->sisRegs3C4[0x26] = temp & 0xFF;
63872b676d7Smrg       sisReg->sisRegs3C4[0x27] = ((temp >> 8) & 3) | 0xF0;
63972b676d7Smrg    }
64072b676d7Smrg
64172b676d7Smrg    /* Restore extended SR registers */
64272b676d7Smrg    for(i = 0x06; i <= 0x3D; i++) {
64372b676d7Smrg       temp = sisReg->sisRegs3C4[i];
64472b676d7Smrg       if(!(pSiS->UseVESA)) {
64572b676d7Smrg	  if(pSiS->VBFlags2 & VB2_LVDS) {
64672b676d7Smrg	     if(i == 0x11) {
64772b676d7Smrg		inSISIDXREG(SISSR,0x11,temp);
64872b676d7Smrg		temp &= 0x0c;
64972b676d7Smrg		temp |= (sisReg->sisRegs3C4[i] & 0xf3);
65072b676d7Smrg	     }
65172b676d7Smrg          }
65272b676d7Smrg       }
65372b676d7Smrg       outSISIDXREG(SISSR, i, temp);
65472b676d7Smrg    }
65572b676d7Smrg
65672b676d7Smrg    /* Restore VCLK and ECLK */
65772b676d7Smrg    if(pSiS->VBFlags2 & (VB2_LVDS | VB2_30xB)) {
65872b676d7Smrg       outSISIDXREG(SISSR,0x31,0x20);
65972b676d7Smrg       outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
66072b676d7Smrg       outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
66172b676d7Smrg       outSISIDXREG(SISSR,0x2d,0x80);
66272b676d7Smrg       outSISIDXREG(SISSR,0x31,0x10);
66372b676d7Smrg       outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
66472b676d7Smrg       outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
66572b676d7Smrg       outSISIDXREG(SISSR,0x2d,0x80);
66672b676d7Smrg    }
66772b676d7Smrg    outSISIDXREG(SISSR,0x31,0x00);
66872b676d7Smrg    outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
66972b676d7Smrg    outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
67072b676d7Smrg    outSISIDXREG(SISSR,0x2d,0x80);
67172b676d7Smrg    if(pSiS->VBFlags2 & (VB2_LVDS | VB2_30xB)) {
67272b676d7Smrg       outSISIDXREG(SISSR,0x31,0x20);
67372b676d7Smrg       outSISIDXREG(SISSR,0x2e,sisReg->sisRegs3C4[0x2e]);
67472b676d7Smrg       outSISIDXREG(SISSR,0x2f,sisReg->sisRegs3C4[0x2f]);
67572b676d7Smrg       outSISIDXREG(SISSR,0x31,0x10);
67672b676d7Smrg       outSISIDXREG(SISSR,0x2e,sisReg->sisRegs3C4[0x2e]);
67772b676d7Smrg       outSISIDXREG(SISSR,0x2f,sisReg->sisRegs3C4[0x2f]);
67872b676d7Smrg       outSISIDXREG(SISSR,0x31,0x00);
67972b676d7Smrg       outSISIDXREG(SISSR,0x2e,sisReg->sisRegs3C4[0x2e]);
68072b676d7Smrg       outSISIDXREG(SISSR,0x2f,sisReg->sisRegs3C4[0x2f]);
68172b676d7Smrg    }
68272b676d7Smrg
68372b676d7Smrg    /* Restore Misc register */
68472b676d7Smrg    outSISREG(SISMISCW, sisReg->sisRegs3C2);
68572b676d7Smrg
68672b676d7Smrg    /* Restore FQBQ and GUI timer settings */
68772b676d7Smrg    if(pSiS->Chipset == PCI_CHIP_SIS630) {
6881fd23544Smrg       temp1 = sis_pci_read_host_bridge_u32(0x50);
6891fd23544Smrg       temp2 = sis_pci_read_host_bridge_u32(0xA0);
6901fd23544Smrg       if(sis_pci_read_host_bridge_u32(0x00) == 0x06301039) {
69172b676d7Smrg          temp1 &= 0xf0ffffff;
69272b676d7Smrg          temp1 |= (sisReg->sisRegsPCI50 & ~0xf0ffffff);
69372b676d7Smrg	  temp2 &= 0xf0ffffff;
69472b676d7Smrg          temp2 |= (sisReg->sisRegsPCIA0 & ~0xf0ffffff);
69572b676d7Smrg       } else {  /* 730 */
69672b676d7Smrg          temp1 &= 0xfffff9ff;
69772b676d7Smrg          temp1 |= (sisReg->sisRegsPCI50 & ~0xfffff9ff);
69872b676d7Smrg	  temp2 &= 0x00ffffff;
69972b676d7Smrg          temp2 |= (sisReg->sisRegsPCIA0 & ~0x00ffffff);
70072b676d7Smrg       }
7011fd23544Smrg       sis_pci_write_host_bridge_u32(0x50, temp1);
7021fd23544Smrg       sis_pci_write_host_bridge_u32(0xA0, temp2);
70372b676d7Smrg    }
70472b676d7Smrg
70572b676d7Smrg    /* Restore panel link/video bridge registers */
70672b676d7Smrg    if(!(pSiS->UseVESA)) {
70772b676d7Smrg       if(pSiS->VBFlags2 & (VB2_LVDS | VB2_CHRONTEL))
70872b676d7Smrg          SiSLVDSChrontelRestore(pScrn, sisReg);
70972b676d7Smrg       else if(pSiS->VBFlags2 & VB2_301)
71072b676d7Smrg          SiS301Restore(pScrn, sisReg);
71172b676d7Smrg       else if(pSiS->VBFlags2 & VB2_30xBLV)
71272b676d7Smrg          SiS301BRestore(pScrn, sisReg);
71372b676d7Smrg    }
71472b676d7Smrg
71572b676d7Smrg    /* MemClock needs this to take effect */
71672b676d7Smrg    outSISIDXREG(SISSR, 0x00, 0x01);    /* Synchronous Reset */
71772b676d7Smrg    outSISIDXREG(SISSR, 0x00, 0x03);    /* End Reset */
71872b676d7Smrg
71972b676d7Smrg    /* Restore mode number */
72072b676d7Smrg    SiS_GetSetModeID(pScrn,sisReg->BIOSModeSave);
72172b676d7Smrg}
72272b676d7Smrg
72372b676d7Smrg/* Save SiS315 series register contents */
72472b676d7Smrgstatic void
72572b676d7SmrgSiS315Save(ScrnInfoPtr pScrn, SISRegPtr sisReg)
72672b676d7Smrg{
72772b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
72872b676d7Smrg    int i, max;
72972b676d7Smrg
73072b676d7Smrg    PDEBUG(xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 3, "SiS315Save()\n"));
73172b676d7Smrg
73272b676d7Smrg#ifdef UNLOCK_ALWAYS
73372b676d7Smrg    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
73472b676d7Smrg#endif
73572b676d7Smrg
73672b676d7Smrg    /* Save SR registers */
73772b676d7Smrg    for(i = 0x00; i <= 0x60; i++) {
73872b676d7Smrg       inSISIDXREG(SISSR, i, sisReg->sisRegs3C4[i]);
73972b676d7Smrg#ifdef TWDEBUG
74072b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
74172b676d7Smrg			 "SR%02X - %02X \n", i,sisReg->sisRegs3C4[i]);
74272b676d7Smrg#endif
74372b676d7Smrg    }
74472b676d7Smrg
74572b676d7Smrg    /* Save command queue location */
74672b676d7Smrg    sisReg->sisMMIO85C0 = SIS_MMIO_IN32(pSiS->IOBase, 0x85C0);
74772b676d7Smrg
74872b676d7Smrg    /* Save CR registers */
74972b676d7Smrg    max = 0x7c;
75072b676d7Smrg    if(pSiS->ChipType >= XGI_20) max = 0xff;
75172b676d7Smrg    for(i = 0x00; i <= max; i++)  {
75272b676d7Smrg       inSISIDXREG(SISCR, i, sisReg->sisRegs3D4[i]);
75372b676d7Smrg#ifdef TWDEBUG
75472b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
75572b676d7Smrg		"CR%02X Contents - %02X \n", i,sisReg->sisRegs3D4[i]);
75672b676d7Smrg#endif
75772b676d7Smrg    }
75872b676d7Smrg
75972b676d7Smrg    /* Save video capture registers */
76072b676d7Smrg    for(i = 0x00; i <= 0x4f; i++)  {
76172b676d7Smrg       inSISIDXREG(SISCAP, i, sisReg->sisCapt[i]);
76272b676d7Smrg#ifdef TWDEBUG_VID
76372b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
76472b676d7Smrg		"Capt%02X Contents - %02X \n", i,sisReg->sisCapt[i]);
76572b676d7Smrg#endif
76672b676d7Smrg    }
76772b676d7Smrg
76872b676d7Smrg    /* Save video playback registers */
76972b676d7Smrg    for(i = 0x00; i <= 0x3f; i++) {
77072b676d7Smrg       inSISIDXREG(SISVID, i, sisReg->sisVid[i]);
77172b676d7Smrg#ifdef TWDEBUG_VID
77272b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
77372b676d7Smrg		"Vid%02X Contents - %02X \n", i,sisReg->sisVid[i]);
77472b676d7Smrg#endif
77572b676d7Smrg    }
77672b676d7Smrg
77772b676d7Smrg    /* Save Misc register */
77872b676d7Smrg    sisReg->sisRegs3C2 = inSISREG(SISMISCR);
77972b676d7Smrg
78072b676d7Smrg    /* Save panel link/video bridge registers */
78172b676d7Smrg#ifndef TWDEBUG
78272b676d7Smrg    if(!pSiS->UseVESA) {
78372b676d7Smrg#endif
78472b676d7Smrg       if(pSiS->VBFlags2 & (VB2_LVDS | VB2_CHRONTEL))
78572b676d7Smrg          SiSLVDSChrontelSave(pScrn, sisReg);
78672b676d7Smrg       else if(pSiS->VBFlags2 & VB2_301)
78772b676d7Smrg          SiS301Save(pScrn, sisReg);
78872b676d7Smrg       else if(pSiS->VBFlags2 & VB2_30xBLV)
78972b676d7Smrg          SiS301BSave(pScrn, sisReg);
79072b676d7Smrg#ifndef TWDEBUG
79172b676d7Smrg    }
79272b676d7Smrg#endif
79372b676d7Smrg
79472b676d7Smrg    /* Save mode number */
79572b676d7Smrg    sisReg->BIOSModeSave = SiS_GetSetModeID(pScrn,0xFF);
79672b676d7Smrg
79772b676d7Smrg#ifdef TWDEBUG
79872b676d7Smrg    xf86DrvMsg(pScrn->scrnIndex, X_INFO,
79972b676d7Smrg    	"BIOS mode ds:449 = 0x%x\n", sisReg->BIOSModeSave);
80072b676d7Smrg#endif
80172b676d7Smrg}
80272b676d7Smrg
80372b676d7Smrg/* Restore SiS315/330 series register contents */
80472b676d7Smrgstatic void
80572b676d7SmrgSiS315Restore(ScrnInfoPtr pScrn, SISRegPtr sisReg)
80672b676d7Smrg{
80772b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
80872b676d7Smrg    int i,temp;
80972b676d7Smrg
81072b676d7Smrg    PDEBUG(xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 4, "SiS315Restore()\n"));
81172b676d7Smrg
81272b676d7Smrg#ifdef UNLOCK_ALWAYS
81372b676d7Smrg    sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
81472b676d7Smrg#endif
81572b676d7Smrg
81672b676d7Smrg    /* Wait for accelerator to finish on-going drawing operations. */
81772b676d7Smrg    inSISIDXREG(SISSR, 0x1E, temp);
81872b676d7Smrg    if(temp & (0x40|0x10|0x02))  {	/* 0x40 = 2D, 0x02 = 3D enabled*/
81972b676d7Smrg       while ( (SIS_MMIO_IN32(pSiS->IOBase, 0x85CC) & 0x80000000) != 0x80000000){};
82072b676d7Smrg       while ( (SIS_MMIO_IN32(pSiS->IOBase, 0x85CC) & 0x80000000) != 0x80000000){};
82172b676d7Smrg       while ( (SIS_MMIO_IN32(pSiS->IOBase, 0x85CC) & 0x80000000) != 0x80000000){};
82272b676d7Smrg    }
82372b676d7Smrg
82472b676d7Smrg    /* We reset the command queue before restoring.
82572b676d7Smrg     * This might be required because we never know what
82672b676d7Smrg     * console driver (like the kernel framebuffer driver)
82772b676d7Smrg     * or application is running and which queue mode it
82872b676d7Smrg     * uses.
82972b676d7Smrg     */
83072b676d7Smrg    andSISIDXREG(SISCR, 0x55, 0x33);
83172b676d7Smrg    orSISIDXREG(SISSR, 0x26, 0x01);
83272b676d7Smrg    outSISIDXREG(SISSR, 0x27, 0x1F);
83372b676d7Smrg
83472b676d7Smrg    /* Restore extended CR registers */
83572b676d7Smrg    for(i = 0x19; i < 0x5C; i++) {
83672b676d7Smrg       outSISIDXREG(SISCR, i, sisReg->sisRegs3D4[i]);
83772b676d7Smrg    }
83872b676d7Smrg    if(pSiS->ChipType < SIS_661) {
83972b676d7Smrg       outSISIDXREG(SISCR, 0x79, sisReg->sisRegs3D4[0x79]);
84072b676d7Smrg    }
84172b676d7Smrg    outSISIDXREG(SISCR, pSiS->myCR63, sisReg->sisRegs3D4[pSiS->myCR63]);
84272b676d7Smrg
84372b676d7Smrg    /* Leave PCI_IO_ENABLE on if accelerators are on (Is this required?) */
84472b676d7Smrg    if(sisReg->sisRegs3C4[0x1e] & 0x52) {  /* 0x40=2D, 0x02=3D */
84572b676d7Smrg       sisReg->sisRegs3C4[0x20] |= 0x20;
84672b676d7Smrg       outSISIDXREG(SISSR, 0x20, sisReg->sisRegs3C4[0x20]);
84772b676d7Smrg    }
84872b676d7Smrg
84972b676d7Smrg    if(pSiS->SiS_Pr->SiS_SensibleSR11) {
85072b676d7Smrg       sisReg->sisRegs3C4[0x11] &= 0x0f;
85172b676d7Smrg    }
85272b676d7Smrg
85372b676d7Smrg    /* Restore extended SR registers */
85472b676d7Smrg    for(i = 0x06; i <= 0x3F; i++) {
85572b676d7Smrg       if(i == 0x26) {
85672b676d7Smrg          continue;
85772b676d7Smrg       } else if(i == 0x27) {
85872b676d7Smrg          outSISIDXREG(SISSR, 0x27, sisReg->sisRegs3C4[0x27]);
85972b676d7Smrg          outSISIDXREG(SISSR, 0x26, sisReg->sisRegs3C4[0x26]);
86072b676d7Smrg       } else {
86172b676d7Smrg          outSISIDXREG(SISSR, i, sisReg->sisRegs3C4[i]);
86272b676d7Smrg       }
86372b676d7Smrg    }
86472b676d7Smrg
86572b676d7Smrg    /* Restore VCLK and ECLK */
86672b676d7Smrg    andSISIDXREG(SISSR,0x31,0xcf);
86772b676d7Smrg    if(pSiS->VBFlags2 & VB2_LVDS) {
86872b676d7Smrg       orSISIDXREG(SISSR,0x31,0x20);
86972b676d7Smrg       outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
87072b676d7Smrg       outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
87172b676d7Smrg       outSISIDXREG(SISSR,0x2d,0x80);
87272b676d7Smrg       andSISIDXREG(SISSR,0x31,0xcf);
87372b676d7Smrg       orSISIDXREG(SISSR,0x31,0x10);
87472b676d7Smrg       outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
87572b676d7Smrg       outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
87672b676d7Smrg       outSISIDXREG(SISSR,0x2d,0x80);
87772b676d7Smrg       andSISIDXREG(SISSR,0x31,0xcf);
87872b676d7Smrg       outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
87972b676d7Smrg       outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
88072b676d7Smrg       outSISIDXREG(SISSR,0x2d,0x01);
88172b676d7Smrg       outSISIDXREG(SISSR,0x31,0x20);
88272b676d7Smrg       outSISIDXREG(SISSR,0x2e,sisReg->sisRegs3C4[0x2e]);
88372b676d7Smrg       outSISIDXREG(SISSR,0x2f,sisReg->sisRegs3C4[0x2f]);
88472b676d7Smrg       outSISIDXREG(SISSR,0x31,0x10);
88572b676d7Smrg       outSISIDXREG(SISSR,0x2e,sisReg->sisRegs3C4[0x2e]);
88672b676d7Smrg       outSISIDXREG(SISSR,0x2f,sisReg->sisRegs3C4[0x2f]);
88772b676d7Smrg       outSISIDXREG(SISSR,0x31,0x00);
88872b676d7Smrg       outSISIDXREG(SISSR,0x2e,sisReg->sisRegs3C4[0x2e]);
88972b676d7Smrg       outSISIDXREG(SISSR,0x2f,sisReg->sisRegs3C4[0x2f]);
89072b676d7Smrg    } else {
89172b676d7Smrg       outSISIDXREG(SISSR,0x2b,sisReg->sisRegs3C4[0x2b]);
89272b676d7Smrg       outSISIDXREG(SISSR,0x2c,sisReg->sisRegs3C4[0x2c]);
89372b676d7Smrg       outSISIDXREG(SISSR,0x2d,0x01);
89472b676d7Smrg    }
89572b676d7Smrg
89672b676d7Smrg#ifndef SISVRAMQ
89772b676d7Smrg    /* Initialize read/write pointer for command queue */
89872b676d7Smrg    SIS_MMIO_OUT32(pSiS->IOBase, 0x85C4, SIS_MMIO_IN32(pSiS->IOBase, 0x85C8));
89972b676d7Smrg#endif
90072b676d7Smrg    /* Restore queue location */
90172b676d7Smrg    SIS_MMIO_OUT32(pSiS->IOBase, 0x85C0, sisReg->sisMMIO85C0);
90272b676d7Smrg
90372b676d7Smrg    /* Restore Misc register */
90472b676d7Smrg    outSISREG(SISMISCW, sisReg->sisRegs3C2);
90572b676d7Smrg
90672b676d7Smrg    /* Restore panel link/video bridge registers */
90772b676d7Smrg    if(!(pSiS->UseVESA)) {
90872b676d7Smrg       if(pSiS->VBFlags2 & (VB2_LVDS | VB2_CHRONTEL))
90972b676d7Smrg          SiSLVDSChrontelRestore(pScrn, sisReg);
91072b676d7Smrg       else if(pSiS->VBFlags2 & VB2_301)
91172b676d7Smrg          SiS301Restore(pScrn, sisReg);
91272b676d7Smrg       else if(pSiS->VBFlags2 & VB2_30xBLV)
91372b676d7Smrg          SiS301BRestore(pScrn, sisReg);
91472b676d7Smrg    }
91572b676d7Smrg
91672b676d7Smrg    /* MemClock needs this to take effect */
91772b676d7Smrg    outSISIDXREG(SISSR, 0x00, 0x01);    /* Synchronous Reset */
91872b676d7Smrg    outSISIDXREG(SISSR, 0x00, 0x03);    /* End Reset */
91972b676d7Smrg
92072b676d7Smrg    /* Restore Mode number */
92172b676d7Smrg    SiS_GetSetModeID(pScrn,sisReg->BIOSModeSave);
92272b676d7Smrg}
92372b676d7Smrg
92472b676d7Smrgstatic void
92572b676d7SmrgSiSVBSave(ScrnInfoPtr pScrn, SISRegPtr sisReg, int p1, int p2, int p3, int p4)
92672b676d7Smrg{
92772b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
92872b676d7Smrg    int     i;
92972b676d7Smrg
93072b676d7Smrg    for(i=0; i<=p1; i++)  {
93172b676d7Smrg       inSISIDXREG(SISPART1, i, sisReg->VBPart1[i]);
93272b676d7Smrg#ifdef TWDEBUG
93372b676d7Smrg       xf86DrvMsg(0, X_INFO, "301xSave: Part1 0x%02x = 0x%02x\n", i, sisReg->VBPart1[i]);
93472b676d7Smrg#endif
93572b676d7Smrg    }
93672b676d7Smrg    for(i=0; i<=p2; i++)  {
93772b676d7Smrg       inSISIDXREG(SISPART2, i, sisReg->VBPart2[i]);
93872b676d7Smrg#ifdef TWDEBUG
93972b676d7Smrg       xf86DrvMsg(0, X_INFO, "301xSave: Part2 0x%02x = 0x%02x\n", i, sisReg->VBPart2[i]);
94072b676d7Smrg#endif
94172b676d7Smrg    }
94272b676d7Smrg    for(i=0; i<=p3; i++)  {
94372b676d7Smrg       inSISIDXREG(SISPART3, i, sisReg->VBPart3[i]);
94472b676d7Smrg#ifdef TWDEBUG
94572b676d7Smrg       xf86DrvMsg(0, X_INFO, "301xSave: Part3 0x%02x = 0x%02x\n", i, sisReg->VBPart3[i]);
94672b676d7Smrg#endif
94772b676d7Smrg    }
94872b676d7Smrg    for(i=0; i<=p4; i++)  {
94972b676d7Smrg       inSISIDXREG(SISPART4, i, sisReg->VBPart4[i]);
95072b676d7Smrg#ifdef TWDEBUG
95172b676d7Smrg       xf86DrvMsg(0, X_INFO, "301xSave: Part4 0x%02x = 0x%02x\n", i, sisReg->VBPart4[i]);
95272b676d7Smrg#endif
95372b676d7Smrg    }
95472b676d7Smrg}
95572b676d7Smrg
95672b676d7Smrg/* Save SiS301 bridge register contents */
95772b676d7Smrgstatic void
95872b676d7SmrgSiS301Save(ScrnInfoPtr pScrn, SISRegPtr sisReg)
95972b676d7Smrg{
96072b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
96172b676d7Smrg    int     Part1max, Part2max, Part3max, Part4max;
96272b676d7Smrg
96372b676d7Smrg    /* Highest register number to save/restore */
96472b676d7Smrg    if(pSiS->VGAEngine == SIS_300_VGA) Part1max = 0x1d;
96572b676d7Smrg    else Part1max = 0x2e;  /* 0x23, but we also need 2d-2e */
96672b676d7Smrg
96772b676d7Smrg    Part2max = 0x45;
96872b676d7Smrg    Part3max = 0x3e;
96972b676d7Smrg    Part4max = 0x1b;
97072b676d7Smrg
97172b676d7Smrg    SiSVBSave(pScrn, sisReg, Part1max, Part2max, Part3max, Part4max);
97272b676d7Smrg
97372b676d7Smrg    sisReg->VBPart2[0x00] &= ~0x20;      /* Disable VB Processor */
97472b676d7Smrg    sisReg->sisRegs3C4[0x32] &= ~0x20;   /* Disable Lock Mode */
97572b676d7Smrg}
97672b676d7Smrg
97772b676d7Smrg/* Restore SiS301 bridge register contents */
97872b676d7Smrgstatic void
97972b676d7SmrgSiS301Restore(ScrnInfoPtr pScrn, SISRegPtr sisReg)
98072b676d7Smrg{
98172b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
98272b676d7Smrg    int     Part1max, Part2max, Part3max, Part4max;
98372b676d7Smrg
98472b676d7Smrg    /* Highest register number to save/restore */
98572b676d7Smrg    if(pSiS->VGAEngine == SIS_300_VGA) Part1max = 0x1d;
98672b676d7Smrg    else Part1max = 0x23;
98772b676d7Smrg
98872b676d7Smrg    Part2max = 0x45;
98972b676d7Smrg    Part3max = 0x3e;
99072b676d7Smrg    Part4max = 0x1b;
99172b676d7Smrg
99272b676d7Smrg    SiSRegInit(pSiS->SiS_Pr, pSiS->RelIO + 0x30);
99374c14cd6Smrg    SiSSetLVDSetc(pSiS->SiS_Pr);
99472b676d7Smrg    SiS_GetVBType(pSiS->SiS_Pr);
99572b676d7Smrg    SiS_DisableBridge(pSiS->SiS_Pr);
99672b676d7Smrg    SiS_UnLockCRT2(pSiS->SiS_Pr);
99772b676d7Smrg
99872b676d7Smrg    /* Pre-restore Part1 */
99972b676d7Smrg    outSISIDXREG(SISPART1, 0x04, 0x00);
100072b676d7Smrg    outSISIDXREG(SISPART1, 0x05, 0x00);
100172b676d7Smrg    outSISIDXREG(SISPART1, 0x06, 0x00);
100272b676d7Smrg    outSISIDXREG(SISPART1, 0x00, sisReg->VBPart1[0]);
100372b676d7Smrg    outSISIDXREG(SISPART1, 0x01, sisReg->VBPart1[1]);
100472b676d7Smrg
100572b676d7Smrg    /* Pre-restore Part4 */
100672b676d7Smrg    outSISIDXREG(SISPART4, 0x0D, sisReg->VBPart4[0x0D]);
100772b676d7Smrg    outSISIDXREG(SISPART4, 0x0C, sisReg->VBPart4[0x0C]);
100872b676d7Smrg
100972b676d7Smrg    if((!(sisReg->sisRegs3D4[0x30] & 0x03)) &&
101072b676d7Smrg       (sisReg->sisRegs3D4[0x31] & 0x20))  {      /* disable CRT2 */
101172b676d7Smrg       SiS_LockCRT2(pSiS->SiS_Pr);
101272b676d7Smrg       return;
101372b676d7Smrg    }
101472b676d7Smrg
101572b676d7Smrg    /* Restore Part1 */
101672b676d7Smrg    SetBlock(SISPART1, 0x02, Part1max, &(sisReg->VBPart1[0x02]));
101772b676d7Smrg    if(pSiS->VGAEngine == SIS_315_VGA) {
101872b676d7Smrg       /* Restore extra registers on 315 series */
101972b676d7Smrg       SetBlock(SISPART1, 0x2C, 0x2E, &(sisReg->VBPart1[0x2C]));
102072b676d7Smrg    }
102172b676d7Smrg
102272b676d7Smrg    /* Restore Part2 */
102372b676d7Smrg    SetBlock(SISPART2, 0x00, Part2max, &(sisReg->VBPart2[0x00]));
102472b676d7Smrg
102572b676d7Smrg    /* Restore Part3 */
102672b676d7Smrg    SetBlock(SISPART3, 0x00, Part3max, &(sisReg->VBPart3[0x00]));
102772b676d7Smrg
102872b676d7Smrg    /* Restore Part4 */
102972b676d7Smrg    SetBlock(SISPART4, 0x0E, 0x11, &(sisReg->VBPart4[0x0E]));
103072b676d7Smrg    SetBlock(SISPART4, 0x13, Part4max, &(sisReg->VBPart4[0x13]));
103172b676d7Smrg
103272b676d7Smrg    /* Post-restore Part4 (CRT2VCLK) */
103372b676d7Smrg    outSISIDXREG(SISPART4, 0x0A, 0x01);
103472b676d7Smrg    outSISIDXREG(SISPART4, 0x0B, sisReg->VBPart4[0x0B]);
103572b676d7Smrg    outSISIDXREG(SISPART4, 0x0A, sisReg->VBPart4[0x0A]);
103672b676d7Smrg    outSISIDXREG(SISPART4, 0x12, 0x00);
103772b676d7Smrg    outSISIDXREG(SISPART4, 0x12, sisReg->VBPart4[0x12]);
103872b676d7Smrg
103972b676d7Smrg    SiS_EnableBridge(pSiS->SiS_Pr);
104072b676d7Smrg    SiS_DisplayOn(pSiS->SiS_Pr);
104172b676d7Smrg    SiS_LockCRT2(pSiS->SiS_Pr);
104272b676d7Smrg}
104372b676d7Smrg
104472b676d7Smrg/* Save SiS30xB/30xLV bridge register contents */
104572b676d7Smrgstatic void
104672b676d7SmrgSiS301BSave(ScrnInfoPtr pScrn, SISRegPtr sisReg)
104772b676d7Smrg{
104872b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
104972b676d7Smrg    int     Part1max, Part2max, Part3max, Part4max;
105072b676d7Smrg
105172b676d7Smrg    Part1max = 0x60;
105272b676d7Smrg    Part2max = 0x4d;
105372b676d7Smrg    Part3max = 0x3e;
105472b676d7Smrg    Part4max = 0x23;
105572b676d7Smrg    if(pSiS->VBFlags2 & (VB2_301LV | VB2_302LV)) {
105672b676d7Smrg       Part4max = 0x34;
105772b676d7Smrg    } else if(pSiS->VBFlags2 & (VB2_301C | VB2_302ELV)) {
105872b676d7Smrg       Part2max = 0xff;
105972b676d7Smrg       Part4max = 0x3c;
106072b676d7Smrg    } /* TODO for 307 */
106172b676d7Smrg
106272b676d7Smrg    SiSVBSave(pScrn, sisReg, Part1max, Part2max, Part3max, Part4max);
106372b676d7Smrg
106472b676d7Smrg    sisReg->VBPart2[0x00] &= ~0x20;      /* Disable VB Processor */
106572b676d7Smrg    sisReg->sisRegs3C4[0x32] &= ~0x20;   /* Disable Lock Mode */
106672b676d7Smrg}
106772b676d7Smrg
106872b676d7Smrg/* Restore SiS30xB/30xLV bridge register contents */
106972b676d7Smrgstatic void
107072b676d7SmrgSiS301BRestore(ScrnInfoPtr pScrn, SISRegPtr sisReg)
107172b676d7Smrg{
107272b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
107372b676d7Smrg    int     Part1max, Part2max, Part3max, Part4max;
107472b676d7Smrg
107572b676d7Smrg    Part1max = 0x23;
107672b676d7Smrg    Part2max = 0x4d;
107772b676d7Smrg    Part3max = 0x3e;
107872b676d7Smrg    Part4max = 0x22;
107972b676d7Smrg    if(pSiS->VBFlags2 & (VB2_301LV|VB2_302LV)) {
108072b676d7Smrg       Part4max = 0x34;
108172b676d7Smrg    } else if(pSiS->VBFlags2 & (VB2_301C|VB2_302ELV)) {
108272b676d7Smrg       Part2max = 0xff;
108372b676d7Smrg       Part4max = 0x3c;
108472b676d7Smrg    } /* TODO for 307 */
108572b676d7Smrg
108672b676d7Smrg    SiSRegInit(pSiS->SiS_Pr, pSiS->RelIO + 0x30);
108774c14cd6Smrg    SiSSetLVDSetc(pSiS->SiS_Pr);
108872b676d7Smrg    SiS_GetVBType(pSiS->SiS_Pr);
108972b676d7Smrg    SiS_DisableBridge(pSiS->SiS_Pr);
109072b676d7Smrg    SiS_UnLockCRT2(pSiS->SiS_Pr);
109172b676d7Smrg
109272b676d7Smrg    /* Pre-restore Part1 */
109372b676d7Smrg    outSISIDXREG(SISPART1, 0x04, 0x00);
109472b676d7Smrg    outSISIDXREG(SISPART1, 0x05, 0x00);
109572b676d7Smrg    outSISIDXREG(SISPART1, 0x06, 0x00);
109672b676d7Smrg    outSISIDXREG(SISPART1, 0x00, sisReg->VBPart1[0x00]);
109772b676d7Smrg    outSISIDXREG(SISPART1, 0x01, sisReg->VBPart1[0x01]);
109872b676d7Smrg    /* Mode reg 0x01 became 0x2e on 315 series (0x01 still contains FIFO) */
109972b676d7Smrg    if(pSiS->VGAEngine == SIS_315_VGA) {
110072b676d7Smrg       outSISIDXREG(SISPART1, 0x2e, sisReg->VBPart1[0x2e]);
110172b676d7Smrg    }
110272b676d7Smrg
110372b676d7Smrg    /* Pre-restore Part4 */
110472b676d7Smrg    outSISIDXREG(SISPART4, 0x0D, sisReg->VBPart4[0x0D]);
110572b676d7Smrg    outSISIDXREG(SISPART4, 0x0C, sisReg->VBPart4[0x0C]);
110672b676d7Smrg
110772b676d7Smrg    if((!(sisReg->sisRegs3D4[0x30] & 0x03)) &&
110872b676d7Smrg       (sisReg->sisRegs3D4[0x31] & 0x20)) {      /* disable CRT2 */
110972b676d7Smrg       SiS_LockCRT2(pSiS->SiS_Pr);
111072b676d7Smrg       return;
111172b676d7Smrg    }
111272b676d7Smrg
111372b676d7Smrg    /* Restore Part1  */
111472b676d7Smrg    SetBlock(SISPART1, 0x02, Part1max, &(sisReg->VBPart1[0x02]));
111572b676d7Smrg    if(pSiS->VGAEngine == SIS_315_VGA) {
111672b676d7Smrg       SetBlock(SISPART1, 0x2C, 0x2D, &(sisReg->VBPart1[0x2C]));
111772b676d7Smrg       SetBlock(SISPART1, 0x35, 0x37, &(sisReg->VBPart1[0x35]));
111872b676d7Smrg       if((pSiS->ChipFlags & SiSCF_Is65x) || (pSiS->ChipType >= SIS_661)) {
111972b676d7Smrg          outSISIDXREG(SISPART1, 0x4c, sisReg->VBPart1[0x4c]);
112072b676d7Smrg       }
112172b676d7Smrg       outSISIDXREG(SISPART1, 0x2e, sisReg->VBPart1[0x2e] & 0x7f);
112272b676d7Smrg    }
112372b676d7Smrg
112472b676d7Smrg    /* Restore Part2 */
112572b676d7Smrg    SetBlock(SISPART2, 0x00, Part2max, &(sisReg->VBPart2[0x00]));
112672b676d7Smrg
112772b676d7Smrg    /* Restore Part3 */
112872b676d7Smrg    SetBlock(SISPART3, 0x00, Part3max, &(sisReg->VBPart3[0x00]));
112972b676d7Smrg
113072b676d7Smrg    /* Restore Part4 */
113172b676d7Smrg    SetBlock(SISPART4, 0x0E, 0x11, &(sisReg->VBPart4[0x0E]));
113272b676d7Smrg    SetBlock(SISPART4, 0x13, Part4max, &(sisReg->VBPart4[0x13]));
113372b676d7Smrg
113472b676d7Smrg    /* Post-restore Part4 (CRT2VCLK) */
113572b676d7Smrg    outSISIDXREG(SISPART4, 0x0A, sisReg->VBPart4[0x0A]);
113672b676d7Smrg    outSISIDXREG(SISPART4, 0x0B, sisReg->VBPart4[0x0B]);
113772b676d7Smrg    outSISIDXREG(SISPART4, 0x12, 0x00);
113872b676d7Smrg    outSISIDXREG(SISPART4, 0x12, sisReg->VBPart4[0x12]);
113972b676d7Smrg
114072b676d7Smrg    SiS_EnableBridge(pSiS->SiS_Pr);
114172b676d7Smrg    SiS_DisplayOn(pSiS->SiS_Pr);
114272b676d7Smrg    SiS_LockCRT2(pSiS->SiS_Pr);
114372b676d7Smrg}
114472b676d7Smrg
114572b676d7Smrg/* Save LVDS bridge (+ Chrontel) register contents */
114672b676d7Smrgstatic void
114772b676d7SmrgSiSLVDSChrontelSave(ScrnInfoPtr pScrn, SISRegPtr sisReg)
114872b676d7Smrg{
114972b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
115072b676d7Smrg    int     i;
115172b676d7Smrg
115272b676d7Smrg    /* Save Part1 */
115372b676d7Smrg    for(i=0; i<0x46; i++) {
115472b676d7Smrg       inSISIDXREG(SISPART1, i, sisReg->VBPart1[i]);
115572b676d7Smrg#ifdef TWDEBUG
115672b676d7Smrg       xf86DrvMsg(pScrn->scrnIndex, X_INFO,
115772b676d7Smrg	                "LVDSSave: Part1Port 0x%02x = 0x%02x\n",
115872b676d7Smrg			i, sisReg->VBPart1[i]);
115972b676d7Smrg#endif
116072b676d7Smrg    }
116172b676d7Smrg
116272b676d7Smrg    /* Save Chrontel registers */
116372b676d7Smrg    if(pSiS->VBFlags2 & VB2_CHRONTEL) {
116472b676d7Smrg       if(pSiS->ChrontelType == CHRONTEL_700x) {
116572b676d7Smrg          for(i=0; i<0x1D; i++)  {
116672b676d7Smrg             sisReg->ch70xx[i] = SiS_GetCH700x(pSiS->SiS_Pr, ch700xidx[i]);
116772b676d7Smrg#ifdef TWDEBUG
116872b676d7Smrg	     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
116972b676d7Smrg	                "LVDSSave: Chrontel 0x%02x = 0x%02x\n",
117072b676d7Smrg			ch700xidx[i], sisReg->ch70xx[i]);
117172b676d7Smrg#endif
117272b676d7Smrg	  }
117372b676d7Smrg       } else {
117472b676d7Smrg          for(i=0; i<35; i++)  {
117572b676d7Smrg             sisReg->ch70xx[i] = SiS_GetCH701x(pSiS->SiS_Pr, ch701xidx[i]);
117672b676d7Smrg#ifdef TWDEBUG
117772b676d7Smrg	     xf86DrvMsg(pScrn->scrnIndex, X_INFO,
117872b676d7Smrg	                "LVDSSave: Chrontel 0x%02x = 0x%02x\n",
117972b676d7Smrg			ch701xidx[i], sisReg->ch70xx[i]);
118072b676d7Smrg#endif
118172b676d7Smrg          }
118272b676d7Smrg       }
118372b676d7Smrg    }
118472b676d7Smrg
118572b676d7Smrg    sisReg->sisRegs3C4[0x32] &= ~0x20;      /* Disable Lock Mode */
118672b676d7Smrg}
118772b676d7Smrg
118872b676d7Smrg/* Restore LVDS bridge (+ Chrontel) register contents */
118972b676d7Smrgstatic void
119072b676d7SmrgSiSLVDSChrontelRestore(ScrnInfoPtr pScrn, SISRegPtr sisReg)
119172b676d7Smrg{
119272b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
119372b676d7Smrg    int i;
119472b676d7Smrg
119572b676d7Smrg    SiSRegInit(pSiS->SiS_Pr, pSiS->RelIO + 0x30);
119674c14cd6Smrg    SiSSetLVDSetc(pSiS->SiS_Pr);
119772b676d7Smrg    SiS_GetVBType(pSiS->SiS_Pr);
119872b676d7Smrg    SiS_DisableBridge(pSiS->SiS_Pr);
119972b676d7Smrg    if(pSiS->ChipType == SIS_730) {
120072b676d7Smrg       outSISIDXREG(SISPART1, 0x00, 0x80);
120172b676d7Smrg    }
120272b676d7Smrg    SiS_UnLockCRT2(pSiS->SiS_Pr);
120372b676d7Smrg
120472b676d7Smrg    if(pSiS->VBFlags2 & VB2_CHRONTEL) {
120572b676d7Smrg       /* Restore Chrontel registers */
120672b676d7Smrg       if(pSiS->ChrontelType == CHRONTEL_700x) {
120772b676d7Smrg          for(i=0; i<0x11; i++) {
120872b676d7Smrg             SiS_SetCH700x(pSiS->SiS_Pr, ch700xidx[i] & 0xFF, sisReg->ch70xx[i]);
120972b676d7Smrg          }
121072b676d7Smrg       } else {
121172b676d7Smrg          for(i=0; i<34; i++) {
121272b676d7Smrg             SiS_SetCH701x(pSiS->SiS_Pr, ch701xidx[i] & 0xFF, sisReg->ch70xx[i]);
121372b676d7Smrg          }
121472b676d7Smrg       }
121572b676d7Smrg    }
121672b676d7Smrg
121772b676d7Smrg    /* pre-restore Part1 */
121872b676d7Smrg    outSISIDXREG(SISPART1, 0x04, 0x00);
121972b676d7Smrg    outSISIDXREG(SISPART1, 0x05, 0x00);
122072b676d7Smrg    outSISIDXREG(SISPART1, 0x06, 0x00);
122172b676d7Smrg    outSISIDXREG(SISPART1, 0x00, sisReg->VBPart1[0]);
122272b676d7Smrg    if(pSiS->VGAEngine == SIS_300_VGA) {
122372b676d7Smrg       outSISIDXREG(SISPART1, 0x01, (sisReg->VBPart1[1] | 0x80));
122472b676d7Smrg    } else {
122572b676d7Smrg       outSISIDXREG(SISPART1, 0x01, sisReg->VBPart1[1]);
122672b676d7Smrg    }
122772b676d7Smrg
122872b676d7Smrg    if((!(sisReg->sisRegs3D4[0x30] & 0x03)) &&
122972b676d7Smrg       (sisReg->sisRegs3D4[0x31] & 0x20)) {      /* disable CRT2 */
123072b676d7Smrg       SiS_LockCRT2(pSiS->SiS_Pr);
123172b676d7Smrg       return;
123272b676d7Smrg    }
123372b676d7Smrg
123472b676d7Smrg    /* Restore Part1 */
123572b676d7Smrg    if(pSiS->VGAEngine == SIS_300_VGA) {
123672b676d7Smrg       outSISIDXREG(SISPART1, 0x02, (sisReg->VBPart1[2] | 0x40));
123772b676d7Smrg    } else {
123872b676d7Smrg       outSISIDXREG(SISPART1, 0x02, sisReg->VBPart1[2]);
123972b676d7Smrg    }
124072b676d7Smrg    SetBlock(SISPART1, 0x03, 0x23, &(sisReg->VBPart1[0x03]));
124172b676d7Smrg    if(pSiS->VGAEngine == SIS_315_VGA) {
124272b676d7Smrg       SetBlock(SISPART1, 0x2C, 0x2E, &(sisReg->VBPart1[0x2C]));
124372b676d7Smrg       SetBlock(SISPART1, 0x35, 0x37, &(sisReg->VBPart1[0x35]));  /* Panel Link Scaler */
124472b676d7Smrg    }
124572b676d7Smrg
124672b676d7Smrg    /* For 550 DSTN registers */
124772b676d7Smrg    if(pSiS->DSTN || pSiS->FSTN) {
124872b676d7Smrg       SetBlock(SISPART1, 0x25, 0x2E, &(sisReg->VBPart1[0x25]));
124972b676d7Smrg       SetBlock(SISPART1, 0x30, 0x45, &(sisReg->VBPart1[0x30]));
125072b676d7Smrg    }
125172b676d7Smrg
125272b676d7Smrg    SiS_EnableBridge(pSiS->SiS_Pr);
125372b676d7Smrg    SiS_DisplayOn(pSiS->SiS_Pr);
125472b676d7Smrg    SiS_LockCRT2(pSiS->SiS_Pr);
125572b676d7Smrg}
125672b676d7Smrg
125772b676d7Smrg/* Restore output selection registers */
125872b676d7Smrgvoid
125972b676d7SmrgSiSRestoreBridge(ScrnInfoPtr pScrn, SISRegPtr sisReg)
126072b676d7Smrg{
126172b676d7Smrg   SISPtr pSiS = SISPTR(pScrn);
126272b676d7Smrg   int i;
126372b676d7Smrg
126472b676d7Smrg#ifdef UNLOCK_ALWAYS
126572b676d7Smrg   sisSaveUnlockExtRegisterLock(pSiS, NULL, NULL);
126672b676d7Smrg#endif
126772b676d7Smrg
126872b676d7Smrg   for(i = 0x30; i <= 0x3b; i++) {
126972b676d7Smrg      if(i == 0x34) continue;
127072b676d7Smrg      outSISIDXREG(SISCR, i, sisReg->sisRegs3D4[i]);
127172b676d7Smrg   }
127272b676d7Smrg
127372b676d7Smrg   if(pSiS->VGAEngine == SIS_315_VGA) {
127472b676d7Smrg      outSISIDXREG(SISCR, pSiS->myCR63, sisReg->sisRegs3D4[pSiS->myCR63]);
127572b676d7Smrg      if(pSiS->ChipType < SIS_661) {
127672b676d7Smrg         outSISIDXREG(SISCR, 0x79, sisReg->sisRegs3D4[0x79]);
127772b676d7Smrg      }
127872b676d7Smrg   }
127972b676d7Smrg}
128072b676d7Smrg
128172b676d7Smrg/* Auxiliary function to find real memory clock (in Khz) */
128272b676d7Smrg/* Not for 530/620 if UMA (on these, the mclk is stored in SR10) */
128372b676d7Smrgint
128472b676d7SmrgSiSMclk(SISPtr pSiS)
128572b676d7Smrg{
128672b676d7Smrg    int mclk=0;
128772b676d7Smrg    UChar Num, Denum, Base;
128872b676d7Smrg
128972b676d7Smrg    switch (pSiS->Chipset)  {
129072b676d7Smrg
129172b676d7Smrg    case PCI_CHIP_SIS300:
129272b676d7Smrg    case PCI_CHIP_SIS540:
129372b676d7Smrg    case PCI_CHIP_SIS630:
129472b676d7Smrg    case PCI_CHIP_SIS315:
129572b676d7Smrg    case PCI_CHIP_SIS315H:
129672b676d7Smrg    case PCI_CHIP_SIS315PRO:
129772b676d7Smrg    case PCI_CHIP_SIS550:
129872b676d7Smrg    case PCI_CHIP_SIS650:
129972b676d7Smrg    case PCI_CHIP_SIS330:
130072b676d7Smrg    case PCI_CHIP_SIS660:
130172b676d7Smrg    case PCI_CHIP_SIS340:
130272b676d7Smrg    case PCI_CHIP_XGIXG20:
130372b676d7Smrg    case PCI_CHIP_XGIXG40:
130472b676d7Smrg
130572b676d7Smrg	/* Numerator */
130672b676d7Smrg	inSISIDXREG(SISSR, 0x28, Num);
130772b676d7Smrg	mclk = 14318 * ((Num & 0x7f) + 1);
130872b676d7Smrg
130972b676d7Smrg	/* Denumerator */
131072b676d7Smrg	inSISIDXREG(SISSR, 0x29, Denum);
131172b676d7Smrg	mclk = mclk / ((Denum & 0x1f) + 1);
131272b676d7Smrg
131372b676d7Smrg	/* Divider */
131472b676d7Smrg	if((Num & 0x80) != 0)  mclk *= 2;
131572b676d7Smrg
131672b676d7Smrg	/* Post-Scaler */
131772b676d7Smrg	if((Denum & 0x80) == 0) {
131872b676d7Smrg	   mclk = mclk / (((Denum & 0x60) >> 5) + 1);
131972b676d7Smrg	} else {
132072b676d7Smrg	   mclk = mclk / ((((Denum & 0x60) >> 5) + 1) * 2);
132172b676d7Smrg	}
132272b676d7Smrg	break;
132372b676d7Smrg
132472b676d7Smrg    case PCI_CHIP_SIS5597:
132572b676d7Smrg    case PCI_CHIP_SIS6326:
132672b676d7Smrg    case PCI_CHIP_SIS530:
132772b676d7Smrg    default:
132872b676d7Smrg	/* Numerator */
132972b676d7Smrg	inSISIDXREG(SISSR, 0x28, Num);
133072b676d7Smrg	mclk = 14318 * ((Num & 0x7f) + 1);
133172b676d7Smrg
133272b676d7Smrg	/* Denumerator */
133372b676d7Smrg	inSISIDXREG(SISSR, 0x29, Denum);
133472b676d7Smrg	mclk = mclk / ((Denum & 0x1f) + 1);
133572b676d7Smrg
133672b676d7Smrg	/* Divider. Doesn't work on older cards */
133772b676d7Smrg	if(pSiS->oldChipset >= OC_SIS5597) {
133872b676d7Smrg	   if(Num & 0x80) mclk *= 2;
133972b676d7Smrg	}
134072b676d7Smrg
134172b676d7Smrg	/* Post-scaler. Values' meaning depends on SR13 bit 7  */
134272b676d7Smrg	inSISIDXREG(SISSR, 0x13, Base);
134372b676d7Smrg	if((Base & 0x80) == 0) {
134472b676d7Smrg	   mclk = mclk / (((Denum & 0x60) >> 5) + 1);
134572b676d7Smrg	} else {
134672b676d7Smrg	   /* Values 00 and 01 are reserved */
134772b676d7Smrg	   if ((Denum & 0x60) == 0x40)  mclk /= 6;
134872b676d7Smrg	   if ((Denum & 0x60) == 0x60)  mclk /= 8;
134972b676d7Smrg	}
135072b676d7Smrg	break;
135172b676d7Smrg    }
135272b676d7Smrg
135372b676d7Smrg    return(mclk);
135472b676d7Smrg}
135572b676d7Smrg
135672b676d7Smrg/* This estimates the CRT2 clock we are going to use.
135772b676d7Smrg * The total bandwidth is to be reduced by the value
135872b676d7Smrg * returned here in order to get an idea of the maximum
135972b676d7Smrg * dotclock left for CRT1.
136072b676d7Smrg * Since we don't know yet, what mode the user chose,
136172b676d7Smrg * we return the maximum dotclock used by
136272b676d7Smrg * - either the LCD attached, or
136372b676d7Smrg * - TV
136472b676d7Smrg * For VGA2, we share the bandwith equally.
136572b676d7Smrg */
136672b676d7Smrgstatic int
136772b676d7SmrgSiSEstimateCRT2Clock(ScrnInfoPtr pScrn, Bool FakeForCRT2)
136872b676d7Smrg{
136972b676d7Smrg	SISPtr pSiS = SISPTR(pScrn);
137072b676d7Smrg
137172b676d7Smrg	if(pSiS->VBFlags & CRT2_LCD) {
137272b676d7Smrg	   if(pSiS->VBLCDFlags & (VB_LCD_320x480 | VB_LCD_800x600 | VB_LCD_640x480)) {
137372b676d7Smrg	      return 40000;
137472b676d7Smrg	   } else if(pSiS->VBLCDFlags & (VB_LCD_1024x768 | VB_LCD_1024x600 | VB_LCD_1152x768)) {
137572b676d7Smrg	      return 65000;
137672b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1280x720) {
137772b676d7Smrg	      /* Fake clock; VGA (4:3) mode is 108, but uses only 75 for LCD */
137872b676d7Smrg	      if(FakeForCRT2) return 108000;
137972b676d7Smrg	      else            return 75000;
138072b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1280x768) {
138172b676d7Smrg	      /* Fake clock; VGA (4:3) mode is 108, but uses only 81 for LCD */
138272b676d7Smrg	      if(FakeForCRT2) return 108000;
138372b676d7Smrg	      else            return 81000;
138472b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1280x800) {
138572b676d7Smrg	      /* Fake clock; VGA (4:3) mode is 108, but uses only 83 for LCD */
138672b676d7Smrg	      if(FakeForCRT2) return 108000;
138772b676d7Smrg	      else            return 83000;
138872b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1280x854) {
138972b676d7Smrg	      /* Fake clock; VGA (4:3) mode is 108, but uses only 84 for LCD */
139072b676d7Smrg	      if(FakeForCRT2) return 108000;
139172b676d7Smrg	      else            return 84000;
139272b676d7Smrg	   } else if(pSiS->VBLCDFlags & (VB_LCD_1280x1024 | VB_LCD_1280x960)) {
139372b676d7Smrg	      return 108000;
139472b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1400x1050) {
139572b676d7Smrg	      /* Fake clock; VGA mode is 122, but uses only 108 for LCD */
139672b676d7Smrg	      if(FakeForCRT2) return 123000;
139772b676d7Smrg	      else            return 108000;
139872b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1680x1050) {
139972b676d7Smrg	      /* Fake clock; VGA mode is 147, but uses only 122 for LCD */
140072b676d7Smrg	      if(FakeForCRT2) return 148000;
140172b676d7Smrg	      else            return 122000;
140272b676d7Smrg	   } else if(pSiS->VBLCDFlags & VB_LCD_1600x1200) {
140372b676d7Smrg	      return 162000;
140472b676d7Smrg	   } else if((pSiS->VBLCDFlags & VB_LCD_CUSTOM) && (pSiS->SiS_Pr->CP_MaxClock)) {
140572b676d7Smrg	      return pSiS->SiS_Pr->CP_MaxClock;
140672b676d7Smrg	   } else {
140772b676d7Smrg	      if(pSiS->VBFlags2 & VB2_30xC) return 162000;
140872b676d7Smrg	      else                          return 108000;
140972b676d7Smrg	   }
141072b676d7Smrg	} else if(pSiS->VBFlags & CRT2_TV) {
141172b676d7Smrg	   if(pSiS->VBFlags2 & VB2_CHRONTEL) {
141272b676d7Smrg	      switch(pSiS->VGAEngine) {
141372b676d7Smrg	      case SIS_300_VGA:
141472b676d7Smrg		 return 50000;	/* 700x: <= 800x600 */
141572b676d7Smrg	      case SIS_315_VGA:
141672b676d7Smrg	      default:
141772b676d7Smrg		 return 70000;  /* 701x: <= 1024x768 */
141872b676d7Smrg	      }
141972b676d7Smrg	   } else if(pSiS->VBFlags2 & VB2_SISBRIDGE) {
142072b676d7Smrg	      if(pSiS->SiS_SD_Flags & (SiS_SD_SUPPORTYPBPR|SiS_SD_SUPPORTHIVISION)) {
142172b676d7Smrg	         if(FakeForCRT2) return 108000;  /* 1280x1024@60 (faked) */
142272b676d7Smrg	         else            return 75000;   /* Really used clock */
142372b676d7Smrg	      } else {
142472b676d7Smrg		 return 70000;
142572b676d7Smrg	      }
142672b676d7Smrg	   }
142772b676d7Smrg	}
142872b676d7Smrg
142972b676d7Smrg	return 0;
143072b676d7Smrg}
143172b676d7Smrg
143272b676d7Smrg/* Calculate the maximum dotclock */
143372b676d7Smrgint SiSMemBandWidth(ScrnInfoPtr pScrn, Bool IsForCRT2)
143472b676d7Smrg{
143572b676d7Smrg	SISPtr pSiS = SISPTR(pScrn);
143672b676d7Smrg#ifdef SISDUALHEAD
143772b676d7Smrg	SISEntPtr pSiSEnt = pSiS->entityPrivate;
143872b676d7Smrg#endif
143972b676d7Smrg	int          bus = pSiS->BusWidth;
144072b676d7Smrg	int          mclk = pSiS->MemClock;
144172b676d7Smrg	int          bpp = pSiS->CurrentLayout.bitsPerPixel;
144272b676d7Smrg	int	     max = 0;
144372b676d7Smrg	float        magic = 0.0, total;
144472b676d7Smrg	int          bytesperpixel = (bpp + 7) / 8;
144572b676d7Smrg	float  	     crt2used, maxcrt2;
144672b676d7Smrg	int	     crt2clock;
144772b676d7Smrg	Bool	     DHM, GetForCRT1;
144872b676d7Smrg#ifdef __SUNPRO_C
144972b676d7Smrg#define const
145072b676d7Smrg#endif
145172b676d7Smrg	const float  magicDED[4] = { 1.2,      1.368421, 2.263158, 1.2};
145272b676d7Smrg	const float  magicINT[4] = { 1.441177, 1.441177, 2.588235, 1.441177 };
145372b676d7Smrg#ifdef __SUNPRO_C
145472b676d7Smrg#undef const
145572b676d7Smrg#endif
145672b676d7Smrg
145772b676d7Smrg	switch(pSiS->Chipset) {
145872b676d7Smrg
145972b676d7Smrg	case PCI_CHIP_SIS5597:
146072b676d7Smrg		total = ((mclk * (bus / 8)) * 0.7) / bytesperpixel;
146172b676d7Smrg		if(total > 135000) total = 135000;
146272b676d7Smrg		xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
146372b676d7Smrg			"Maximum pixel clock at %d bpp is %g MHz\n",
146472b676d7Smrg			bpp, total/1000);
146572b676d7Smrg		return(int)(total);
146672b676d7Smrg
146772b676d7Smrg	case PCI_CHIP_SIS6326:
146872b676d7Smrg		total = ((mclk * (bus / 8)) * 0.7) / bytesperpixel;
146972b676d7Smrg		if(total > 175500) total = 175500;
147072b676d7Smrg		xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
147172b676d7Smrg			"Maximum pixel clock at %d bpp is %g MHz\n",
147272b676d7Smrg			bpp, total/1000);
147372b676d7Smrg		return(int)(total);
147472b676d7Smrg
147572b676d7Smrg	case PCI_CHIP_SIS530:
147672b676d7Smrg		total = ((mclk * (bus / 8)) * 0.7) / bytesperpixel;
147772b676d7Smrg		if(total > 230000) total = 230000;
147872b676d7Smrg		xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
147972b676d7Smrg			"Maximum pixel clock at %d bpp is %g MHz\n",
148072b676d7Smrg			bpp, total/1000);
148172b676d7Smrg		return(int)(total);
148272b676d7Smrg
148372b676d7Smrg	case PCI_CHIP_SIS300:
148472b676d7Smrg	case PCI_CHIP_SIS540:
148572b676d7Smrg	case PCI_CHIP_SIS630:
148672b676d7Smrg	case PCI_CHIP_SIS315:
148772b676d7Smrg	case PCI_CHIP_SIS315H:
148872b676d7Smrg	case PCI_CHIP_SIS315PRO:
148972b676d7Smrg	case PCI_CHIP_SIS550:
149072b676d7Smrg	case PCI_CHIP_SIS650:
149172b676d7Smrg	case PCI_CHIP_SIS330:
149272b676d7Smrg	case PCI_CHIP_SIS660:
149372b676d7Smrg	case PCI_CHIP_SIS340:
149472b676d7Smrg	case PCI_CHIP_XGIXG20:
149572b676d7Smrg	case PCI_CHIP_XGIXG40:
149672b676d7Smrg		switch(pSiS->Chipset) {
149772b676d7Smrg		case PCI_CHIP_SIS300:
149872b676d7Smrg		    magic = magicDED[bus/64];
149972b676d7Smrg		    max = 540000;
150072b676d7Smrg		    break;
150172b676d7Smrg		case PCI_CHIP_SIS540:
150272b676d7Smrg		case PCI_CHIP_SIS630:
150372b676d7Smrg		    magic = magicINT[bus/64];
150472b676d7Smrg		    max = 540000;
150572b676d7Smrg		    break;
150672b676d7Smrg		case PCI_CHIP_SIS315:
150772b676d7Smrg		case PCI_CHIP_SIS315H:
150872b676d7Smrg		case PCI_CHIP_SIS315PRO:
150972b676d7Smrg		case PCI_CHIP_SIS330:
151072b676d7Smrg		    magic = magicDED[bus/64];
151172b676d7Smrg		    max = 780000;
151272b676d7Smrg		    break;
151372b676d7Smrg		case PCI_CHIP_SIS550:
151472b676d7Smrg		    magic = magicINT[bus/64];
151572b676d7Smrg		    max = 610000;
151672b676d7Smrg		    break;
151772b676d7Smrg		case PCI_CHIP_SIS650:
151872b676d7Smrg		    magic = magicINT[bus/64];
151972b676d7Smrg		    max = 680000;
152072b676d7Smrg		    break;
152172b676d7Smrg		case PCI_CHIP_SIS660:
152272b676d7Smrg		    if((pSiS->ChipType >= SIS_660) &&
152372b676d7Smrg		       (pSiS->ChipFlags & SiSCF_760LFB)) {
152472b676d7Smrg		       magic = magicDED[bus/64];
152572b676d7Smrg		    } else {
152672b676d7Smrg		       magic = magicINT[bus/64];
152772b676d7Smrg		    }
152872b676d7Smrg		    max = 680000;
152972b676d7Smrg		case PCI_CHIP_SIS340:
153072b676d7Smrg		case PCI_CHIP_XGIXG40:
153172b676d7Smrg		    magic = magicDED[bus/64];
153272b676d7Smrg		    max = 800000;
153372b676d7Smrg		    break;
153472b676d7Smrg		case PCI_CHIP_XGIXG20:
153572b676d7Smrg		    magic = 1.0; /* magicDED[bus/64]; */
153672b676d7Smrg		    max = 332000;
153772b676d7Smrg		    break;
153872b676d7Smrg		}
153972b676d7Smrg
154072b676d7Smrg		PDEBUG(ErrorF("mclk: %d, bus: %d, magic: %g, bpp: %d\n",
154172b676d7Smrg				mclk, bus, magic, bpp));
154272b676d7Smrg
154372b676d7Smrg		total = mclk * bus / bpp;
154472b676d7Smrg
154572b676d7Smrg		xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
154672b676d7Smrg			"Memory bandwidth at %d bpp is %g MHz\n", bpp, total/1000);
154772b676d7Smrg
154872b676d7Smrg		if((pSiS->VBFlags & CRT2_ENABLE) && (!pSiS->CRT1off)) {
154972b676d7Smrg
155072b676d7Smrg		    maxcrt2 = 135000;
155172b676d7Smrg		    if(pSiS->VBFlags2 & (VB2_301B|VB2_302B)) maxcrt2 = 162000;
155272b676d7Smrg		    else if(pSiS->VBFlags2 & VB2_301C)       maxcrt2 = 203000;
155372b676d7Smrg		    else if(pSiS->VBFlags2 & VB2_307T)       maxcrt2 = 203000; /* TODO */
155472b676d7Smrg		    /* if(pSiS->VBFlags2 & VB2_30xBDH)       maxcrt2 = 100000;
155572b676d7Smrg		       Ignore 301B-DH here; seems the current version is like
155672b676d7Smrg		       301B anyway */
155772b676d7Smrg
155872b676d7Smrg		    crt2used = 0.0;
155972b676d7Smrg		    crt2clock = SiSEstimateCRT2Clock(pScrn, IsForCRT2);
156072b676d7Smrg		    if(crt2clock) {
156172b676d7Smrg		       crt2used = crt2clock + 2000;
156272b676d7Smrg		    }
156372b676d7Smrg		    DHM = FALSE;
156472b676d7Smrg		    GetForCRT1 = FALSE;
156572b676d7Smrg
156672b676d7Smrg#ifdef SISDUALHEAD
156772b676d7Smrg		    if((pSiS->DualHeadMode) && (pSiSEnt)) {
156872b676d7Smrg		       DHM = TRUE;
156972b676d7Smrg		       if(pSiS->SecondHead) GetForCRT1 = TRUE;
157072b676d7Smrg		    }
157172b676d7Smrg#endif
157272b676d7Smrg#ifdef SISMERGED
157372b676d7Smrg		    if(pSiS->MergedFB && IsForCRT2) {
157472b676d7Smrg		       DHM = TRUE;
157572b676d7Smrg		       GetForCRT1 = FALSE;
157672b676d7Smrg		    }
157772b676d7Smrg#endif
157872b676d7Smrg
157972b676d7Smrg		    if(DHM) {
158072b676d7Smrg
158172b676d7Smrg			if(!GetForCRT1) {
158272b676d7Smrg
158372b676d7Smrg			     /* First head = CRT2 */
158472b676d7Smrg
158572b676d7Smrg			     if(crt2clock) {
158672b676d7Smrg				/* We use the mem bandwidth as max clock; this
158772b676d7Smrg				 * might exceed the 70% limit a bit, but that
158872b676d7Smrg				 * does not matter; we take care of that limit
158972b676d7Smrg				 * when we calc CRT1. Overall, we might use up
159072b676d7Smrg				 * to 85% of the memory bandwidth, which seems
159172b676d7Smrg				 * enough to use accel and video.
159272b676d7Smrg				 * The "* macic" is just to compensate the
159372b676d7Smrg				 * calculation below.
159472b676d7Smrg				*/
159572b676d7Smrg				total = crt2used * magic;
159672b676d7Smrg
159772b676d7Smrg			     } else {
159872b676d7Smrg				/*  We don't know about the second head's
159972b676d7Smrg				 *  depth yet. So we assume it uses the
160072b676d7Smrg				 *  same. But since the maximum dotclock
160172b676d7Smrg				 *  is limited on CRT2, we can assume a
160272b676d7Smrg				 *  maximum here.
160372b676d7Smrg				 */
160472b676d7Smrg				if((total / 2) > (maxcrt2 + 2000)) {
160572b676d7Smrg				    total = (maxcrt2 + 2000) * magic;
160672b676d7Smrg				    crt2used = maxcrt2 + 2000;
160772b676d7Smrg				} else {
160872b676d7Smrg				    total /= 2;
160972b676d7Smrg				    crt2used = total;
161072b676d7Smrg				}
161172b676d7Smrg
161272b676d7Smrg			     }
161372b676d7Smrg
161472b676d7Smrg			     xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
161572b676d7Smrg				  "Bandwidth reserved for CRT2 is %g MHz\n",
161672b676d7Smrg				      crt2used/1000);
161772b676d7Smrg
161872b676d7Smrg			} else {
161972b676d7Smrg#ifdef SISDUALHEAD
162072b676d7Smrg			     /* Second head = CRT1 */
162172b676d7Smrg
162272b676d7Smrg			     /*     Now We know about the first head's depth,
162372b676d7Smrg			      *     so we can calculate more accurately.
162472b676d7Smrg			      */
162572b676d7Smrg
162672b676d7Smrg			     if(crt2clock) {
162772b676d7Smrg				total -= (crt2used * pSiSEnt->pScrn_1->bitsPerPixel / bpp);
162872b676d7Smrg				xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
162972b676d7Smrg				   "Bandwidth reserved for CRT2 at %d bpp is %g Mhz\n",
163072b676d7Smrg				      bpp,
163172b676d7Smrg				      (crt2used * pSiSEnt->pScrn_1->bitsPerPixel / bpp)/1000);
163272b676d7Smrg			     } else {
163372b676d7Smrg				total -= (pSiSEnt->maxUsedClock * pSiSEnt->pScrn_1->bitsPerPixel / bpp);
163472b676d7Smrg				xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
163572b676d7Smrg				   "Bandwidth reserved for CRT2 at %d bpp is %d Mhz\n",
163672b676d7Smrg				      bpp,
163772b676d7Smrg				      (pSiSEnt->maxUsedClock * pSiSEnt->pScrn_1->bitsPerPixel / bpp)/1000);
163872b676d7Smrg			     }
163972b676d7Smrg
164072b676d7Smrg			     xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
164172b676d7Smrg				 "Bandwidth available for CRT1 is %g MHz\n", total/1000);
164272b676d7Smrg#endif
164372b676d7Smrg			}
164472b676d7Smrg
164572b676d7Smrg		    } else {
164672b676d7Smrg
164772b676d7Smrg			if(crt2clock) {
164872b676d7Smrg			    total -= crt2used;
164972b676d7Smrg			} else {
165072b676d7Smrg                            if((total / 2) > (maxcrt2 + 2000)) {
165172b676d7Smrg				total -= (maxcrt2 + 2000);
165272b676d7Smrg				crt2used = maxcrt2 + 2000;
165372b676d7Smrg			    } else {
165472b676d7Smrg				total /= 2;
165572b676d7Smrg				crt2used = total;
165672b676d7Smrg			    }
165772b676d7Smrg			}
165872b676d7Smrg			xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
165972b676d7Smrg			  "Bandwidth reserved for CRT2 is %g Mhz\n", crt2used/1000);
166072b676d7Smrg
166172b676d7Smrg			xf86DrvMsg(pScrn->scrnIndex, X_PROBED,
166272b676d7Smrg			  "Bandwidth available for CRT1 is %g MHz\n", total/1000);
166372b676d7Smrg
166472b676d7Smrg		    }
166572b676d7Smrg
166672b676d7Smrg		}
166772b676d7Smrg
166872b676d7Smrg		total /= magic;
166972b676d7Smrg		if(total > (max / 2)) total = max / 2;
167072b676d7Smrg		return(int)(total);
167172b676d7Smrg
167272b676d7Smrg        default:
167372b676d7Smrg		return(135000);
167472b676d7Smrg        }
167572b676d7Smrg}
167672b676d7Smrg
167772b676d7Smrg/* Load the palette. We do this for all supported color depths
167872b676d7Smrg * in order to support gamma correction. We hereby convert the
167972b676d7Smrg * given colormap to a complete 24bit color palette and enable
168072b676d7Smrg * the correspoding bit in SR7 to enable the 24bit lookup table.
168172b676d7Smrg * Gamma correction for CRT2 is only supported on SiS video bridges.
168272b676d7Smrg * There are there 6-bit-RGB values submitted even if bpp is 16 and
168372b676d7Smrg * weight is 565, because SetWeight() sets rgbBits to the maximum
168472b676d7Smrg * (which is 6 in the 565 case).
168572b676d7Smrg */
168672b676d7Smrgvoid
168772b676d7SmrgSISLoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors,
168872b676d7Smrg               VisualPtr pVisual)
168972b676d7Smrg{
169072b676d7Smrg     SISPtr  pSiS = SISPTR(pScrn);
169172b676d7Smrg     int     i, j, index;
169272b676d7Smrg     int     myshift = 8 - pScrn->rgbBits;
169372b676d7Smrg     UChar   backup = 0;
169472b676d7Smrg     Bool    dogamma1 = pSiS->CRT1gamma;
169572b676d7Smrg     Bool    resetxvgamma = FALSE;
169672b676d7Smrg#ifdef SISDUALHEAD
169772b676d7Smrg     SISEntPtr pSiSEnt = pSiS->entityPrivate;
169872b676d7Smrg
169972b676d7Smrg     if(pSiS->DualHeadMode) dogamma1 = pSiSEnt->CRT1gamma;
170072b676d7Smrg#endif
170172b676d7Smrg
170272b676d7Smrg     PDEBUG(xf86DrvMsg(pScrn->scrnIndex, X_INFO, "LoadPalette()\n"));
170372b676d7Smrg
170472b676d7Smrg#ifdef SISDUALHEAD
170572b676d7Smrg     if((!pSiS->DualHeadMode) || (pSiS->SecondHead)) {
170672b676d7Smrg#endif
170772b676d7Smrg
170872b676d7Smrg	if(pSiS->VGAEngine == SIS_315_VGA) {
170972b676d7Smrg	   inSISIDXREG(SISSR, 0x1f, backup);
171072b676d7Smrg	   andSISIDXREG(SISSR, 0x1f, 0xe7);
171172b676d7Smrg	   if( (pSiS->XvGamma) &&
171272b676d7Smrg	       (pSiS->MiscFlags & MISC_CRT1OVERLAYGAMMA) &&
171372b676d7Smrg	       ((pSiS->CurrentLayout.depth == 16) ||
171472b676d7Smrg	        (pSiS->CurrentLayout.depth == 24)) ) {
171572b676d7Smrg	      orSISIDXREG(SISSR, 0x1f, 0x10);
171672b676d7Smrg	      resetxvgamma = TRUE;
171772b676d7Smrg	   }
171872b676d7Smrg	}
171972b676d7Smrg
172072b676d7Smrg	switch(pSiS->CurrentLayout.depth) {
172172b676d7Smrg	  case 15:
172272b676d7Smrg	     if(dogamma1) {
172372b676d7Smrg		orSISIDXREG(SISSR, 0x07, 0x04);
172472b676d7Smrg		/* 315/330: depth 15 not supported, no MMIO code needed */
172572b676d7Smrg		for(i=0; i<numColors; i++) {
172672b676d7Smrg		   index = indices[i];
172772b676d7Smrg		   if(index < 32) {   /* Paranoia */
172872b676d7Smrg		      for(j=0; j<8; j++) {
172972b676d7Smrg			 outSISREG(SISCOLIDX, (index << 3) + j);
173072b676d7Smrg			 outSISREG(SISCOLDATA, colors[index].red   << myshift);
173172b676d7Smrg			 outSISREG(SISCOLDATA, colors[index].green << myshift);
173272b676d7Smrg			 outSISREG(SISCOLDATA, colors[index].blue  << myshift);
173372b676d7Smrg		      }
173472b676d7Smrg		   }
173572b676d7Smrg		}
173672b676d7Smrg	     } else {
173772b676d7Smrg		andSISIDXREG(SISSR, 0x07, ~0x04);
173872b676d7Smrg	     }
173972b676d7Smrg	     break;
174072b676d7Smrg	  case 16:
174172b676d7Smrg	     if(dogamma1) {
174272b676d7Smrg		orSISIDXREG(SISSR, 0x07, 0x04);
174372b676d7Smrg		if(pSiS->ChipFlags & SiSCF_MMIOPalette) {
174472b676d7Smrg		   for(i=0; i<numColors; i++) {
174572b676d7Smrg		      index = indices[i];
174672b676d7Smrg		      if(index < 64) {  /* Paranoia */
174772b676d7Smrg			 for(j=0; j<4; j++) {
174872b676d7Smrg			    SIS_MMIO_OUT32(pSiS->IOBase, 0x8570,
174972b676d7Smrg					   (colors[index].green     << (myshift + 8))  |
175072b676d7Smrg					   (colors[index >> 1].blue << (myshift + 16)) |
175172b676d7Smrg					   (colors[index >> 1].red  << myshift)        |
175272b676d7Smrg					   (((index << 2) + j)      << 24));
175372b676d7Smrg			 }
175472b676d7Smrg		      }
175572b676d7Smrg		   }
175672b676d7Smrg		} else {
175772b676d7Smrg		   for(i=0; i<numColors; i++) {
175872b676d7Smrg		      index = indices[i];
175972b676d7Smrg		      if(index < 64) {  /* Paranoia */
176072b676d7Smrg			 for(j=0; j<4; j++) {
176172b676d7Smrg			    outSISREG(SISCOLIDX, (index << 2) + j);
176272b676d7Smrg			    outSISREG(SISCOLDATA, colors[index >> 1].red  << myshift);
176372b676d7Smrg			    outSISREG(SISCOLDATA, colors[index].green     << myshift);
176472b676d7Smrg			    outSISREG(SISCOLDATA, colors[index >> 1].blue << myshift);
176572b676d7Smrg			 }
176672b676d7Smrg		      }
176772b676d7Smrg		   }
176872b676d7Smrg		}
176972b676d7Smrg	     } else {
177072b676d7Smrg		andSISIDXREG(SISSR, 0x07, ~0x04);
177172b676d7Smrg	     }
177272b676d7Smrg	     break;
177372b676d7Smrg          case 24:
177472b676d7Smrg	     if(dogamma1) {
177572b676d7Smrg		orSISIDXREG(SISSR, 0x07, 0x04);
177672b676d7Smrg		if(pSiS->ChipFlags & SiSCF_MMIOPalette) {
177772b676d7Smrg		   for(i=0; i<numColors; i++)  {
177872b676d7Smrg		      index = indices[i];
177972b676d7Smrg		      if(index < 256) {   /* Paranoia */
178072b676d7Smrg			 SIS_MMIO_OUT32(pSiS->IOBase, 0x8570,
178172b676d7Smrg					(colors[index].blue  << 16) |
178272b676d7Smrg					(colors[index].green <<  8) |
178372b676d7Smrg					(colors[index].red)         |
178472b676d7Smrg					(index               << 24));
178572b676d7Smrg		      }
178672b676d7Smrg		   }
178772b676d7Smrg		} else {
178872b676d7Smrg		   for(i=0; i<numColors; i++)  {
178972b676d7Smrg		      index = indices[i];
179072b676d7Smrg		      if(index < 256) {   /* Paranoia */
179172b676d7Smrg			 outSISREG(SISCOLIDX, index);
179272b676d7Smrg			 outSISREG(SISCOLDATA, colors[index].red);
179372b676d7Smrg			 outSISREG(SISCOLDATA, colors[index].green);
179472b676d7Smrg			 outSISREG(SISCOLDATA, colors[index].blue);
179572b676d7Smrg		      }
179672b676d7Smrg		   }
179772b676d7Smrg		}
179872b676d7Smrg	     } else {
179972b676d7Smrg		andSISIDXREG(SISSR, 0x07, ~0x04);
180072b676d7Smrg	     }
180172b676d7Smrg	     break;
180272b676d7Smrg	  default:
180372b676d7Smrg	     andSISIDXREG(SISSR, 0x07, ~0x04);
180472b676d7Smrg	     if(pSiS->ChipFlags & SiSCF_MMIOPalette) {
180572b676d7Smrg	        for(i=0; i<numColors; i++)  {
180672b676d7Smrg		   index = indices[i];
180772b676d7Smrg		   SIS_MMIO_OUT32(pSiS->IOBase, 0x8570,
180872b676d7Smrg				  ((colors[index].blue)  << 16) |
180972b676d7Smrg				  ((colors[index].green) <<  8) |
181072b676d7Smrg				  (colors[index].red)           |
181172b676d7Smrg				  (index                 << 24));
181272b676d7Smrg		}
181372b676d7Smrg	     } else {
181472b676d7Smrg		for(i=0; i<numColors; i++) {
181572b676d7Smrg		   /* In pio mode, only 6 bits are supported */
181672b676d7Smrg		   index = indices[i];
181772b676d7Smrg		   outSISREG(SISCOLIDX, index);
181872b676d7Smrg		   outSISREG(SISCOLDATA, colors[index].red   >> 2);
181972b676d7Smrg		   outSISREG(SISCOLDATA, colors[index].green >> 2);
182072b676d7Smrg		   outSISREG(SISCOLDATA, colors[index].blue  >> 2);
182172b676d7Smrg		}
182272b676d7Smrg	     }
182372b676d7Smrg	}
182472b676d7Smrg
182572b676d7Smrg	if(pSiS->VGAEngine == SIS_315_VGA) {
182672b676d7Smrg	   outSISIDXREG(SISSR, 0x1f, backup);
182772b676d7Smrg	   inSISIDXREG(SISSR, 0x07, backup);
182872b676d7Smrg	   if((backup & 0x04) && (resetxvgamma) && (pSiS->ResetXvGamma)) {
182972b676d7Smrg	      (pSiS->ResetXvGamma)(pScrn);
183072b676d7Smrg	   }
183172b676d7Smrg	}
183272b676d7Smrg
183372b676d7Smrg#ifdef SISDUALHEAD
183472b676d7Smrg    }
183572b676d7Smrg#endif
183672b676d7Smrg
183772b676d7Smrg#ifdef SISDUALHEAD
183872b676d7Smrg    if((!pSiS->DualHeadMode) || (!pSiS->SecondHead)) {
183972b676d7Smrg#endif
184072b676d7Smrg       switch(pSiS->VGAEngine) {
184172b676d7Smrg       case SIS_300_VGA:
184272b676d7Smrg       case SIS_315_VGA:
184372b676d7Smrg	  if(pSiS->VBFlags & CRT2_ENABLE) {
184472b676d7Smrg	     /* Only the SiS bridges support a CRT2 palette */
184572b676d7Smrg	     if(pSiS->VBFlags2 & VB2_SISBRIDGE) {
184672b676d7Smrg		if((pSiS->CRT2SepGamma) && (pSiS->crt2cindices) && (pSiS->crt2colors)) {
184772b676d7Smrg		   SiS301LoadPalette(pScrn, numColors, pSiS->crt2cindices, pSiS->crt2colors, myshift);
184872b676d7Smrg		} else {
184972b676d7Smrg		   SiS301LoadPalette(pScrn, numColors, indices, colors, myshift);
185072b676d7Smrg		}
185172b676d7Smrg	     }
185272b676d7Smrg          }
185372b676d7Smrg       }
185472b676d7Smrg#ifdef SISDUALHEAD
185572b676d7Smrg    }
185672b676d7Smrg#endif
185772b676d7Smrg
185872b676d7Smrg}
185972b676d7Smrg
186072b676d7Smrgvoid
186172b676d7SmrgSiS_UpdateGammaCRT2(ScrnInfoPtr pScrn)
186272b676d7Smrg{
186372b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
186472b676d7Smrg
186572b676d7Smrg    if((!pSiS->CRT2SepGamma) || (!pSiS->crt2cindices) || (!pSiS->crt2gcolortable)) return;
186672b676d7Smrg
186772b676d7Smrg#ifdef SISDUALHEAD
186872b676d7Smrg    if(pSiS->DualHeadMode) return;
186972b676d7Smrg#endif
187072b676d7Smrg
187172b676d7Smrg    SISCalculateGammaRampCRT2(pScrn);
187272b676d7Smrg    SiS301LoadPalette(pScrn, pSiS->CRT2ColNum, pSiS->crt2cindices, pSiS->crt2colors, (8 - pScrn->rgbBits));
187372b676d7Smrg}
187472b676d7Smrg
187572b676d7Smrgstatic  void
187672b676d7SmrgSiS301LoadPalette(ScrnInfoPtr pScrn, int numColors, int *indices, LOCO *colors, int myshift)
187772b676d7Smrg{
187872b676d7Smrg	SISPtr  pSiS = SISPTR(pScrn);
187972b676d7Smrg	int     i, j, index;
188072b676d7Smrg	Bool    dogamma2 = pSiS->CRT2gamma;
188172b676d7Smrg#ifdef SISDUALHEAD
188272b676d7Smrg	SISEntPtr pSiSEnt = pSiS->entityPrivate;
188372b676d7Smrg
188472b676d7Smrg	if(pSiS->DualHeadMode) dogamma2 = pSiSEnt->CRT2gamma;
188572b676d7Smrg#endif
188672b676d7Smrg
188772b676d7Smrg	/* 301B-DH does not support a color palette for LCD */
188872b676d7Smrg	if((pSiS->VBFlags2 & VB2_30xBDH) && (pSiS->VBFlags & CRT2_LCD)) return;
188972b676d7Smrg
189072b676d7Smrg	switch(pSiS->CurrentLayout.depth) {
189172b676d7Smrg          case 15:
189272b676d7Smrg	     if(dogamma2) {
189372b676d7Smrg		orSISIDXREG(SISPART4, 0x0d, 0x08);
189472b676d7Smrg		for(i=0; i<numColors; i++) {
189572b676d7Smrg		   index = indices[i];
189672b676d7Smrg		   if(index < 32) {   /* Paranoia */
189772b676d7Smrg		      for(j=0; j<8; j++) {
189872b676d7Smrg			 outSISREG(SISCOL2IDX, (index << 3) + j);
189972b676d7Smrg			 outSISREG(SISCOL2DATA, colors[index].red   << myshift);
190072b676d7Smrg			 outSISREG(SISCOL2DATA, colors[index].green << myshift);
190172b676d7Smrg			 outSISREG(SISCOL2DATA, colors[index].blue  << myshift);
190272b676d7Smrg		      }
190372b676d7Smrg		   }
190472b676d7Smrg		}
190572b676d7Smrg	     } else {
190672b676d7Smrg		andSISIDXREG(SISPART4, 0x0d, ~0x08);
190772b676d7Smrg	     }
190872b676d7Smrg	     break;
190972b676d7Smrg	  case 16:
191072b676d7Smrg	     if(dogamma2) {
191172b676d7Smrg		orSISIDXREG(SISPART4, 0x0d, 0x08);
191272b676d7Smrg		for(i = 0; i < numColors; i++) {
191372b676d7Smrg		   index = indices[i];
191472b676d7Smrg		   if(index < 64) {  /* Paranoia */
191572b676d7Smrg		      for(j = 0; j < 4; j++) {
191672b676d7Smrg			 outSISREG(SISCOL2IDX, (index << 2) + j);
191772b676d7Smrg			 outSISREG(SISCOL2DATA, colors[index >> 1].red  << myshift);
191872b676d7Smrg			 outSISREG(SISCOL2DATA, colors[index].green     << myshift);
191972b676d7Smrg			 outSISREG(SISCOL2DATA, colors[index >> 1].blue << myshift);
192072b676d7Smrg		      }
192172b676d7Smrg		   }
192272b676d7Smrg		}
192372b676d7Smrg	     } else {
192472b676d7Smrg		andSISIDXREG(SISPART4, 0x0d, ~0x08);
192572b676d7Smrg	     }
192672b676d7Smrg	     break;
192772b676d7Smrg          case 24:
192872b676d7Smrg	     if(dogamma2) {
192972b676d7Smrg		orSISIDXREG(SISPART4, 0x0d, 0x08);
193072b676d7Smrg		for(i = 0; i < numColors; i++) {
193172b676d7Smrg		   index = indices[i];
193272b676d7Smrg		   if(index < 256) {   /* Paranoia */
193372b676d7Smrg		      outSISREG(SISCOL2IDX, index);
193472b676d7Smrg		      outSISREG(SISCOL2DATA, colors[index].red);
193572b676d7Smrg		      outSISREG(SISCOL2DATA, colors[index].green);
193672b676d7Smrg		      outSISREG(SISCOL2DATA, colors[index].blue);
193772b676d7Smrg		   }
193872b676d7Smrg        	}
193972b676d7Smrg	     } else {
194072b676d7Smrg		andSISIDXREG(SISPART4, 0x0d, ~0x08);
194172b676d7Smrg	     }
194272b676d7Smrg	     break;
194372b676d7Smrg	  default:
194472b676d7Smrg	     orSISIDXREG(SISPART4, 0x0d, 0x08);
194572b676d7Smrg	     for(i = 0; i < numColors; i++) {
194672b676d7Smrg		index = indices[i];
194772b676d7Smrg		outSISREG(SISCOL2IDX,  index);
194872b676d7Smrg		outSISREG(SISCOL2DATA, colors[index].red);
194972b676d7Smrg		outSISREG(SISCOL2DATA, colors[index].green);
195072b676d7Smrg		outSISREG(SISCOL2DATA, colors[index].blue);
195172b676d7Smrg             }
195272b676d7Smrg	 }
195372b676d7Smrg}
195472b676d7Smrg
195572b676d7Smrgvoid
195672b676d7SmrgSISDACPreInit(ScrnInfoPtr pScrn)
195772b676d7Smrg{
195872b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
195972b676d7Smrg    Bool IsForCRT2 = FALSE;
196072b676d7Smrg
196172b676d7Smrg#ifdef SISDUALHEAD
196272b676d7Smrg    if((pSiS->DualHeadMode) && (!pSiS->SecondHead))
196372b676d7Smrg       IsForCRT2 = TRUE;
196472b676d7Smrg#endif
196572b676d7Smrg
196672b676d7Smrg    pSiS->MaxClock = SiSMemBandWidth(pScrn, IsForCRT2);
196772b676d7Smrg
196872b676d7Smrg    switch (pSiS->Chipset) {
196972b676d7Smrg       case PCI_CHIP_SIS550:
197072b676d7Smrg       case PCI_CHIP_SIS315:
197172b676d7Smrg       case PCI_CHIP_SIS315H:
197272b676d7Smrg       case PCI_CHIP_SIS315PRO:
197372b676d7Smrg       case PCI_CHIP_SIS650:
197472b676d7Smrg       case PCI_CHIP_SIS330:
197572b676d7Smrg       case PCI_CHIP_SIS660:
197672b676d7Smrg       case PCI_CHIP_SIS340:
197772b676d7Smrg       case PCI_CHIP_XGIXG20:
197872b676d7Smrg       case PCI_CHIP_XGIXG40:
197972b676d7Smrg          pSiS->SiSSave     = SiS315Save;
198072b676d7Smrg          pSiS->SiSRestore  = SiS315Restore;
198172b676d7Smrg          break;
198272b676d7Smrg       case PCI_CHIP_SIS300:
198372b676d7Smrg       case PCI_CHIP_SIS540:
198472b676d7Smrg       case PCI_CHIP_SIS630:
198572b676d7Smrg          pSiS->SiSSave     = SiS300Save;
198672b676d7Smrg          pSiS->SiSRestore  = SiS300Restore;
198772b676d7Smrg          break;
198872b676d7Smrg       case PCI_CHIP_SIS5597:
198972b676d7Smrg       case PCI_CHIP_SIS6326:
199072b676d7Smrg       case PCI_CHIP_SIS530:
199172b676d7Smrg       default:
199272b676d7Smrg          pSiS->SiSSave     = SiSSave;
199372b676d7Smrg          pSiS->SiSRestore  = SiSRestore;
199472b676d7Smrg          break;
199572b676d7Smrg    }
199672b676d7Smrg}
199772b676d7Smrg
199872b676d7Smrgstatic void
199972b676d7SmrgSetBlock(CARD16 port, CARD8 from, CARD8 to, CARD8 *DataPtr)
200072b676d7Smrg{
200172b676d7Smrg    CARD8 index;
200272b676d7Smrg
200372b676d7Smrg    for(index = from; index <= to; index++, DataPtr++) {
200472b676d7Smrg       outSISIDXREG(port, index, *DataPtr);
200572b676d7Smrg    }
200672b676d7Smrg}
200772b676d7Smrg
200872b676d7Smrgvoid
200972b676d7SmrgSiS6326SetTVReg(ScrnInfoPtr pScrn, CARD8 index, CARD8 data)
201072b676d7Smrg{
201172b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
201272b676d7Smrg    outSISIDXREG(SISCR, 0xE0, index);
201372b676d7Smrg    outSISIDXREG(SISCR, 0xE1, data);
201472b676d7Smrg#ifdef TWDEBUG
201572b676d7Smrg    xf86DrvMsg(0, X_INFO, "SiS6326: Setting Tv %02x to %02x\n", index, data);
201672b676d7Smrg#endif
201772b676d7Smrg}
201872b676d7Smrg
201972b676d7SmrgUChar
202072b676d7SmrgSiS6326GetTVReg(ScrnInfoPtr pScrn, CARD8 index)
202172b676d7Smrg{
202272b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
202372b676d7Smrg    UChar  data;
202472b676d7Smrg
202572b676d7Smrg    outSISIDXREG(SISCR, 0xE0, index);
202672b676d7Smrg    inSISIDXREG(SISCR, 0xE1, data);
202772b676d7Smrg    return(data);
202872b676d7Smrg}
202972b676d7Smrg
203072b676d7Smrgvoid
203172b676d7SmrgSiS6326SetXXReg(ScrnInfoPtr pScrn, CARD8 index, CARD8 data)
203272b676d7Smrg{
203372b676d7Smrg    SISPtr  pSiS = SISPTR(pScrn);
203472b676d7Smrg    outSISIDXREG(SISCR, 0xE2, index);
203572b676d7Smrg    outSISIDXREG(SISCR, 0xE3, data);
203672b676d7Smrg}
203772b676d7Smrg
203872b676d7SmrgUChar
203972b676d7SmrgSiS6326GetXXReg(ScrnInfoPtr pScrn, CARD8 index)
204072b676d7Smrg{
204172b676d7Smrg    SISPtr pSiS = SISPTR(pScrn);
204272b676d7Smrg    UChar  data;
204372b676d7Smrg
204472b676d7Smrg    outSISIDXREG(SISCR, 0xE2, index);
204572b676d7Smrg    inSISIDXREG(SISCR, 0xE3, data);
204672b676d7Smrg    return(data);
204772b676d7Smrg}
204872b676d7Smrg
204972b676d7SmrgUChar SiSGetCopyROP(int rop)
205072b676d7Smrg{
205172b676d7Smrg    const UChar sisALUConv[] =
205272b676d7Smrg    {
205372b676d7Smrg       0x00,       /* dest = 0;            0,      GXclear,        0 */
205472b676d7Smrg       0x88,       /* dest &= src;         DSa,    GXand,          0x1 */
205572b676d7Smrg       0x44,       /* dest = src & ~dest;  SDna,   GXandReverse,   0x2 */
205672b676d7Smrg       0xCC,       /* dest = src;          S,      GXcopy,         0x3 */
205772b676d7Smrg       0x22,       /* dest &= ~src;        DSna,   GXandInverted,  0x4 */
205872b676d7Smrg       0xAA,       /* dest = dest;         D,      GXnoop,         0x5 */
205972b676d7Smrg       0x66,       /* dest = ^src;         DSx,    GXxor,          0x6 */
206072b676d7Smrg       0xEE,       /* dest |= src;         DSo,    GXor,           0x7 */
206172b676d7Smrg       0x11,       /* dest = ~src & ~dest; DSon,   GXnor,          0x8 */
206272b676d7Smrg       0x99,       /* dest ^= ~src ;       DSxn,   GXequiv,        0x9 */
206372b676d7Smrg       0x55,       /* dest = ~dest;        Dn,     GXInvert,       0xA */
206472b676d7Smrg       0xDD,       /* dest = src|~dest ;   SDno,   GXorReverse,    0xB */
206572b676d7Smrg       0x33,       /* dest = ~src;         Sn,     GXcopyInverted, 0xC */
206672b676d7Smrg       0xBB,       /* dest |= ~src;        DSno,   GXorInverted,   0xD */
206772b676d7Smrg       0x77,       /* dest = ~src|~dest;   DSan,   GXnand,         0xE */
206872b676d7Smrg       0xFF,       /* dest = 0xFF;         1,      GXset,          0xF */
206972b676d7Smrg    };
207072b676d7Smrg
207172b676d7Smrg    return(sisALUConv[rop]);
207272b676d7Smrg}
207372b676d7Smrg
207472b676d7SmrgUChar SiSGetPatternROP(int rop)
207572b676d7Smrg{
207672b676d7Smrg    const UChar sisPatALUConv[] =
207772b676d7Smrg    {
207872b676d7Smrg       0x00,       /* dest = 0;            0,      GXclear,        0 */
207972b676d7Smrg       0xA0,       /* dest &= src;         DPa,    GXand,          0x1 */
208072b676d7Smrg       0x50,       /* dest = src & ~dest;  PDna,   GXandReverse,   0x2 */
208172b676d7Smrg       0xF0,       /* dest = src;          P,      GXcopy,         0x3 */
208272b676d7Smrg       0x0A,       /* dest &= ~src;        DPna,   GXandInverted,  0x4 */
208372b676d7Smrg       0xAA,       /* dest = dest;         D,      GXnoop,         0x5 */
208472b676d7Smrg       0x5A,       /* dest = ^src;         DPx,    GXxor,          0x6 */
208572b676d7Smrg       0xFA,       /* dest |= src;         DPo,    GXor,           0x7 */
208672b676d7Smrg       0x05,       /* dest = ~src & ~dest; DPon,   GXnor,          0x8 */
208772b676d7Smrg       0xA5,       /* dest ^= ~src ;       DPxn,   GXequiv,        0x9 */
208872b676d7Smrg       0x55,       /* dest = ~dest;        Dn,     GXInvert,       0xA */
208972b676d7Smrg       0xF5,       /* dest = src|~dest ;   PDno,   GXorReverse,    0xB */
209072b676d7Smrg       0x0F,       /* dest = ~src;         Pn,     GXcopyInverted, 0xC */
209172b676d7Smrg       0xAF,       /* dest |= ~src;        DPno,   GXorInverted,   0xD */
209272b676d7Smrg       0x5F,       /* dest = ~src|~dest;   DPan,   GXnand,         0xE */
209372b676d7Smrg       0xFF,       /* dest = 0xFF;         1,      GXset,          0xF */
209472b676d7Smrg    };
209572b676d7Smrg
209672b676d7Smrg    return(sisPatALUConv[rop]);
209772b676d7Smrg}
2098