109885543Smrg/* Header:   //Mercury/Projects/archives/XFree86/4.0/smi_dac.c-arc   1.8   27 Nov 2000 15:47:08   Frido  $ */
209885543Smrg
309885543Smrg/*
409885543SmrgCopyright (C) 1994-1998 The XFree86 Project, Inc.  All Rights Reserved.
509885543SmrgCopyright (C) 2000 Silicon Motion, Inc.  All Rights Reserved.
609885543Smrg
709885543SmrgPermission is hereby granted, free of charge, to any person obtaining a copy of
809885543Smrgthis software and associated documentation files (the "Software"), to deal in
909885543Smrgthe Software without restriction, including without limitation the rights to
1009885543Smrguse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1109885543Smrgof the Software, and to permit persons to whom the Software is furnished to do
1209885543Smrgso, subject to the following conditions:
1309885543Smrg
1409885543SmrgThe above copyright notice and this permission notice shall be included in all
1509885543Smrgcopies or substantial portions of the Software.
1609885543Smrg
1709885543SmrgTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1809885543SmrgIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FIT-
1909885543SmrgNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
2009885543SmrgXFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
2109885543SmrgAN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2209885543SmrgWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2309885543Smrg
2409885543SmrgExcept as contained in this notice, the names of the XFree86 Project and
2509885543SmrgSilicon Motion shall not be used in advertising or otherwise to promote the
2609885543Smrgsale, use or other dealings in this Software without prior written
2709885543Smrgauthorization from the XFree86 Project and Silicon Motion.
2809885543Smrg*/
2909885543Smrg
3009885543Smrg#ifdef HAVE_CONFIG_H
3109885543Smrg#include "config.h"
3209885543Smrg#endif
3309885543Smrg
3409885543Smrg#include "smi.h"
3509885543Smrg
3609885543Smrg#define BASE_FREQ	14.31818	/* MHz */
3709885543Smrg
3809885543Smrgvoid
3909885543SmrgSMI_CommonCalcClock(int scrnIndex, long freq, int min_m, int min_n1,
4009885543Smrg		    int max_n1, int min_n2, int max_n2, long freq_min,
4109885543Smrg		    long freq_max, unsigned char *mdiv, unsigned char *ndiv)
4209885543Smrg{
4309885543Smrg    ScrnInfoPtr pScrn = xf86Screens[scrnIndex];
4409885543Smrg    SMIPtr pSmi = SMIPTR(pScrn);
4509885543Smrg    double div, diff, best_diff;
4609885543Smrg    unsigned int m;
4709885543Smrg    unsigned char n1, n2;
4809885543Smrg    unsigned char best_n1 = 63, best_n2 = 3, best_m = 255;
4909885543Smrg
5009885543Smrg    double ffreq     = freq     / 1000.0 / BASE_FREQ;
5109885543Smrg    double ffreq_min = freq_min / 1000.0 / BASE_FREQ;
5209885543Smrg    double ffreq_max = freq_max / 1000.0 / BASE_FREQ;
5309885543Smrg
5409885543Smrg    if (ffreq < ffreq_min / (1 << max_n2)) {
5509885543Smrg	xf86DrvMsg(scrnIndex,X_WARNING,"invalid frequency %1.3f MHz  [freq >= %1.3f MHz]\n",
5609885543Smrg		ffreq * BASE_FREQ, ffreq_min * BASE_FREQ / (1 << max_n2));
5709885543Smrg	ffreq = ffreq_min / (1 << max_n2);
5809885543Smrg    }
5909885543Smrg    if (ffreq > ffreq_max / (1 << min_n2)) {
6009885543Smrg	xf86DrvMsg(scrnIndex,X_WARNING,"invalid frequency %1.3f MHz  [freq <= %1.3f MHz]\n",
6109885543Smrg		ffreq * BASE_FREQ, ffreq_max * BASE_FREQ / (1 << min_n2));
6209885543Smrg	ffreq = ffreq_max / (1 << min_n2);
6309885543Smrg    }
6409885543Smrg
6509885543Smrg    /* work out suitable timings */
6609885543Smrg    best_diff = ffreq;
6709885543Smrg
6809885543Smrg    for (n2 = min_n2; n2 <= max_n2; n2++) {
6909885543Smrg	for (n1 = min_n1; n1 <= max_n1; n1++) {
7009885543Smrg	    m = (int)(ffreq * n1 * (1 << n2) + 0.5);
7109885543Smrg	    if ( (m < min_m) || (m > 255) ) {
7209885543Smrg		continue;
7309885543Smrg	    }
7409885543Smrg	    div = (double)(m) / (double)(n1);
7509885543Smrg	    if ( (div >= ffreq_min) && (div <= ffreq_max) ) {
7609885543Smrg		diff = ffreq - div / (1 << n2);
7709885543Smrg		if (diff < 0.0) {
7809885543Smrg		    diff = -diff;
7909885543Smrg		}
8009885543Smrg		if (diff < best_diff) {
8109885543Smrg		    best_diff = diff;
8209885543Smrg		    best_m    = m;
8309885543Smrg		    best_n1   = n1;
8409885543Smrg		    best_n2   = n2;
8509885543Smrg		}
8609885543Smrg	    }
8709885543Smrg	}
8809885543Smrg    }
8909885543Smrg
907104f784Smrg    DEBUG("Clock parameters for %1.6f MHz: m=%d, n1=%d, n2=%d\n",
917104f784Smrg	  ((double)(best_m) / (double)(best_n1) / (1 << best_n2)) * BASE_FREQ,
927104f784Smrg	  best_m, best_n1, best_n2);
9309885543Smrg
9409885543Smrg    if (SMI_LYNX_SERIES(pSmi->Chipset)) {
957104f784Smrg	/* Prefer post scalar enabled for even denominators */
967104f784Smrg	if (freq < 70000 && max_n2 > 0 &&
977104f784Smrg	    best_n2 == 0 && best_n1 % 2 == 0){
987104f784Smrg	    best_n1 >>= 1;
997104f784Smrg	    best_n2 = 1;
1007104f784Smrg	}
1017104f784Smrg
1027104f784Smrg	*ndiv = best_n1 |
1037104f784Smrg	    (best_n2 & 0x1) << 7 |
1047104f784Smrg	    (best_n2 & 0x2) >> 1 <<6;
10509885543Smrg    } else {
10609885543Smrg	*ndiv = best_n1 | (best_n2 << 7);
1077104f784Smrg
1087104f784Smrg	/* Enable second VCO */
10909885543Smrg	if (freq > 120000)
11009885543Smrg	    *ndiv |= 1 << 6;
11109885543Smrg    }
11209885543Smrg
11309885543Smrg    *mdiv = best_m;
11409885543Smrg}
11509885543Smrg
116