1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun *
3*4882a593Smuzhiyun * Hardware accelerated Matrox PCI cards - G450/G550 PLL control.
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Portions Copyright (c) 2001 Matrox Graphics Inc.
8*4882a593Smuzhiyun *
9*4882a593Smuzhiyun * Version: 1.64 2002/06/10
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * This file is subject to the terms and conditions of the GNU General Public
12*4882a593Smuzhiyun * License. See the file COPYING in the main directory of this archive for
13*4882a593Smuzhiyun * more details.
14*4882a593Smuzhiyun *
15*4882a593Smuzhiyun */
16*4882a593Smuzhiyun
17*4882a593Smuzhiyun #include "g450_pll.h"
18*4882a593Smuzhiyun #include "matroxfb_DAC1064.h"
19*4882a593Smuzhiyun
g450_vco2f(unsigned char p,unsigned int fvco)20*4882a593Smuzhiyun static inline unsigned int g450_vco2f(unsigned char p, unsigned int fvco) {
21*4882a593Smuzhiyun return (p & 0x40) ? fvco : fvco >> ((p & 3) + 1);
22*4882a593Smuzhiyun }
23*4882a593Smuzhiyun
g450_f2vco(unsigned char p,unsigned int fin)24*4882a593Smuzhiyun static inline unsigned int g450_f2vco(unsigned char p, unsigned int fin) {
25*4882a593Smuzhiyun return (p & 0x40) ? fin : fin << ((p & 3) + 1);
26*4882a593Smuzhiyun }
27*4882a593Smuzhiyun
g450_mnp2vco(const struct matrox_fb_info * minfo,unsigned int mnp)28*4882a593Smuzhiyun static unsigned int g450_mnp2vco(const struct matrox_fb_info *minfo,
29*4882a593Smuzhiyun unsigned int mnp)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun unsigned int m, n;
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun m = ((mnp >> 16) & 0x0FF) + 1;
34*4882a593Smuzhiyun n = ((mnp >> 7) & 0x1FE) + 4;
35*4882a593Smuzhiyun return (minfo->features.pll.ref_freq * n + (m >> 1)) / m;
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
g450_mnp2f(const struct matrox_fb_info * minfo,unsigned int mnp)38*4882a593Smuzhiyun unsigned int g450_mnp2f(const struct matrox_fb_info *minfo, unsigned int mnp)
39*4882a593Smuzhiyun {
40*4882a593Smuzhiyun return g450_vco2f(mnp, g450_mnp2vco(minfo, mnp));
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
pll_freq_delta(unsigned int f1,unsigned int f2)43*4882a593Smuzhiyun static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) {
44*4882a593Smuzhiyun if (f2 < f1) {
45*4882a593Smuzhiyun f2 = f1 - f2;
46*4882a593Smuzhiyun } else {
47*4882a593Smuzhiyun f2 = f2 - f1;
48*4882a593Smuzhiyun }
49*4882a593Smuzhiyun return f2;
50*4882a593Smuzhiyun }
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun #define NO_MORE_MNP 0x01FFFFFF
53*4882a593Smuzhiyun #define G450_MNP_FREQBITS (0xFFFFFF43) /* do not mask high byte so we'll catch NO_MORE_MNP */
54*4882a593Smuzhiyun
g450_nextpll(const struct matrox_fb_info * minfo,const struct matrox_pll_limits * pi,unsigned int * fvco,unsigned int mnp)55*4882a593Smuzhiyun static unsigned int g450_nextpll(const struct matrox_fb_info *minfo,
56*4882a593Smuzhiyun const struct matrox_pll_limits *pi,
57*4882a593Smuzhiyun unsigned int *fvco, unsigned int mnp)
58*4882a593Smuzhiyun {
59*4882a593Smuzhiyun unsigned int m, n, p;
60*4882a593Smuzhiyun unsigned int tvco = *fvco;
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun m = (mnp >> 16) & 0xFF;
63*4882a593Smuzhiyun p = mnp & 0xFF;
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun do {
66*4882a593Smuzhiyun if (m == 0 || m == 0xFF) {
67*4882a593Smuzhiyun if (m == 0) {
68*4882a593Smuzhiyun if (p & 0x40) {
69*4882a593Smuzhiyun return NO_MORE_MNP;
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun if (p & 3) {
72*4882a593Smuzhiyun p--;
73*4882a593Smuzhiyun } else {
74*4882a593Smuzhiyun p = 0x40;
75*4882a593Smuzhiyun }
76*4882a593Smuzhiyun tvco >>= 1;
77*4882a593Smuzhiyun if (tvco < pi->vcomin) {
78*4882a593Smuzhiyun return NO_MORE_MNP;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun *fvco = tvco;
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun p &= 0x43;
84*4882a593Smuzhiyun if (tvco < 550000) {
85*4882a593Smuzhiyun /* p |= 0x00; */
86*4882a593Smuzhiyun } else if (tvco < 700000) {
87*4882a593Smuzhiyun p |= 0x08;
88*4882a593Smuzhiyun } else if (tvco < 1000000) {
89*4882a593Smuzhiyun p |= 0x10;
90*4882a593Smuzhiyun } else if (tvco < 1150000) {
91*4882a593Smuzhiyun p |= 0x18;
92*4882a593Smuzhiyun } else {
93*4882a593Smuzhiyun p |= 0x20;
94*4882a593Smuzhiyun }
95*4882a593Smuzhiyun m = 9;
96*4882a593Smuzhiyun } else {
97*4882a593Smuzhiyun m--;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun n = ((tvco * (m+1) + minfo->features.pll.ref_freq) / (minfo->features.pll.ref_freq * 2)) - 2;
100*4882a593Smuzhiyun } while (n < 0x03 || n > 0x7A);
101*4882a593Smuzhiyun return (m << 16) | (n << 8) | p;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun
g450_firstpll(const struct matrox_fb_info * minfo,const struct matrox_pll_limits * pi,unsigned int * vco,unsigned int fout)104*4882a593Smuzhiyun static unsigned int g450_firstpll(const struct matrox_fb_info *minfo,
105*4882a593Smuzhiyun const struct matrox_pll_limits *pi,
106*4882a593Smuzhiyun unsigned int *vco, unsigned int fout)
107*4882a593Smuzhiyun {
108*4882a593Smuzhiyun unsigned int p;
109*4882a593Smuzhiyun unsigned int vcomax;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun vcomax = pi->vcomax;
112*4882a593Smuzhiyun if (fout > (vcomax / 2)) {
113*4882a593Smuzhiyun if (fout > vcomax) {
114*4882a593Smuzhiyun *vco = vcomax;
115*4882a593Smuzhiyun } else {
116*4882a593Smuzhiyun *vco = fout;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun p = 0x40;
119*4882a593Smuzhiyun } else {
120*4882a593Smuzhiyun unsigned int tvco;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun p = 3;
123*4882a593Smuzhiyun tvco = g450_f2vco(p, fout);
124*4882a593Smuzhiyun while (p && (tvco > vcomax)) {
125*4882a593Smuzhiyun p--;
126*4882a593Smuzhiyun tvco >>= 1;
127*4882a593Smuzhiyun }
128*4882a593Smuzhiyun if (tvco < pi->vcomin) {
129*4882a593Smuzhiyun tvco = pi->vcomin;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun *vco = tvco;
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun return g450_nextpll(minfo, pi, vco, 0xFF0000 | p);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
g450_setpll(const struct matrox_fb_info * minfo,unsigned int mnp,unsigned int pll)136*4882a593Smuzhiyun static inline unsigned int g450_setpll(const struct matrox_fb_info *minfo,
137*4882a593Smuzhiyun unsigned int mnp, unsigned int pll)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun switch (pll) {
140*4882a593Smuzhiyun case M_PIXEL_PLL_A:
141*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLAM, mnp >> 16);
142*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLAN, mnp >> 8);
143*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLAP, mnp);
144*4882a593Smuzhiyun return M1064_XPIXPLLSTAT;
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun case M_PIXEL_PLL_B:
147*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLBM, mnp >> 16);
148*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLBN, mnp >> 8);
149*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLBP, mnp);
150*4882a593Smuzhiyun return M1064_XPIXPLLSTAT;
151*4882a593Smuzhiyun
152*4882a593Smuzhiyun case M_PIXEL_PLL_C:
153*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLCM, mnp >> 16);
154*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLCN, mnp >> 8);
155*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXPLLCP, mnp);
156*4882a593Smuzhiyun return M1064_XPIXPLLSTAT;
157*4882a593Smuzhiyun
158*4882a593Smuzhiyun case M_SYSTEM_PLL:
159*4882a593Smuzhiyun matroxfb_DAC_out(minfo, DAC1064_XSYSPLLM, mnp >> 16);
160*4882a593Smuzhiyun matroxfb_DAC_out(minfo, DAC1064_XSYSPLLN, mnp >> 8);
161*4882a593Smuzhiyun matroxfb_DAC_out(minfo, DAC1064_XSYSPLLP, mnp);
162*4882a593Smuzhiyun return DAC1064_XSYSPLLSTAT;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun case M_VIDEO_PLL:
165*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XVIDPLLM, mnp >> 16);
166*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XVIDPLLN, mnp >> 8);
167*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XVIDPLLP, mnp);
168*4882a593Smuzhiyun return M1064_XVIDPLLSTAT;
169*4882a593Smuzhiyun }
170*4882a593Smuzhiyun return 0;
171*4882a593Smuzhiyun }
172*4882a593Smuzhiyun
g450_cmppll(const struct matrox_fb_info * minfo,unsigned int mnp,unsigned int pll)173*4882a593Smuzhiyun static inline unsigned int g450_cmppll(const struct matrox_fb_info *minfo,
174*4882a593Smuzhiyun unsigned int mnp, unsigned int pll)
175*4882a593Smuzhiyun {
176*4882a593Smuzhiyun unsigned char m = mnp >> 16;
177*4882a593Smuzhiyun unsigned char n = mnp >> 8;
178*4882a593Smuzhiyun unsigned char p = mnp;
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun switch (pll) {
181*4882a593Smuzhiyun case M_PIXEL_PLL_A:
182*4882a593Smuzhiyun return (matroxfb_DAC_in(minfo, M1064_XPIXPLLAM) != m ||
183*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XPIXPLLAN) != n ||
184*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XPIXPLLAP) != p);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun case M_PIXEL_PLL_B:
187*4882a593Smuzhiyun return (matroxfb_DAC_in(minfo, M1064_XPIXPLLBM) != m ||
188*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XPIXPLLBN) != n ||
189*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XPIXPLLBP) != p);
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun case M_PIXEL_PLL_C:
192*4882a593Smuzhiyun return (matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) != m ||
193*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) != n ||
194*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XPIXPLLCP) != p);
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun case M_SYSTEM_PLL:
197*4882a593Smuzhiyun return (matroxfb_DAC_in(minfo, DAC1064_XSYSPLLM) != m ||
198*4882a593Smuzhiyun matroxfb_DAC_in(minfo, DAC1064_XSYSPLLN) != n ||
199*4882a593Smuzhiyun matroxfb_DAC_in(minfo, DAC1064_XSYSPLLP) != p);
200*4882a593Smuzhiyun
201*4882a593Smuzhiyun case M_VIDEO_PLL:
202*4882a593Smuzhiyun return (matroxfb_DAC_in(minfo, M1064_XVIDPLLM) != m ||
203*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XVIDPLLN) != n ||
204*4882a593Smuzhiyun matroxfb_DAC_in(minfo, M1064_XVIDPLLP) != p);
205*4882a593Smuzhiyun }
206*4882a593Smuzhiyun return 1;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
g450_isplllocked(const struct matrox_fb_info * minfo,unsigned int regidx)209*4882a593Smuzhiyun static inline int g450_isplllocked(const struct matrox_fb_info *minfo,
210*4882a593Smuzhiyun unsigned int regidx)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun unsigned int j;
213*4882a593Smuzhiyun
214*4882a593Smuzhiyun for (j = 0; j < 1000; j++) {
215*4882a593Smuzhiyun if (matroxfb_DAC_in(minfo, regidx) & 0x40) {
216*4882a593Smuzhiyun unsigned int r = 0;
217*4882a593Smuzhiyun int i;
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun for (i = 0; i < 100; i++) {
220*4882a593Smuzhiyun r += matroxfb_DAC_in(minfo, regidx) & 0x40;
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun return r >= (90 * 0x40);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun /* udelay(1)... but DAC_in is much slower... */
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun return 0;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun
g450_testpll(const struct matrox_fb_info * minfo,unsigned int mnp,unsigned int pll)229*4882a593Smuzhiyun static int g450_testpll(const struct matrox_fb_info *minfo, unsigned int mnp,
230*4882a593Smuzhiyun unsigned int pll)
231*4882a593Smuzhiyun {
232*4882a593Smuzhiyun return g450_isplllocked(minfo, g450_setpll(minfo, mnp, pll));
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
updatehwstate_clk(struct matrox_hw_state * hw,unsigned int mnp,unsigned int pll)235*4882a593Smuzhiyun static void updatehwstate_clk(struct matrox_hw_state* hw, unsigned int mnp, unsigned int pll) {
236*4882a593Smuzhiyun switch (pll) {
237*4882a593Smuzhiyun case M_SYSTEM_PLL:
238*4882a593Smuzhiyun hw->DACclk[3] = mnp >> 16;
239*4882a593Smuzhiyun hw->DACclk[4] = mnp >> 8;
240*4882a593Smuzhiyun hw->DACclk[5] = mnp;
241*4882a593Smuzhiyun break;
242*4882a593Smuzhiyun }
243*4882a593Smuzhiyun }
244*4882a593Smuzhiyun
matroxfb_g450_setpll_cond(struct matrox_fb_info * minfo,unsigned int mnp,unsigned int pll)245*4882a593Smuzhiyun void matroxfb_g450_setpll_cond(struct matrox_fb_info *minfo, unsigned int mnp,
246*4882a593Smuzhiyun unsigned int pll)
247*4882a593Smuzhiyun {
248*4882a593Smuzhiyun if (g450_cmppll(minfo, mnp, pll)) {
249*4882a593Smuzhiyun g450_setpll(minfo, mnp, pll);
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
g450_findworkingpll(struct matrox_fb_info * minfo,unsigned int pll,unsigned int * mnparray,unsigned int mnpcount)253*4882a593Smuzhiyun static inline unsigned int g450_findworkingpll(struct matrox_fb_info *minfo,
254*4882a593Smuzhiyun unsigned int pll,
255*4882a593Smuzhiyun unsigned int *mnparray,
256*4882a593Smuzhiyun unsigned int mnpcount)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun unsigned int found = 0;
259*4882a593Smuzhiyun unsigned int idx;
260*4882a593Smuzhiyun unsigned int mnpfound = mnparray[0];
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun for (idx = 0; idx < mnpcount; idx++) {
263*4882a593Smuzhiyun unsigned int sarray[3];
264*4882a593Smuzhiyun unsigned int *sptr;
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun unsigned int mnp;
267*4882a593Smuzhiyun
268*4882a593Smuzhiyun sptr = sarray;
269*4882a593Smuzhiyun mnp = mnparray[idx];
270*4882a593Smuzhiyun if (mnp & 0x38) {
271*4882a593Smuzhiyun *sptr++ = mnp - 8;
272*4882a593Smuzhiyun }
273*4882a593Smuzhiyun if ((mnp & 0x38) != 0x38) {
274*4882a593Smuzhiyun *sptr++ = mnp + 8;
275*4882a593Smuzhiyun }
276*4882a593Smuzhiyun *sptr = mnp;
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun while (sptr >= sarray) {
279*4882a593Smuzhiyun unsigned int mnp = *sptr--;
280*4882a593Smuzhiyun
281*4882a593Smuzhiyun if (g450_testpll(minfo, mnp - 0x0300, pll) &&
282*4882a593Smuzhiyun g450_testpll(minfo, mnp + 0x0300, pll) &&
283*4882a593Smuzhiyun g450_testpll(minfo, mnp - 0x0200, pll) &&
284*4882a593Smuzhiyun g450_testpll(minfo, mnp + 0x0200, pll) &&
285*4882a593Smuzhiyun g450_testpll(minfo, mnp - 0x0100, pll) &&
286*4882a593Smuzhiyun g450_testpll(minfo, mnp + 0x0100, pll)) {
287*4882a593Smuzhiyun if (g450_testpll(minfo, mnp, pll)) {
288*4882a593Smuzhiyun return mnp;
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun } else if (!found && g450_testpll(minfo, mnp, pll)) {
291*4882a593Smuzhiyun mnpfound = mnp;
292*4882a593Smuzhiyun found = 1;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun }
296*4882a593Smuzhiyun g450_setpll(minfo, mnpfound, pll);
297*4882a593Smuzhiyun return mnpfound;
298*4882a593Smuzhiyun }
299*4882a593Smuzhiyun
g450_addcache(struct matrox_pll_cache * ci,unsigned int mnp_key,unsigned int mnp_value)300*4882a593Smuzhiyun static void g450_addcache(struct matrox_pll_cache* ci, unsigned int mnp_key, unsigned int mnp_value) {
301*4882a593Smuzhiyun if (++ci->valid > ARRAY_SIZE(ci->data)) {
302*4882a593Smuzhiyun ci->valid = ARRAY_SIZE(ci->data);
303*4882a593Smuzhiyun }
304*4882a593Smuzhiyun memmove(ci->data + 1, ci->data, (ci->valid - 1) * sizeof(*ci->data));
305*4882a593Smuzhiyun ci->data[0].mnp_key = mnp_key & G450_MNP_FREQBITS;
306*4882a593Smuzhiyun ci->data[0].mnp_value = mnp_value;
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun
g450_checkcache(struct matrox_fb_info * minfo,struct matrox_pll_cache * ci,unsigned int mnp_key)309*4882a593Smuzhiyun static int g450_checkcache(struct matrox_fb_info *minfo,
310*4882a593Smuzhiyun struct matrox_pll_cache *ci, unsigned int mnp_key)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun unsigned int i;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun mnp_key &= G450_MNP_FREQBITS;
315*4882a593Smuzhiyun for (i = 0; i < ci->valid; i++) {
316*4882a593Smuzhiyun if (ci->data[i].mnp_key == mnp_key) {
317*4882a593Smuzhiyun unsigned int mnp;
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun mnp = ci->data[i].mnp_value;
320*4882a593Smuzhiyun if (i) {
321*4882a593Smuzhiyun memmove(ci->data + 1, ci->data, i * sizeof(*ci->data));
322*4882a593Smuzhiyun ci->data[0].mnp_key = mnp_key;
323*4882a593Smuzhiyun ci->data[0].mnp_value = mnp;
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun return mnp;
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun return NO_MORE_MNP;
329*4882a593Smuzhiyun }
330*4882a593Smuzhiyun
__g450_setclk(struct matrox_fb_info * minfo,unsigned int fout,unsigned int pll,unsigned int * mnparray,unsigned int * deltaarray)331*4882a593Smuzhiyun static int __g450_setclk(struct matrox_fb_info *minfo, unsigned int fout,
332*4882a593Smuzhiyun unsigned int pll, unsigned int *mnparray,
333*4882a593Smuzhiyun unsigned int *deltaarray)
334*4882a593Smuzhiyun {
335*4882a593Smuzhiyun unsigned int mnpcount;
336*4882a593Smuzhiyun const struct matrox_pll_limits* pi;
337*4882a593Smuzhiyun struct matrox_pll_cache* ci;
338*4882a593Smuzhiyun
339*4882a593Smuzhiyun switch (pll) {
340*4882a593Smuzhiyun case M_PIXEL_PLL_A:
341*4882a593Smuzhiyun case M_PIXEL_PLL_B:
342*4882a593Smuzhiyun case M_PIXEL_PLL_C:
343*4882a593Smuzhiyun {
344*4882a593Smuzhiyun u_int8_t tmp, xpwrctrl;
345*4882a593Smuzhiyun unsigned long flags;
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun matroxfb_DAC_lock_irqsave(flags);
348*4882a593Smuzhiyun
349*4882a593Smuzhiyun xpwrctrl = matroxfb_DAC_in(minfo, M1064_XPWRCTRL);
350*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPWRCTRL, xpwrctrl & ~M1064_XPWRCTRL_PANELPDN);
351*4882a593Smuzhiyun mga_outb(M_SEQ_INDEX, M_SEQ1);
352*4882a593Smuzhiyun mga_outb(M_SEQ_DATA, mga_inb(M_SEQ_DATA) | M_SEQ1_SCROFF);
353*4882a593Smuzhiyun tmp = matroxfb_DAC_in(minfo, M1064_XPIXCLKCTRL);
354*4882a593Smuzhiyun tmp |= M1064_XPIXCLKCTRL_DIS;
355*4882a593Smuzhiyun if (!(tmp & M1064_XPIXCLKCTRL_PLL_UP)) {
356*4882a593Smuzhiyun tmp |= M1064_XPIXCLKCTRL_PLL_UP;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPIXCLKCTRL, tmp);
359*4882a593Smuzhiyun /* DVI PLL preferred for frequencies up to
360*4882a593Smuzhiyun panel link max, standard PLL otherwise */
361*4882a593Smuzhiyun if (fout >= minfo->max_pixel_clock_panellink)
362*4882a593Smuzhiyun tmp = 0;
363*4882a593Smuzhiyun else tmp =
364*4882a593Smuzhiyun M1064_XDVICLKCTRL_DVIDATAPATHSEL |
365*4882a593Smuzhiyun M1064_XDVICLKCTRL_C1DVICLKSEL |
366*4882a593Smuzhiyun M1064_XDVICLKCTRL_C1DVICLKEN |
367*4882a593Smuzhiyun M1064_XDVICLKCTRL_DVILOOPCTL |
368*4882a593Smuzhiyun M1064_XDVICLKCTRL_P1LOOPBWDTCTL;
369*4882a593Smuzhiyun /* Setting this breaks PC systems so don't do it */
370*4882a593Smuzhiyun /* matroxfb_DAC_out(minfo, M1064_XDVICLKCTRL, tmp); */
371*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPWRCTRL,
372*4882a593Smuzhiyun xpwrctrl);
373*4882a593Smuzhiyun
374*4882a593Smuzhiyun matroxfb_DAC_unlock_irqrestore(flags);
375*4882a593Smuzhiyun }
376*4882a593Smuzhiyun {
377*4882a593Smuzhiyun u_int8_t misc;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun misc = mga_inb(M_MISC_REG_READ) & ~0x0C;
380*4882a593Smuzhiyun switch (pll) {
381*4882a593Smuzhiyun case M_PIXEL_PLL_A:
382*4882a593Smuzhiyun break;
383*4882a593Smuzhiyun case M_PIXEL_PLL_B:
384*4882a593Smuzhiyun misc |= 0x04;
385*4882a593Smuzhiyun break;
386*4882a593Smuzhiyun default:
387*4882a593Smuzhiyun misc |= 0x0C;
388*4882a593Smuzhiyun break;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun mga_outb(M_MISC_REG, misc);
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun pi = &minfo->limits.pixel;
393*4882a593Smuzhiyun ci = &minfo->cache.pixel;
394*4882a593Smuzhiyun break;
395*4882a593Smuzhiyun case M_SYSTEM_PLL:
396*4882a593Smuzhiyun {
397*4882a593Smuzhiyun u_int32_t opt;
398*4882a593Smuzhiyun
399*4882a593Smuzhiyun pci_read_config_dword(minfo->pcidev, PCI_OPTION_REG, &opt);
400*4882a593Smuzhiyun if (!(opt & 0x20)) {
401*4882a593Smuzhiyun pci_write_config_dword(minfo->pcidev, PCI_OPTION_REG, opt | 0x20);
402*4882a593Smuzhiyun }
403*4882a593Smuzhiyun }
404*4882a593Smuzhiyun pi = &minfo->limits.system;
405*4882a593Smuzhiyun ci = &minfo->cache.system;
406*4882a593Smuzhiyun break;
407*4882a593Smuzhiyun case M_VIDEO_PLL:
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun u_int8_t tmp;
410*4882a593Smuzhiyun unsigned int mnp;
411*4882a593Smuzhiyun unsigned long flags;
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun matroxfb_DAC_lock_irqsave(flags);
414*4882a593Smuzhiyun tmp = matroxfb_DAC_in(minfo, M1064_XPWRCTRL);
415*4882a593Smuzhiyun if (!(tmp & 2)) {
416*4882a593Smuzhiyun matroxfb_DAC_out(minfo, M1064_XPWRCTRL, tmp | 2);
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun
419*4882a593Smuzhiyun mnp = matroxfb_DAC_in(minfo, M1064_XPIXPLLCM) << 16;
420*4882a593Smuzhiyun mnp |= matroxfb_DAC_in(minfo, M1064_XPIXPLLCN) << 8;
421*4882a593Smuzhiyun matroxfb_DAC_unlock_irqrestore(flags);
422*4882a593Smuzhiyun }
423*4882a593Smuzhiyun pi = &minfo->limits.video;
424*4882a593Smuzhiyun ci = &minfo->cache.video;
425*4882a593Smuzhiyun break;
426*4882a593Smuzhiyun default:
427*4882a593Smuzhiyun return -EINVAL;
428*4882a593Smuzhiyun }
429*4882a593Smuzhiyun
430*4882a593Smuzhiyun mnpcount = 0;
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun unsigned int mnp;
433*4882a593Smuzhiyun unsigned int xvco;
434*4882a593Smuzhiyun
435*4882a593Smuzhiyun for (mnp = g450_firstpll(minfo, pi, &xvco, fout); mnp != NO_MORE_MNP; mnp = g450_nextpll(minfo, pi, &xvco, mnp)) {
436*4882a593Smuzhiyun unsigned int idx;
437*4882a593Smuzhiyun unsigned int vco;
438*4882a593Smuzhiyun unsigned int delta;
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun vco = g450_mnp2vco(minfo, mnp);
441*4882a593Smuzhiyun delta = pll_freq_delta(fout, g450_vco2f(mnp, vco));
442*4882a593Smuzhiyun for (idx = mnpcount; idx > 0; idx--) {
443*4882a593Smuzhiyun /* == is important; due to nextpll algorithm we get
444*4882a593Smuzhiyun sorted equally good frequencies from lower VCO
445*4882a593Smuzhiyun frequency to higher - with <= lowest wins, while
446*4882a593Smuzhiyun with < highest one wins */
447*4882a593Smuzhiyun if (delta <= deltaarray[idx-1]) {
448*4882a593Smuzhiyun /* all else being equal except VCO,
449*4882a593Smuzhiyun * choose VCO not near (within 1/16th or so) VCOmin
450*4882a593Smuzhiyun * (freqs near VCOmin aren't as stable)
451*4882a593Smuzhiyun */
452*4882a593Smuzhiyun if (delta == deltaarray[idx-1]
453*4882a593Smuzhiyun && vco != g450_mnp2vco(minfo, mnparray[idx-1])
454*4882a593Smuzhiyun && vco < (pi->vcomin * 17 / 16)) {
455*4882a593Smuzhiyun break;
456*4882a593Smuzhiyun }
457*4882a593Smuzhiyun mnparray[idx] = mnparray[idx-1];
458*4882a593Smuzhiyun deltaarray[idx] = deltaarray[idx-1];
459*4882a593Smuzhiyun } else {
460*4882a593Smuzhiyun break;
461*4882a593Smuzhiyun }
462*4882a593Smuzhiyun }
463*4882a593Smuzhiyun mnparray[idx] = mnp;
464*4882a593Smuzhiyun deltaarray[idx] = delta;
465*4882a593Smuzhiyun mnpcount++;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun }
468*4882a593Smuzhiyun /* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */
469*4882a593Smuzhiyun if (!mnpcount) {
470*4882a593Smuzhiyun return -EBUSY;
471*4882a593Smuzhiyun }
472*4882a593Smuzhiyun {
473*4882a593Smuzhiyun unsigned long flags;
474*4882a593Smuzhiyun unsigned int mnp;
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun matroxfb_DAC_lock_irqsave(flags);
477*4882a593Smuzhiyun mnp = g450_checkcache(minfo, ci, mnparray[0]);
478*4882a593Smuzhiyun if (mnp != NO_MORE_MNP) {
479*4882a593Smuzhiyun matroxfb_g450_setpll_cond(minfo, mnp, pll);
480*4882a593Smuzhiyun } else {
481*4882a593Smuzhiyun mnp = g450_findworkingpll(minfo, pll, mnparray, mnpcount);
482*4882a593Smuzhiyun g450_addcache(ci, mnparray[0], mnp);
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun updatehwstate_clk(&minfo->hw, mnp, pll);
485*4882a593Smuzhiyun matroxfb_DAC_unlock_irqrestore(flags);
486*4882a593Smuzhiyun return mnp;
487*4882a593Smuzhiyun }
488*4882a593Smuzhiyun }
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun /* It must be greater than number of possible PLL values.
491*4882a593Smuzhiyun * Currently there is 5(p) * 10(m) = 50 possible values. */
492*4882a593Smuzhiyun #define MNP_TABLE_SIZE 64
493*4882a593Smuzhiyun
matroxfb_g450_setclk(struct matrox_fb_info * minfo,unsigned int fout,unsigned int pll)494*4882a593Smuzhiyun int matroxfb_g450_setclk(struct matrox_fb_info *minfo, unsigned int fout,
495*4882a593Smuzhiyun unsigned int pll)
496*4882a593Smuzhiyun {
497*4882a593Smuzhiyun unsigned int* arr;
498*4882a593Smuzhiyun
499*4882a593Smuzhiyun arr = kmalloc(sizeof(*arr) * MNP_TABLE_SIZE * 2, GFP_KERNEL);
500*4882a593Smuzhiyun if (arr) {
501*4882a593Smuzhiyun int r;
502*4882a593Smuzhiyun
503*4882a593Smuzhiyun r = __g450_setclk(minfo, fout, pll, arr, arr + MNP_TABLE_SIZE);
504*4882a593Smuzhiyun kfree(arr);
505*4882a593Smuzhiyun return r;
506*4882a593Smuzhiyun }
507*4882a593Smuzhiyun return -ENOMEM;
508*4882a593Smuzhiyun }
509*4882a593Smuzhiyun
510*4882a593Smuzhiyun EXPORT_SYMBOL(matroxfb_g450_setclk);
511*4882a593Smuzhiyun EXPORT_SYMBOL(g450_mnp2f);
512*4882a593Smuzhiyun EXPORT_SYMBOL(matroxfb_g450_setpll_cond);
513*4882a593Smuzhiyun
514*4882a593Smuzhiyun MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
515*4882a593Smuzhiyun MODULE_DESCRIPTION("Matrox G450/G550 PLL driver");
516*4882a593Smuzhiyun
517*4882a593Smuzhiyun MODULE_LICENSE("GPL");
518