xref: /linux/drivers/gpu/drm/mgag200/mgag200_mode.c (revision db05f8d3dc875249a5a11737ca715584b72851e8)
1c51669eaSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2414c4531SDave Airlie /*
3414c4531SDave Airlie  * Copyright 2010 Matt Turner.
4414c4531SDave Airlie  * Copyright 2012 Red Hat
5414c4531SDave Airlie  *
6414c4531SDave Airlie  * Authors: Matthew Garrett
7414c4531SDave Airlie  *	    Matt Turner
8414c4531SDave Airlie  *	    Dave Airlie
9414c4531SDave Airlie  */
10414c4531SDave Airlie 
11414c4531SDave Airlie #include <linux/delay.h>
1209daa2e7SThomas Zimmermann #include <linux/pci.h>
13414c4531SDave Airlie 
14760285e7SDavid Howells #include <drm/drm_crtc_helper.h>
159f397801SSam Ravnborg #include <drm/drm_fourcc.h>
165635b7cfSThomas Zimmermann #include <drm/drm_gem_framebuffer_helper.h>
173cb9ae4fSDaniel Vetter #include <drm/drm_plane_helper.h>
18fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
1903e44ad1SThomas Zimmermann #include <drm/drm_simple_kms_helper.h>
20414c4531SDave Airlie 
21414c4531SDave Airlie #include "mgag200_drv.h"
22414c4531SDave Airlie 
23414c4531SDave Airlie #define MGAG200_LUT_SIZE 256
24414c4531SDave Airlie 
25414c4531SDave Airlie /*
26414c4531SDave Airlie  * This file contains setup code for the CRTC.
27414c4531SDave Airlie  */
28414c4531SDave Airlie 
29414c4531SDave Airlie static void mga_crtc_load_lut(struct drm_crtc *crtc)
30414c4531SDave Airlie {
31414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
328d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(dev);
33f4510a27SMatt Roper 	struct drm_framebuffer *fb = crtc->primary->fb;
349ed85e14SPeter Rosin 	u16 *r_ptr, *g_ptr, *b_ptr;
35414c4531SDave Airlie 	int i;
36414c4531SDave Airlie 
37414c4531SDave Airlie 	if (!crtc->enabled)
38414c4531SDave Airlie 		return;
39414c4531SDave Airlie 
409ed85e14SPeter Rosin 	r_ptr = crtc->gamma_store;
419ed85e14SPeter Rosin 	g_ptr = r_ptr + crtc->gamma_size;
429ed85e14SPeter Rosin 	b_ptr = g_ptr + crtc->gamma_size;
439ed85e14SPeter Rosin 
44414c4531SDave Airlie 	WREG8(DAC_INDEX + MGA1064_INDEX, 0);
45414c4531SDave Airlie 
46272725c7SVille Syrjälä 	if (fb && fb->format->cpp[0] * 8 == 16) {
47b00c600eSVille Syrjälä 		int inc = (fb->format->depth == 15) ? 8 : 4;
48de7500eaSEgbert Eich 		u8 r, b;
49de7500eaSEgbert Eich 		for (i = 0; i < MGAG200_LUT_SIZE; i += inc) {
50b00c600eSVille Syrjälä 			if (fb->format->depth == 16) {
51de7500eaSEgbert Eich 				if (i > (MGAG200_LUT_SIZE >> 1)) {
52de7500eaSEgbert Eich 					r = b = 0;
53de7500eaSEgbert Eich 				} else {
549ed85e14SPeter Rosin 					r = *r_ptr++ >> 8;
559ed85e14SPeter Rosin 					b = *b_ptr++ >> 8;
569ed85e14SPeter Rosin 					r_ptr++;
579ed85e14SPeter Rosin 					b_ptr++;
58de7500eaSEgbert Eich 				}
59de7500eaSEgbert Eich 			} else {
609ed85e14SPeter Rosin 				r = *r_ptr++ >> 8;
619ed85e14SPeter Rosin 				b = *b_ptr++ >> 8;
62de7500eaSEgbert Eich 			}
63de7500eaSEgbert Eich 			/* VGA registers */
64de7500eaSEgbert Eich 			WREG8(DAC_INDEX + MGA1064_COL_PAL, r);
659ed85e14SPeter Rosin 			WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8);
66de7500eaSEgbert Eich 			WREG8(DAC_INDEX + MGA1064_COL_PAL, b);
67de7500eaSEgbert Eich 		}
68de7500eaSEgbert Eich 		return;
69de7500eaSEgbert Eich 	}
70414c4531SDave Airlie 	for (i = 0; i < MGAG200_LUT_SIZE; i++) {
71414c4531SDave Airlie 		/* VGA registers */
729ed85e14SPeter Rosin 		WREG8(DAC_INDEX + MGA1064_COL_PAL, *r_ptr++ >> 8);
739ed85e14SPeter Rosin 		WREG8(DAC_INDEX + MGA1064_COL_PAL, *g_ptr++ >> 8);
749ed85e14SPeter Rosin 		WREG8(DAC_INDEX + MGA1064_COL_PAL, *b_ptr++ >> 8);
75414c4531SDave Airlie 	}
76414c4531SDave Airlie }
77414c4531SDave Airlie 
78414c4531SDave Airlie static inline void mga_wait_vsync(struct mga_device *mdev)
79414c4531SDave Airlie {
803cdc0e8dSChristopher Harvey 	unsigned long timeout = jiffies + HZ/10;
81414c4531SDave Airlie 	unsigned int status = 0;
82414c4531SDave Airlie 
83414c4531SDave Airlie 	do {
84414c4531SDave Airlie 		status = RREG32(MGAREG_Status);
853cdc0e8dSChristopher Harvey 	} while ((status & 0x08) && time_before(jiffies, timeout));
863cdc0e8dSChristopher Harvey 	timeout = jiffies + HZ/10;
87414c4531SDave Airlie 	status = 0;
88414c4531SDave Airlie 	do {
89414c4531SDave Airlie 		status = RREG32(MGAREG_Status);
903cdc0e8dSChristopher Harvey 	} while (!(status & 0x08) && time_before(jiffies, timeout));
91414c4531SDave Airlie }
92414c4531SDave Airlie 
93414c4531SDave Airlie static inline void mga_wait_busy(struct mga_device *mdev)
94414c4531SDave Airlie {
953cdc0e8dSChristopher Harvey 	unsigned long timeout = jiffies + HZ;
96414c4531SDave Airlie 	unsigned int status = 0;
97414c4531SDave Airlie 	do {
98414c4531SDave Airlie 		status = RREG8(MGAREG_Status + 2);
993cdc0e8dSChristopher Harvey 	} while ((status & 0x01) && time_before(jiffies, timeout));
100414c4531SDave Airlie }
101414c4531SDave Airlie 
102e829d7efSMathieu Larouche #define P_ARRAY_SIZE 9
103e829d7efSMathieu Larouche 
104414c4531SDave Airlie static int mga_g200se_set_plls(struct mga_device *mdev, long clock)
105414c4531SDave Airlie {
106414c4531SDave Airlie 	unsigned int vcomax, vcomin, pllreffreq;
107414c4531SDave Airlie 	unsigned int delta, tmpdelta, permitteddelta;
108414c4531SDave Airlie 	unsigned int testp, testm, testn;
109414c4531SDave Airlie 	unsigned int p, m, n;
110414c4531SDave Airlie 	unsigned int computed;
111e829d7efSMathieu Larouche 	unsigned int pvalues_e4[P_ARRAY_SIZE] = {16, 14, 12, 10, 8, 6, 4, 2, 1};
112e829d7efSMathieu Larouche 	unsigned int fvv;
113e829d7efSMathieu Larouche 	unsigned int i;
114e829d7efSMathieu Larouche 
115e829d7efSMathieu Larouche 	if (mdev->unique_rev_id <= 0x03) {
116414c4531SDave Airlie 
117414c4531SDave Airlie 		m = n = p = 0;
118414c4531SDave Airlie 		vcomax = 320000;
119414c4531SDave Airlie 		vcomin = 160000;
120414c4531SDave Airlie 		pllreffreq = 25000;
121414c4531SDave Airlie 
122414c4531SDave Airlie 		delta = 0xffffffff;
123414c4531SDave Airlie 		permitteddelta = clock * 5 / 1000;
124414c4531SDave Airlie 
125414c4531SDave Airlie 		for (testp = 8; testp > 0; testp /= 2) {
126414c4531SDave Airlie 			if (clock * testp > vcomax)
127414c4531SDave Airlie 				continue;
128414c4531SDave Airlie 			if (clock * testp < vcomin)
129414c4531SDave Airlie 				continue;
130414c4531SDave Airlie 
131414c4531SDave Airlie 			for (testn = 17; testn < 256; testn++) {
132414c4531SDave Airlie 				for (testm = 1; testm < 32; testm++) {
133414c4531SDave Airlie 					computed = (pllreffreq * testn) /
134414c4531SDave Airlie 						(testm * testp);
135414c4531SDave Airlie 					if (computed > clock)
136414c4531SDave Airlie 						tmpdelta = computed - clock;
137414c4531SDave Airlie 					else
138414c4531SDave Airlie 						tmpdelta = clock - computed;
139414c4531SDave Airlie 					if (tmpdelta < delta) {
140414c4531SDave Airlie 						delta = tmpdelta;
141414c4531SDave Airlie 						m = testm - 1;
142414c4531SDave Airlie 						n = testn - 1;
143414c4531SDave Airlie 						p = testp - 1;
144414c4531SDave Airlie 					}
145414c4531SDave Airlie 				}
146414c4531SDave Airlie 			}
147414c4531SDave Airlie 		}
148e829d7efSMathieu Larouche 	} else {
149e829d7efSMathieu Larouche 
150e829d7efSMathieu Larouche 
151e829d7efSMathieu Larouche 		m = n = p = 0;
152e829d7efSMathieu Larouche 		vcomax        = 1600000;
153e829d7efSMathieu Larouche 		vcomin        = 800000;
154e829d7efSMathieu Larouche 		pllreffreq    = 25000;
155e829d7efSMathieu Larouche 
156e829d7efSMathieu Larouche 		if (clock < 25000)
157e829d7efSMathieu Larouche 			clock = 25000;
158e829d7efSMathieu Larouche 
159e829d7efSMathieu Larouche 		clock = clock * 2;
160e829d7efSMathieu Larouche 
161e829d7efSMathieu Larouche 		delta = 0xFFFFFFFF;
162e829d7efSMathieu Larouche 		/* Permited delta is 0.5% as VESA Specification */
163e829d7efSMathieu Larouche 		permitteddelta = clock * 5 / 1000;
164e829d7efSMathieu Larouche 
165e829d7efSMathieu Larouche 		for (i = 0 ; i < P_ARRAY_SIZE ; i++) {
166e829d7efSMathieu Larouche 			testp = pvalues_e4[i];
167e829d7efSMathieu Larouche 
168e829d7efSMathieu Larouche 			if ((clock * testp) > vcomax)
169e829d7efSMathieu Larouche 				continue;
170e829d7efSMathieu Larouche 			if ((clock * testp) < vcomin)
171e829d7efSMathieu Larouche 				continue;
172e829d7efSMathieu Larouche 
173e829d7efSMathieu Larouche 			for (testn = 50; testn <= 256; testn++) {
174e829d7efSMathieu Larouche 				for (testm = 1; testm <= 32; testm++) {
175e829d7efSMathieu Larouche 					computed = (pllreffreq * testn) /
176e829d7efSMathieu Larouche 						(testm * testp);
177e829d7efSMathieu Larouche 					if (computed > clock)
178e829d7efSMathieu Larouche 						tmpdelta = computed - clock;
179e829d7efSMathieu Larouche 					else
180e829d7efSMathieu Larouche 						tmpdelta = clock - computed;
181e829d7efSMathieu Larouche 
182e829d7efSMathieu Larouche 					if (tmpdelta < delta) {
183e829d7efSMathieu Larouche 						delta = tmpdelta;
184e829d7efSMathieu Larouche 						m = testm - 1;
185e829d7efSMathieu Larouche 						n = testn - 1;
186e829d7efSMathieu Larouche 						p = testp - 1;
187e829d7efSMathieu Larouche 					}
188e829d7efSMathieu Larouche 				}
189e829d7efSMathieu Larouche 			}
190e829d7efSMathieu Larouche 		}
191e829d7efSMathieu Larouche 
192d3922b69SMathieu Larouche 		fvv = pllreffreq * (n + 1) / (m + 1);
193e829d7efSMathieu Larouche 		fvv = (fvv - 800000) / 50000;
194e829d7efSMathieu Larouche 
195e829d7efSMathieu Larouche 		if (fvv > 15)
196e829d7efSMathieu Larouche 			fvv = 15;
197e829d7efSMathieu Larouche 
198e829d7efSMathieu Larouche 		p |= (fvv << 4);
199e829d7efSMathieu Larouche 		m |= 0x80;
200e829d7efSMathieu Larouche 
201e829d7efSMathieu Larouche 		clock = clock / 2;
202e829d7efSMathieu Larouche 	}
203414c4531SDave Airlie 
204414c4531SDave Airlie 	if (delta > permitteddelta) {
2058dfe162aSJoe Perches 		pr_warn("PLL delta too large\n");
206414c4531SDave Airlie 		return 1;
207414c4531SDave Airlie 	}
208414c4531SDave Airlie 
209414c4531SDave Airlie 	WREG_DAC(MGA1064_PIX_PLLC_M, m);
210414c4531SDave Airlie 	WREG_DAC(MGA1064_PIX_PLLC_N, n);
211414c4531SDave Airlie 	WREG_DAC(MGA1064_PIX_PLLC_P, p);
212d3922b69SMathieu Larouche 
213d3922b69SMathieu Larouche 	if (mdev->unique_rev_id >= 0x04) {
214d3922b69SMathieu Larouche 		WREG_DAC(0x1a, 0x09);
215d3922b69SMathieu Larouche 		msleep(20);
216d3922b69SMathieu Larouche 		WREG_DAC(0x1a, 0x01);
217d3922b69SMathieu Larouche 
218d3922b69SMathieu Larouche 	}
219d3922b69SMathieu Larouche 
220414c4531SDave Airlie 	return 0;
221414c4531SDave Airlie }
222414c4531SDave Airlie 
223414c4531SDave Airlie static int mga_g200wb_set_plls(struct mga_device *mdev, long clock)
224414c4531SDave Airlie {
225414c4531SDave Airlie 	unsigned int vcomax, vcomin, pllreffreq;
226546aee51SSudip Mukherjee 	unsigned int delta, tmpdelta;
2276d857c18SMathieu Larouche 	unsigned int testp, testm, testn, testp2;
228414c4531SDave Airlie 	unsigned int p, m, n;
229414c4531SDave Airlie 	unsigned int computed;
230414c4531SDave Airlie 	int i, j, tmpcount, vcount;
231414c4531SDave Airlie 	bool pll_locked = false;
232414c4531SDave Airlie 	u8 tmp;
233414c4531SDave Airlie 
234414c4531SDave Airlie 	m = n = p = 0;
2356d857c18SMathieu Larouche 
2366d857c18SMathieu Larouche 	delta = 0xffffffff;
2376d857c18SMathieu Larouche 
2386d857c18SMathieu Larouche 	if (mdev->type == G200_EW3) {
2396d857c18SMathieu Larouche 
2406d857c18SMathieu Larouche 		vcomax = 800000;
2416d857c18SMathieu Larouche 		vcomin = 400000;
2426d857c18SMathieu Larouche 		pllreffreq = 25000;
2436d857c18SMathieu Larouche 
2446d857c18SMathieu Larouche 		for (testp = 1; testp < 8; testp++) {
2456d857c18SMathieu Larouche 			for (testp2 = 1; testp2 < 8; testp2++) {
2466d857c18SMathieu Larouche 				if (testp < testp2)
2476d857c18SMathieu Larouche 					continue;
2486d857c18SMathieu Larouche 				if ((clock * testp * testp2) > vcomax)
2496d857c18SMathieu Larouche 					continue;
2506d857c18SMathieu Larouche 				if ((clock * testp * testp2) < vcomin)
2516d857c18SMathieu Larouche 					continue;
2526d857c18SMathieu Larouche 				for (testm = 1; testm < 26; testm++) {
2536d857c18SMathieu Larouche 					for (testn = 32; testn < 2048 ; testn++) {
2546d857c18SMathieu Larouche 						computed = (pllreffreq * testn) /
2556d857c18SMathieu Larouche 							(testm * testp * testp2);
2566d857c18SMathieu Larouche 						if (computed > clock)
2576d857c18SMathieu Larouche 							tmpdelta = computed - clock;
2586d857c18SMathieu Larouche 						else
2596d857c18SMathieu Larouche 							tmpdelta = clock - computed;
2606d857c18SMathieu Larouche 						if (tmpdelta < delta) {
2616d857c18SMathieu Larouche 							delta = tmpdelta;
2626d857c18SMathieu Larouche 							m = ((testn & 0x100) >> 1) |
2636d857c18SMathieu Larouche 								(testm);
2646d857c18SMathieu Larouche 							n = (testn & 0xFF);
2656d857c18SMathieu Larouche 							p = ((testn & 0x600) >> 3) |
2666d857c18SMathieu Larouche 								(testp2 << 3) |
2676d857c18SMathieu Larouche 								(testp);
2686d857c18SMathieu Larouche 						}
2696d857c18SMathieu Larouche 					}
2706d857c18SMathieu Larouche 				}
2716d857c18SMathieu Larouche 			}
2726d857c18SMathieu Larouche 		}
2736d857c18SMathieu Larouche 	} else {
2746d857c18SMathieu Larouche 
275414c4531SDave Airlie 		vcomax = 550000;
276414c4531SDave Airlie 		vcomin = 150000;
277414c4531SDave Airlie 		pllreffreq = 48000;
278414c4531SDave Airlie 
279414c4531SDave Airlie 		for (testp = 1; testp < 9; testp++) {
280414c4531SDave Airlie 			if (clock * testp > vcomax)
281414c4531SDave Airlie 				continue;
282414c4531SDave Airlie 			if (clock * testp < vcomin)
283414c4531SDave Airlie 				continue;
284414c4531SDave Airlie 
285414c4531SDave Airlie 			for (testm = 1; testm < 17; testm++) {
286414c4531SDave Airlie 				for (testn = 1; testn < 151; testn++) {
287414c4531SDave Airlie 					computed = (pllreffreq * testn) /
288414c4531SDave Airlie 						(testm * testp);
289414c4531SDave Airlie 					if (computed > clock)
290414c4531SDave Airlie 						tmpdelta = computed - clock;
291414c4531SDave Airlie 					else
292414c4531SDave Airlie 						tmpdelta = clock - computed;
293414c4531SDave Airlie 					if (tmpdelta < delta) {
294414c4531SDave Airlie 						delta = tmpdelta;
295414c4531SDave Airlie 						n = testn - 1;
2966d857c18SMathieu Larouche 						m = (testm - 1) |
2976d857c18SMathieu Larouche 							((n >> 1) & 0x80);
298414c4531SDave Airlie 						p = testp - 1;
299414c4531SDave Airlie 					}
300414c4531SDave Airlie 				}
301414c4531SDave Airlie 			}
302414c4531SDave Airlie 		}
3036d857c18SMathieu Larouche 	}
304414c4531SDave Airlie 
305414c4531SDave Airlie 	for (i = 0; i <= 32 && pll_locked == false; i++) {
306414c4531SDave Airlie 		if (i > 0) {
307414c4531SDave Airlie 			WREG8(MGAREG_CRTC_INDEX, 0x1e);
308414c4531SDave Airlie 			tmp = RREG8(MGAREG_CRTC_DATA);
309414c4531SDave Airlie 			if (tmp < 0xff)
310414c4531SDave Airlie 				WREG8(MGAREG_CRTC_DATA, tmp+1);
311414c4531SDave Airlie 		}
312414c4531SDave Airlie 
313414c4531SDave Airlie 		/* set pixclkdis to 1 */
314414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
315414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
316414c4531SDave Airlie 		tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
317fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
318414c4531SDave Airlie 
319414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
320414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
321414c4531SDave Airlie 		tmp |= MGA1064_REMHEADCTL_CLKDIS;
322fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
323414c4531SDave Airlie 
324414c4531SDave Airlie 		/* select PLL Set C */
325414c4531SDave Airlie 		tmp = RREG8(MGAREG_MEM_MISC_READ);
326414c4531SDave Airlie 		tmp |= 0x3 << 2;
327414c4531SDave Airlie 		WREG8(MGAREG_MEM_MISC_WRITE, tmp);
328414c4531SDave Airlie 
329414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
330414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
331414c4531SDave Airlie 		tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN | 0x80;
332fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
333414c4531SDave Airlie 
334414c4531SDave Airlie 		udelay(500);
335414c4531SDave Airlie 
336414c4531SDave Airlie 		/* reset the PLL */
337414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_VREF_CTL);
338414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
339414c4531SDave Airlie 		tmp &= ~0x04;
340fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
341414c4531SDave Airlie 
342414c4531SDave Airlie 		udelay(50);
343414c4531SDave Airlie 
344414c4531SDave Airlie 		/* program pixel pll register */
345414c4531SDave Airlie 		WREG_DAC(MGA1064_WB_PIX_PLLC_N, n);
346414c4531SDave Airlie 		WREG_DAC(MGA1064_WB_PIX_PLLC_M, m);
347414c4531SDave Airlie 		WREG_DAC(MGA1064_WB_PIX_PLLC_P, p);
348414c4531SDave Airlie 
349414c4531SDave Airlie 		udelay(50);
350414c4531SDave Airlie 
351414c4531SDave Airlie 		/* turn pll on */
352414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_VREF_CTL);
353414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
354414c4531SDave Airlie 		tmp |= 0x04;
355414c4531SDave Airlie 		WREG_DAC(MGA1064_VREF_CTL, tmp);
356414c4531SDave Airlie 
357414c4531SDave Airlie 		udelay(500);
358414c4531SDave Airlie 
359414c4531SDave Airlie 		/* select the pixel pll */
360414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
361414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
362414c4531SDave Airlie 		tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
363414c4531SDave Airlie 		tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
364fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
365414c4531SDave Airlie 
366414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
367414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
368414c4531SDave Airlie 		tmp &= ~MGA1064_REMHEADCTL_CLKSL_MSK;
369414c4531SDave Airlie 		tmp |= MGA1064_REMHEADCTL_CLKSL_PLL;
370fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
371414c4531SDave Airlie 
372414c4531SDave Airlie 		/* reset dotclock rate bit */
373414c4531SDave Airlie 		WREG8(MGAREG_SEQ_INDEX, 1);
374414c4531SDave Airlie 		tmp = RREG8(MGAREG_SEQ_DATA);
375414c4531SDave Airlie 		tmp &= ~0x8;
376414c4531SDave Airlie 		WREG8(MGAREG_SEQ_DATA, tmp);
377414c4531SDave Airlie 
378414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
379414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
380414c4531SDave Airlie 		tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
381fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
382414c4531SDave Airlie 
383414c4531SDave Airlie 		vcount = RREG8(MGAREG_VCOUNT);
384414c4531SDave Airlie 
385414c4531SDave Airlie 		for (j = 0; j < 30 && pll_locked == false; j++) {
386414c4531SDave Airlie 			tmpcount = RREG8(MGAREG_VCOUNT);
387414c4531SDave Airlie 			if (tmpcount < vcount)
388414c4531SDave Airlie 				vcount = 0;
389414c4531SDave Airlie 			if ((tmpcount - vcount) > 2)
390414c4531SDave Airlie 				pll_locked = true;
391414c4531SDave Airlie 			else
392414c4531SDave Airlie 				udelay(5);
393414c4531SDave Airlie 		}
394414c4531SDave Airlie 	}
395414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
396414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
397414c4531SDave Airlie 	tmp &= ~MGA1064_REMHEADCTL_CLKDIS;
398414c4531SDave Airlie 	WREG_DAC(MGA1064_REMHEADCTL, tmp);
399414c4531SDave Airlie 	return 0;
400414c4531SDave Airlie }
401414c4531SDave Airlie 
402414c4531SDave Airlie static int mga_g200ev_set_plls(struct mga_device *mdev, long clock)
403414c4531SDave Airlie {
404414c4531SDave Airlie 	unsigned int vcomax, vcomin, pllreffreq;
405546aee51SSudip Mukherjee 	unsigned int delta, tmpdelta;
406414c4531SDave Airlie 	unsigned int testp, testm, testn;
407414c4531SDave Airlie 	unsigned int p, m, n;
408414c4531SDave Airlie 	unsigned int computed;
409414c4531SDave Airlie 	u8 tmp;
410414c4531SDave Airlie 
411414c4531SDave Airlie 	m = n = p = 0;
412414c4531SDave Airlie 	vcomax = 550000;
413414c4531SDave Airlie 	vcomin = 150000;
414414c4531SDave Airlie 	pllreffreq = 50000;
415414c4531SDave Airlie 
416414c4531SDave Airlie 	delta = 0xffffffff;
417414c4531SDave Airlie 
418414c4531SDave Airlie 	for (testp = 16; testp > 0; testp--) {
419414c4531SDave Airlie 		if (clock * testp > vcomax)
420414c4531SDave Airlie 			continue;
421414c4531SDave Airlie 		if (clock * testp < vcomin)
422414c4531SDave Airlie 			continue;
423414c4531SDave Airlie 
424414c4531SDave Airlie 		for (testn = 1; testn < 257; testn++) {
425414c4531SDave Airlie 			for (testm = 1; testm < 17; testm++) {
426414c4531SDave Airlie 				computed = (pllreffreq * testn) /
427414c4531SDave Airlie 					(testm * testp);
428414c4531SDave Airlie 				if (computed > clock)
429414c4531SDave Airlie 					tmpdelta = computed - clock;
430414c4531SDave Airlie 				else
431414c4531SDave Airlie 					tmpdelta = clock - computed;
432414c4531SDave Airlie 				if (tmpdelta < delta) {
433414c4531SDave Airlie 					delta = tmpdelta;
434414c4531SDave Airlie 					n = testn - 1;
435414c4531SDave Airlie 					m = testm - 1;
436414c4531SDave Airlie 					p = testp - 1;
437414c4531SDave Airlie 				}
438414c4531SDave Airlie 			}
439414c4531SDave Airlie 		}
440414c4531SDave Airlie 	}
441414c4531SDave Airlie 
442414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
443414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
444414c4531SDave Airlie 	tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
445fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
446414c4531SDave Airlie 
447414c4531SDave Airlie 	tmp = RREG8(MGAREG_MEM_MISC_READ);
448414c4531SDave Airlie 	tmp |= 0x3 << 2;
449414c4531SDave Airlie 	WREG8(MGAREG_MEM_MISC_WRITE, tmp);
450414c4531SDave Airlie 
451414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
452414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
453fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp & ~0x40);
454414c4531SDave Airlie 
455414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
456414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
457414c4531SDave Airlie 	tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
458fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
459414c4531SDave Airlie 
460414c4531SDave Airlie 	WREG_DAC(MGA1064_EV_PIX_PLLC_M, m);
461414c4531SDave Airlie 	WREG_DAC(MGA1064_EV_PIX_PLLC_N, n);
462414c4531SDave Airlie 	WREG_DAC(MGA1064_EV_PIX_PLLC_P, p);
463414c4531SDave Airlie 
464414c4531SDave Airlie 	udelay(50);
465414c4531SDave Airlie 
466414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
467414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
468414c4531SDave Airlie 	tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
469fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
470414c4531SDave Airlie 
471414c4531SDave Airlie 	udelay(500);
472414c4531SDave Airlie 
473414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
474414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
475414c4531SDave Airlie 	tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
476414c4531SDave Airlie 	tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
477fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
478414c4531SDave Airlie 
479414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_PLL_STAT);
480414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
481fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp | 0x40);
482414c4531SDave Airlie 
483414c4531SDave Airlie 	tmp = RREG8(MGAREG_MEM_MISC_READ);
484414c4531SDave Airlie 	tmp |= (0x3 << 2);
485414c4531SDave Airlie 	WREG8(MGAREG_MEM_MISC_WRITE, tmp);
486414c4531SDave Airlie 
487414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
488414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
489414c4531SDave Airlie 	tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
490fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
491414c4531SDave Airlie 
492414c4531SDave Airlie 	return 0;
493414c4531SDave Airlie }
494414c4531SDave Airlie 
495414c4531SDave Airlie static int mga_g200eh_set_plls(struct mga_device *mdev, long clock)
496414c4531SDave Airlie {
497414c4531SDave Airlie 	unsigned int vcomax, vcomin, pllreffreq;
498546aee51SSudip Mukherjee 	unsigned int delta, tmpdelta;
499414c4531SDave Airlie 	unsigned int testp, testm, testn;
500414c4531SDave Airlie 	unsigned int p, m, n;
501414c4531SDave Airlie 	unsigned int computed;
502414c4531SDave Airlie 	int i, j, tmpcount, vcount;
503414c4531SDave Airlie 	u8 tmp;
504414c4531SDave Airlie 	bool pll_locked = false;
505414c4531SDave Airlie 
506414c4531SDave Airlie 	m = n = p = 0;
507f0493e65SMathieu Larouche 
508f0493e65SMathieu Larouche 	if (mdev->type == G200_EH3) {
509f0493e65SMathieu Larouche 		vcomax = 3000000;
510f0493e65SMathieu Larouche 		vcomin = 1500000;
511f0493e65SMathieu Larouche 		pllreffreq = 25000;
512f0493e65SMathieu Larouche 
513f0493e65SMathieu Larouche 		delta = 0xffffffff;
514f0493e65SMathieu Larouche 
515f0493e65SMathieu Larouche 		testp = 0;
516f0493e65SMathieu Larouche 
517f0493e65SMathieu Larouche 		for (testm = 150; testm >= 6; testm--) {
518f0493e65SMathieu Larouche 			if (clock * testm > vcomax)
519f0493e65SMathieu Larouche 				continue;
520f0493e65SMathieu Larouche 			if (clock * testm < vcomin)
521f0493e65SMathieu Larouche 				continue;
522f0493e65SMathieu Larouche 			for (testn = 120; testn >= 60; testn--) {
523f0493e65SMathieu Larouche 				computed = (pllreffreq * testn) / testm;
524f0493e65SMathieu Larouche 				if (computed > clock)
525f0493e65SMathieu Larouche 					tmpdelta = computed - clock;
526f0493e65SMathieu Larouche 				else
527f0493e65SMathieu Larouche 					tmpdelta = clock - computed;
528f0493e65SMathieu Larouche 				if (tmpdelta < delta) {
529f0493e65SMathieu Larouche 					delta = tmpdelta;
530f0493e65SMathieu Larouche 					n = testn;
531f0493e65SMathieu Larouche 					m = testm;
532f0493e65SMathieu Larouche 					p = testp;
533f0493e65SMathieu Larouche 				}
534f0493e65SMathieu Larouche 				if (delta == 0)
535f0493e65SMathieu Larouche 					break;
536f0493e65SMathieu Larouche 			}
537f0493e65SMathieu Larouche 			if (delta == 0)
538f0493e65SMathieu Larouche 				break;
539f0493e65SMathieu Larouche 		}
540f0493e65SMathieu Larouche 	} else {
541f0493e65SMathieu Larouche 
542414c4531SDave Airlie 		vcomax = 800000;
543414c4531SDave Airlie 		vcomin = 400000;
544260b3f12SJulia Lemire 		pllreffreq = 33333;
545414c4531SDave Airlie 
546414c4531SDave Airlie 		delta = 0xffffffff;
547414c4531SDave Airlie 
548260b3f12SJulia Lemire 		for (testp = 16; testp > 0; testp >>= 1) {
549414c4531SDave Airlie 			if (clock * testp > vcomax)
550414c4531SDave Airlie 				continue;
551414c4531SDave Airlie 			if (clock * testp < vcomin)
552414c4531SDave Airlie 				continue;
553414c4531SDave Airlie 
554414c4531SDave Airlie 			for (testm = 1; testm < 33; testm++) {
555260b3f12SJulia Lemire 				for (testn = 17; testn < 257; testn++) {
556414c4531SDave Airlie 					computed = (pllreffreq * testn) /
557414c4531SDave Airlie 						(testm * testp);
558414c4531SDave Airlie 					if (computed > clock)
559414c4531SDave Airlie 						tmpdelta = computed - clock;
560414c4531SDave Airlie 					else
561414c4531SDave Airlie 						tmpdelta = clock - computed;
562414c4531SDave Airlie 					if (tmpdelta < delta) {
563414c4531SDave Airlie 						delta = tmpdelta;
564414c4531SDave Airlie 						n = testn - 1;
565260b3f12SJulia Lemire 						m = (testm - 1);
566414c4531SDave Airlie 						p = testp - 1;
567414c4531SDave Airlie 					}
568414c4531SDave Airlie 					if ((clock * testp) >= 600000)
569260b3f12SJulia Lemire 						p |= 0x80;
570414c4531SDave Airlie 				}
571414c4531SDave Airlie 			}
572414c4531SDave Airlie 		}
573f0493e65SMathieu Larouche 	}
574414c4531SDave Airlie 	for (i = 0; i <= 32 && pll_locked == false; i++) {
575414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
576414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
577414c4531SDave Airlie 		tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
578fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
579414c4531SDave Airlie 
580414c4531SDave Airlie 		tmp = RREG8(MGAREG_MEM_MISC_READ);
581414c4531SDave Airlie 		tmp |= 0x3 << 2;
582414c4531SDave Airlie 		WREG8(MGAREG_MEM_MISC_WRITE, tmp);
583414c4531SDave Airlie 
584414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
585414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
586414c4531SDave Airlie 		tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
587fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
588414c4531SDave Airlie 
589414c4531SDave Airlie 		udelay(500);
590414c4531SDave Airlie 
591414c4531SDave Airlie 		WREG_DAC(MGA1064_EH_PIX_PLLC_M, m);
592414c4531SDave Airlie 		WREG_DAC(MGA1064_EH_PIX_PLLC_N, n);
593414c4531SDave Airlie 		WREG_DAC(MGA1064_EH_PIX_PLLC_P, p);
594414c4531SDave Airlie 
595414c4531SDave Airlie 		udelay(500);
596414c4531SDave Airlie 
597414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
598414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
599414c4531SDave Airlie 		tmp &= ~MGA1064_PIX_CLK_CTL_SEL_MSK;
600414c4531SDave Airlie 		tmp |= MGA1064_PIX_CLK_CTL_SEL_PLL;
601fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
602414c4531SDave Airlie 
603414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
604414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
605414c4531SDave Airlie 		tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
606414c4531SDave Airlie 		tmp &= ~MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
607fb70a669SChristopher Harvey 		WREG8(DAC_DATA, tmp);
608414c4531SDave Airlie 
609414c4531SDave Airlie 		vcount = RREG8(MGAREG_VCOUNT);
610414c4531SDave Airlie 
611414c4531SDave Airlie 		for (j = 0; j < 30 && pll_locked == false; j++) {
612414c4531SDave Airlie 			tmpcount = RREG8(MGAREG_VCOUNT);
613414c4531SDave Airlie 			if (tmpcount < vcount)
614414c4531SDave Airlie 				vcount = 0;
615414c4531SDave Airlie 			if ((tmpcount - vcount) > 2)
616414c4531SDave Airlie 				pll_locked = true;
617414c4531SDave Airlie 			else
618414c4531SDave Airlie 				udelay(5);
619414c4531SDave Airlie 		}
620414c4531SDave Airlie 	}
621414c4531SDave Airlie 
622414c4531SDave Airlie 	return 0;
623414c4531SDave Airlie }
624414c4531SDave Airlie 
625414c4531SDave Airlie static int mga_g200er_set_plls(struct mga_device *mdev, long clock)
626414c4531SDave Airlie {
627414c4531SDave Airlie 	unsigned int vcomax, vcomin, pllreffreq;
628414c4531SDave Airlie 	unsigned int delta, tmpdelta;
6299830605dSDave Airlie 	int testr, testn, testm, testo;
630414c4531SDave Airlie 	unsigned int p, m, n;
6319830605dSDave Airlie 	unsigned int computed, vco;
632414c4531SDave Airlie 	int tmp;
6339830605dSDave Airlie 	const unsigned int m_div_val[] = { 1, 2, 4, 8 };
634414c4531SDave Airlie 
635414c4531SDave Airlie 	m = n = p = 0;
636414c4531SDave Airlie 	vcomax = 1488000;
637414c4531SDave Airlie 	vcomin = 1056000;
638414c4531SDave Airlie 	pllreffreq = 48000;
639414c4531SDave Airlie 
640414c4531SDave Airlie 	delta = 0xffffffff;
641414c4531SDave Airlie 
642414c4531SDave Airlie 	for (testr = 0; testr < 4; testr++) {
643414c4531SDave Airlie 		if (delta == 0)
644414c4531SDave Airlie 			break;
645414c4531SDave Airlie 		for (testn = 5; testn < 129; testn++) {
646414c4531SDave Airlie 			if (delta == 0)
647414c4531SDave Airlie 				break;
648414c4531SDave Airlie 			for (testm = 3; testm >= 0; testm--) {
649414c4531SDave Airlie 				if (delta == 0)
650414c4531SDave Airlie 					break;
651414c4531SDave Airlie 				for (testo = 5; testo < 33; testo++) {
6529830605dSDave Airlie 					vco = pllreffreq * (testn + 1) /
653414c4531SDave Airlie 						(testr + 1);
6549830605dSDave Airlie 					if (vco < vcomin)
655414c4531SDave Airlie 						continue;
6569830605dSDave Airlie 					if (vco > vcomax)
657414c4531SDave Airlie 						continue;
6589830605dSDave Airlie 					computed = vco / (m_div_val[testm] * (testo + 1));
659414c4531SDave Airlie 					if (computed > clock)
660414c4531SDave Airlie 						tmpdelta = computed - clock;
661414c4531SDave Airlie 					else
662414c4531SDave Airlie 						tmpdelta = clock - computed;
663414c4531SDave Airlie 					if (tmpdelta < delta) {
664414c4531SDave Airlie 						delta = tmpdelta;
665414c4531SDave Airlie 						m = testm | (testo << 3);
666414c4531SDave Airlie 						n = testn;
667414c4531SDave Airlie 						p = testr | (testr << 3);
668414c4531SDave Airlie 					}
669414c4531SDave Airlie 				}
670414c4531SDave Airlie 			}
671414c4531SDave Airlie 		}
672414c4531SDave Airlie 	}
673414c4531SDave Airlie 
674414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
675414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
676414c4531SDave Airlie 	tmp |= MGA1064_PIX_CLK_CTL_CLK_DIS;
677fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
678414c4531SDave Airlie 
679414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_REMHEADCTL);
680414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
681414c4531SDave Airlie 	tmp |= MGA1064_REMHEADCTL_CLKDIS;
682fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
683414c4531SDave Airlie 
684414c4531SDave Airlie 	tmp = RREG8(MGAREG_MEM_MISC_READ);
685414c4531SDave Airlie 	tmp |= (0x3<<2) | 0xc0;
686414c4531SDave Airlie 	WREG8(MGAREG_MEM_MISC_WRITE, tmp);
687414c4531SDave Airlie 
688414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_PIX_CLK_CTL);
689414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
690414c4531SDave Airlie 	tmp &= ~MGA1064_PIX_CLK_CTL_CLK_DIS;
691414c4531SDave Airlie 	tmp |= MGA1064_PIX_CLK_CTL_CLK_POW_DOWN;
692fb70a669SChristopher Harvey 	WREG8(DAC_DATA, tmp);
693414c4531SDave Airlie 
694414c4531SDave Airlie 	udelay(500);
695414c4531SDave Airlie 
696414c4531SDave Airlie 	WREG_DAC(MGA1064_ER_PIX_PLLC_N, n);
697414c4531SDave Airlie 	WREG_DAC(MGA1064_ER_PIX_PLLC_M, m);
698414c4531SDave Airlie 	WREG_DAC(MGA1064_ER_PIX_PLLC_P, p);
699414c4531SDave Airlie 
700414c4531SDave Airlie 	udelay(50);
701414c4531SDave Airlie 
702414c4531SDave Airlie 	return 0;
703414c4531SDave Airlie }
704414c4531SDave Airlie 
705414c4531SDave Airlie static int mga_crtc_set_plls(struct mga_device *mdev, long clock)
706414c4531SDave Airlie {
707*db05f8d3SThomas Zimmermann 	u8 misc;
708*db05f8d3SThomas Zimmermann 
709414c4531SDave Airlie 	switch(mdev->type) {
710414c4531SDave Airlie 	case G200_SE_A:
711414c4531SDave Airlie 	case G200_SE_B:
712414c4531SDave Airlie 		return mga_g200se_set_plls(mdev, clock);
713414c4531SDave Airlie 		break;
714414c4531SDave Airlie 	case G200_WB:
7156d857c18SMathieu Larouche 	case G200_EW3:
716414c4531SDave Airlie 		return mga_g200wb_set_plls(mdev, clock);
717414c4531SDave Airlie 		break;
718414c4531SDave Airlie 	case G200_EV:
719414c4531SDave Airlie 		return mga_g200ev_set_plls(mdev, clock);
720414c4531SDave Airlie 		break;
721414c4531SDave Airlie 	case G200_EH:
722f0493e65SMathieu Larouche 	case G200_EH3:
723414c4531SDave Airlie 		return mga_g200eh_set_plls(mdev, clock);
724414c4531SDave Airlie 		break;
725414c4531SDave Airlie 	case G200_ER:
726414c4531SDave Airlie 		return mga_g200er_set_plls(mdev, clock);
727414c4531SDave Airlie 		break;
728414c4531SDave Airlie 	}
729*db05f8d3SThomas Zimmermann 
730*db05f8d3SThomas Zimmermann 	misc = RREG8(MGA_MISC_IN);
731*db05f8d3SThomas Zimmermann 	misc &= ~MGAREG_MISC_CLK_SEL_MASK;
732*db05f8d3SThomas Zimmermann 	misc |= MGAREG_MISC_CLK_SEL_MGA_MSK;
733*db05f8d3SThomas Zimmermann 	WREG8(MGA_MISC_OUT, misc);
734*db05f8d3SThomas Zimmermann 
735414c4531SDave Airlie 	return 0;
736414c4531SDave Airlie }
737414c4531SDave Airlie 
738414c4531SDave Airlie static void mga_g200wb_prepare(struct drm_crtc *crtc)
739414c4531SDave Airlie {
7408d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(crtc->dev);
741414c4531SDave Airlie 	u8 tmp;
742414c4531SDave Airlie 	int iter_max;
743414c4531SDave Airlie 
744414c4531SDave Airlie 	/* 1- The first step is to warn the BMC of an upcoming mode change.
745414c4531SDave Airlie 	 * We are putting the misc<0> to output.*/
746414c4531SDave Airlie 
747414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_GEN_IO_CTL);
748414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
749414c4531SDave Airlie 	tmp |= 0x10;
750414c4531SDave Airlie 	WREG_DAC(MGA1064_GEN_IO_CTL, tmp);
751414c4531SDave Airlie 
752414c4531SDave Airlie 	/* we are putting a 1 on the misc<0> line */
753414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
754414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
755414c4531SDave Airlie 	tmp |= 0x10;
756414c4531SDave Airlie 	WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
757414c4531SDave Airlie 
758414c4531SDave Airlie 	/* 2- Second step to mask and further scan request
759414c4531SDave Airlie 	 * This will be done by asserting the remfreqmsk bit (XSPAREREG<7>)
760414c4531SDave Airlie 	 */
761414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_SPAREREG);
762414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
763414c4531SDave Airlie 	tmp |= 0x80;
764414c4531SDave Airlie 	WREG_DAC(MGA1064_SPAREREG, tmp);
765414c4531SDave Airlie 
766414c4531SDave Airlie 	/* 3a- the third step is to verifu if there is an active scan
767414c4531SDave Airlie 	 * We are searching for a 0 on remhsyncsts <XSPAREREG<0>)
768414c4531SDave Airlie 	 */
769414c4531SDave Airlie 	iter_max = 300;
770414c4531SDave Airlie 	while (!(tmp & 0x1) && iter_max) {
771414c4531SDave Airlie 		WREG8(DAC_INDEX, MGA1064_SPAREREG);
772414c4531SDave Airlie 		tmp = RREG8(DAC_DATA);
773414c4531SDave Airlie 		udelay(1000);
774414c4531SDave Airlie 		iter_max--;
775414c4531SDave Airlie 	}
776414c4531SDave Airlie 
777414c4531SDave Airlie 	/* 3b- this step occurs only if the remove is actually scanning
778414c4531SDave Airlie 	 * we are waiting for the end of the frame which is a 1 on
779414c4531SDave Airlie 	 * remvsyncsts (XSPAREREG<1>)
780414c4531SDave Airlie 	 */
781414c4531SDave Airlie 	if (iter_max) {
782414c4531SDave Airlie 		iter_max = 300;
783414c4531SDave Airlie 		while ((tmp & 0x2) && iter_max) {
784414c4531SDave Airlie 			WREG8(DAC_INDEX, MGA1064_SPAREREG);
785414c4531SDave Airlie 			tmp = RREG8(DAC_DATA);
786414c4531SDave Airlie 			udelay(1000);
787414c4531SDave Airlie 			iter_max--;
788414c4531SDave Airlie 		}
789414c4531SDave Airlie 	}
790414c4531SDave Airlie }
791414c4531SDave Airlie 
792414c4531SDave Airlie static void mga_g200wb_commit(struct drm_crtc *crtc)
793414c4531SDave Airlie {
794414c4531SDave Airlie 	u8 tmp;
7958d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(crtc->dev);
796414c4531SDave Airlie 
797414c4531SDave Airlie 	/* 1- The first step is to ensure that the vrsten and hrsten are set */
798414c4531SDave Airlie 	WREG8(MGAREG_CRTCEXT_INDEX, 1);
799414c4531SDave Airlie 	tmp = RREG8(MGAREG_CRTCEXT_DATA);
800414c4531SDave Airlie 	WREG8(MGAREG_CRTCEXT_DATA, tmp | 0x88);
801414c4531SDave Airlie 
802414c4531SDave Airlie 	/* 2- second step is to assert the rstlvl2 */
803414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
804414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
805414c4531SDave Airlie 	tmp |= 0x8;
806414c4531SDave Airlie 	WREG8(DAC_DATA, tmp);
807414c4531SDave Airlie 
808414c4531SDave Airlie 	/* wait 10 us */
809414c4531SDave Airlie 	udelay(10);
810414c4531SDave Airlie 
811414c4531SDave Airlie 	/* 3- deassert rstlvl2 */
812414c4531SDave Airlie 	tmp &= ~0x08;
813414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_REMHEADCTL2);
814414c4531SDave Airlie 	WREG8(DAC_DATA, tmp);
815414c4531SDave Airlie 
816414c4531SDave Airlie 	/* 4- remove mask of scan request */
817414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_SPAREREG);
818414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
819414c4531SDave Airlie 	tmp &= ~0x80;
820414c4531SDave Airlie 	WREG8(DAC_DATA, tmp);
821414c4531SDave Airlie 
822414c4531SDave Airlie 	/* 5- put back a 0 on the misc<0> line */
823414c4531SDave Airlie 	WREG8(DAC_INDEX, MGA1064_GEN_IO_DATA);
824414c4531SDave Airlie 	tmp = RREG8(DAC_DATA);
825414c4531SDave Airlie 	tmp &= ~0x10;
826414c4531SDave Airlie 	WREG_DAC(MGA1064_GEN_IO_DATA, tmp);
827414c4531SDave Airlie }
828414c4531SDave Airlie 
8299f1d0366SChristopher Harvey /*
830d6237687SThomas Zimmermann  * This is how the framebuffer base address is stored in g200 cards:
831d6237687SThomas Zimmermann  *   * Assume @offset is the gpu_addr variable of the framebuffer object
832d6237687SThomas Zimmermann  *   * Then addr is the number of _pixels_ (not bytes) from the start of
833d6237687SThomas Zimmermann  *     VRAM to the first pixel we want to display. (divided by 2 for 32bit
834d6237687SThomas Zimmermann  *     framebuffers)
835d6237687SThomas Zimmermann  *   * addr is stored in the CRTCEXT0, CRTCC and CRTCD registers
836d6237687SThomas Zimmermann  *      addr<20> -> CRTCEXT0<6>
837d6237687SThomas Zimmermann  *      addr<19-16> -> CRTCEXT0<3-0>
838d6237687SThomas Zimmermann  *      addr<15-8> -> CRTCC<7-0>
839d6237687SThomas Zimmermann  *      addr<7-0> -> CRTCD<7-0>
840d6237687SThomas Zimmermann  *
841d6237687SThomas Zimmermann  *  CRTCEXT0 has to be programmed last to trigger an update and make the
842d6237687SThomas Zimmermann  *  new addr variable take effect.
8439f1d0366SChristopher Harvey  */
844d6237687SThomas Zimmermann static void mgag200_set_startadd(struct mga_device *mdev,
845d6237687SThomas Zimmermann 				 unsigned long offset)
846414c4531SDave Airlie {
847d6237687SThomas Zimmermann 	struct drm_device *dev = mdev->dev;
848d6237687SThomas Zimmermann 	u32 startadd;
849d6237687SThomas Zimmermann 	u8 crtcc, crtcd, crtcext0;
850414c4531SDave Airlie 
851d6237687SThomas Zimmermann 	startadd = offset / 8;
852414c4531SDave Airlie 
853d6237687SThomas Zimmermann 	/*
854d6237687SThomas Zimmermann 	 * Can't store addresses any higher than that, but we also
855d6237687SThomas Zimmermann 	 * don't have more than 16 MiB of memory, so it should be fine.
856d6237687SThomas Zimmermann 	 */
857d6237687SThomas Zimmermann 	drm_WARN_ON(dev, startadd > 0x1fffff);
858414c4531SDave Airlie 
859d6237687SThomas Zimmermann 	RREG_ECRT(0x00, crtcext0);
860d6237687SThomas Zimmermann 
861d6237687SThomas Zimmermann 	crtcc = (startadd >> 8) & 0xff;
862d6237687SThomas Zimmermann 	crtcd = startadd & 0xff;
863d6237687SThomas Zimmermann 	crtcext0 &= 0xb0;
864d6237687SThomas Zimmermann 	crtcext0 |= ((startadd >> 14) & BIT(6)) |
865d6237687SThomas Zimmermann 		    ((startadd >> 16) & 0x0f);
866d6237687SThomas Zimmermann 
867d6237687SThomas Zimmermann 	WREG_CRT(0x0c, crtcc);
868d6237687SThomas Zimmermann 	WREG_CRT(0x0d, crtcd);
869d6237687SThomas Zimmermann 	WREG_ECRT(0x00, crtcext0);
870414c4531SDave Airlie }
871414c4531SDave Airlie 
872fb724f1eSThomas Zimmermann static int mga_crtc_do_set_base(struct mga_device *mdev,
873fb724f1eSThomas Zimmermann 				const struct drm_framebuffer *fb,
874fb724f1eSThomas Zimmermann 				const struct drm_framebuffer *old_fb)
875414c4531SDave Airlie {
876ebb04eb3SThomas Zimmermann 	struct drm_gem_vram_object *gbo;
877414c4531SDave Airlie 	int ret;
878ebb04eb3SThomas Zimmermann 	s64 gpu_addr;
879414c4531SDave Airlie 
880fb724f1eSThomas Zimmermann 	if (old_fb) {
881fb724f1eSThomas Zimmermann 		gbo = drm_gem_vram_of_gem(old_fb->obj[0]);
88281da87f6SThomas Zimmermann 		drm_gem_vram_unpin(gbo);
883414c4531SDave Airlie 	}
884414c4531SDave Airlie 
885fb724f1eSThomas Zimmermann 	gbo = drm_gem_vram_of_gem(fb->obj[0]);
886414c4531SDave Airlie 
887ebb04eb3SThomas Zimmermann 	ret = drm_gem_vram_pin(gbo, DRM_GEM_VRAM_PL_FLAG_VRAM);
888ebb04eb3SThomas Zimmermann 	if (ret)
8895b24f715SThomas Zimmermann 		return ret;
890ebb04eb3SThomas Zimmermann 	gpu_addr = drm_gem_vram_offset(gbo);
891ebb04eb3SThomas Zimmermann 	if (gpu_addr < 0) {
892ebb04eb3SThomas Zimmermann 		ret = (int)gpu_addr;
893ebb04eb3SThomas Zimmermann 		goto err_drm_gem_vram_unpin;
894414c4531SDave Airlie 	}
895414c4531SDave Airlie 
896d6237687SThomas Zimmermann 	mgag200_set_startadd(mdev, (unsigned long)gpu_addr);
897414c4531SDave Airlie 
898414c4531SDave Airlie 	return 0;
899ebb04eb3SThomas Zimmermann 
900ebb04eb3SThomas Zimmermann err_drm_gem_vram_unpin:
901ebb04eb3SThomas Zimmermann 	drm_gem_vram_unpin(gbo);
902ebb04eb3SThomas Zimmermann 	return ret;
903414c4531SDave Airlie }
904414c4531SDave Airlie 
905414c4531SDave Airlie static int mga_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
906414c4531SDave Airlie 				  struct drm_framebuffer *old_fb)
907414c4531SDave Airlie {
908d6237687SThomas Zimmermann 	struct drm_device *dev = crtc->dev;
909d6237687SThomas Zimmermann 	struct mga_device *mdev = dev->dev_private;
910fb724f1eSThomas Zimmermann 	struct drm_framebuffer *fb = crtc->primary->fb;
911d6237687SThomas Zimmermann 	unsigned int count;
912d6237687SThomas Zimmermann 
913d6237687SThomas Zimmermann 	do { } while (RREG8(0x1fda) & 0x08);
914d6237687SThomas Zimmermann 	do { } while (!(RREG8(0x1fda) & 0x08));
915d6237687SThomas Zimmermann 
916d6237687SThomas Zimmermann 	count = RREG8(MGAREG_VCOUNT) + 2;
917d6237687SThomas Zimmermann 	do { } while (RREG8(MGAREG_VCOUNT) < count);
918d6237687SThomas Zimmermann 
919fb724f1eSThomas Zimmermann 	return mga_crtc_do_set_base(mdev, fb, old_fb);
920414c4531SDave Airlie }
921414c4531SDave Airlie 
922a6edae07SThomas Zimmermann static void mgag200_set_mode_regs(struct mga_device *mdev,
923a6edae07SThomas Zimmermann 				  const struct drm_display_mode *mode)
924a6edae07SThomas Zimmermann {
925a6edae07SThomas Zimmermann 	unsigned int hdisplay, hsyncstart, hsyncend, htotal;
926a6edae07SThomas Zimmermann 	unsigned int vdisplay, vsyncstart, vsyncend, vtotal;
927*db05f8d3SThomas Zimmermann 	u8 misc, crtcext1, crtcext2, crtcext5;
928a6edae07SThomas Zimmermann 
929a6edae07SThomas Zimmermann 	hdisplay = mode->hdisplay / 8 - 1;
930a6edae07SThomas Zimmermann 	hsyncstart = mode->hsync_start / 8 - 1;
931a6edae07SThomas Zimmermann 	hsyncend = mode->hsync_end / 8 - 1;
932a6edae07SThomas Zimmermann 	htotal = mode->htotal / 8 - 1;
933a6edae07SThomas Zimmermann 
934a6edae07SThomas Zimmermann 	/* Work around hardware quirk */
935a6edae07SThomas Zimmermann 	if ((htotal & 0x07) == 0x06 || (htotal & 0x07) == 0x04)
936a6edae07SThomas Zimmermann 		htotal++;
937a6edae07SThomas Zimmermann 
938a6edae07SThomas Zimmermann 	vdisplay = mode->vdisplay - 1;
939a6edae07SThomas Zimmermann 	vsyncstart = mode->vsync_start - 1;
940a6edae07SThomas Zimmermann 	vsyncend = mode->vsync_end - 1;
941a6edae07SThomas Zimmermann 	vtotal = mode->vtotal - 2;
942a6edae07SThomas Zimmermann 
943*db05f8d3SThomas Zimmermann 	misc = RREG8(MGA_MISC_IN);
944*db05f8d3SThomas Zimmermann 
945a6edae07SThomas Zimmermann 	if (mode->flags & DRM_MODE_FLAG_NHSYNC)
946*db05f8d3SThomas Zimmermann 		misc |= MGAREG_MISC_HSYNCPOL;
947*db05f8d3SThomas Zimmermann 	else
948*db05f8d3SThomas Zimmermann 		misc &= ~MGAREG_MISC_HSYNCPOL;
949*db05f8d3SThomas Zimmermann 
950a6edae07SThomas Zimmermann 	if (mode->flags & DRM_MODE_FLAG_NVSYNC)
951*db05f8d3SThomas Zimmermann 		misc |= MGAREG_MISC_VSYNCPOL;
952*db05f8d3SThomas Zimmermann 	else
953*db05f8d3SThomas Zimmermann 		misc &= ~MGAREG_MISC_VSYNCPOL;
954a6edae07SThomas Zimmermann 
955a6edae07SThomas Zimmermann 	crtcext1 = (((htotal - 4) & 0x100) >> 8) |
956a6edae07SThomas Zimmermann 		   ((hdisplay & 0x100) >> 7) |
957a6edae07SThomas Zimmermann 		   ((hsyncstart & 0x100) >> 6) |
958a6edae07SThomas Zimmermann 		    (htotal & 0x40);
959a6edae07SThomas Zimmermann 	if (mdev->type == G200_WB || mdev->type == G200_EW3)
960a6edae07SThomas Zimmermann 		crtcext1 |= BIT(7) | /* vrsten */
961a6edae07SThomas Zimmermann 			    BIT(3); /* hrsten */
962a6edae07SThomas Zimmermann 
963a6edae07SThomas Zimmermann 	crtcext2 = ((vtotal & 0xc00) >> 10) |
964a6edae07SThomas Zimmermann 		   ((vdisplay & 0x400) >> 8) |
965a6edae07SThomas Zimmermann 		   ((vdisplay & 0xc00) >> 7) |
966a6edae07SThomas Zimmermann 		   ((vsyncstart & 0xc00) >> 5) |
967a6edae07SThomas Zimmermann 		   ((vdisplay & 0x400) >> 3);
968a6edae07SThomas Zimmermann 	crtcext5 = 0x00;
969a6edae07SThomas Zimmermann 
970a6edae07SThomas Zimmermann 	WREG_CRT(0, htotal - 4);
971a6edae07SThomas Zimmermann 	WREG_CRT(1, hdisplay);
972a6edae07SThomas Zimmermann 	WREG_CRT(2, hdisplay);
973a6edae07SThomas Zimmermann 	WREG_CRT(3, (htotal & 0x1F) | 0x80);
974a6edae07SThomas Zimmermann 	WREG_CRT(4, hsyncstart);
975a6edae07SThomas Zimmermann 	WREG_CRT(5, ((htotal & 0x20) << 2) | (hsyncend & 0x1F));
976a6edae07SThomas Zimmermann 	WREG_CRT(6, vtotal & 0xFF);
977a6edae07SThomas Zimmermann 	WREG_CRT(7, ((vtotal & 0x100) >> 8) |
978a6edae07SThomas Zimmermann 		 ((vdisplay & 0x100) >> 7) |
979a6edae07SThomas Zimmermann 		 ((vsyncstart & 0x100) >> 6) |
980a6edae07SThomas Zimmermann 		 ((vdisplay & 0x100) >> 5) |
981a6edae07SThomas Zimmermann 		 ((vdisplay & 0x100) >> 4) | /* linecomp */
982a6edae07SThomas Zimmermann 		 ((vtotal & 0x200) >> 4) |
983a6edae07SThomas Zimmermann 		 ((vdisplay & 0x200) >> 3) |
984a6edae07SThomas Zimmermann 		 ((vsyncstart & 0x200) >> 2));
985a6edae07SThomas Zimmermann 	WREG_CRT(9, ((vdisplay & 0x200) >> 4) |
986a6edae07SThomas Zimmermann 		 ((vdisplay & 0x200) >> 3));
987a6edae07SThomas Zimmermann 	WREG_CRT(16, vsyncstart & 0xFF);
988a6edae07SThomas Zimmermann 	WREG_CRT(17, (vsyncend & 0x0F) | 0x20);
989a6edae07SThomas Zimmermann 	WREG_CRT(18, vdisplay & 0xFF);
990a6edae07SThomas Zimmermann 	WREG_CRT(20, 0);
991a6edae07SThomas Zimmermann 	WREG_CRT(21, vdisplay & 0xFF);
992a6edae07SThomas Zimmermann 	WREG_CRT(22, (vtotal + 1) & 0xFF);
993a6edae07SThomas Zimmermann 	WREG_CRT(23, 0xc3);
994a6edae07SThomas Zimmermann 	WREG_CRT(24, vdisplay & 0xFF);
995a6edae07SThomas Zimmermann 
996a6edae07SThomas Zimmermann 	WREG_ECRT(0x01, crtcext1);
997a6edae07SThomas Zimmermann 	WREG_ECRT(0x02, crtcext2);
998a6edae07SThomas Zimmermann 	WREG_ECRT(0x05, crtcext5);
999*db05f8d3SThomas Zimmermann 
1000*db05f8d3SThomas Zimmermann 	WREG8(MGA_MISC_OUT, misc);
1001*db05f8d3SThomas Zimmermann 
1002*db05f8d3SThomas Zimmermann 	mga_crtc_set_plls(mdev, mode->clock);
1003a6edae07SThomas Zimmermann }
1004a6edae07SThomas Zimmermann 
1005414c4531SDave Airlie static int mga_crtc_mode_set(struct drm_crtc *crtc,
1006414c4531SDave Airlie 				struct drm_display_mode *mode,
1007414c4531SDave Airlie 				struct drm_display_mode *adjusted_mode,
1008414c4531SDave Airlie 				int x, int y, struct drm_framebuffer *old_fb)
1009414c4531SDave Airlie {
1010414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
10118d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(dev);
101272952757SVille Syrjälä 	const struct drm_framebuffer *fb = crtc->primary->fb;
1013414c4531SDave Airlie 	int pitch;
1014414c4531SDave Airlie 	int option = 0, option2 = 0;
1015414c4531SDave Airlie 	int i;
1016414c4531SDave Airlie 	unsigned char misc = 0;
1017414c4531SDave Airlie 	unsigned char ext_vga[6];
1018414c4531SDave Airlie 	u8 bppshift;
1019414c4531SDave Airlie 
1020414c4531SDave Airlie 	static unsigned char dacvalue[] = {
1021414c4531SDave Airlie 		/* 0x00: */        0,    0,    0,    0,    0,    0, 0x00,    0,
1022414c4531SDave Airlie 		/* 0x08: */        0,    0,    0,    0,    0,    0,    0,    0,
1023414c4531SDave Airlie 		/* 0x10: */        0,    0,    0,    0,    0,    0,    0,    0,
1024414c4531SDave Airlie 		/* 0x18: */     0x00,    0, 0xC9, 0xFF, 0xBF, 0x20, 0x1F, 0x20,
1025414c4531SDave Airlie 		/* 0x20: */     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1026414c4531SDave Airlie 		/* 0x28: */     0x00, 0x00, 0x00, 0x00,    0,    0,    0, 0x40,
1027414c4531SDave Airlie 		/* 0x30: */     0x00, 0xB0, 0x00, 0xC2, 0x34, 0x14, 0x02, 0x83,
1028414c4531SDave Airlie 		/* 0x38: */     0x00, 0x93, 0x00, 0x77, 0x00, 0x00, 0x00, 0x3A,
1029414c4531SDave Airlie 		/* 0x40: */        0,    0,    0,    0,    0,    0,    0,    0,
1030414c4531SDave Airlie 		/* 0x48: */        0,    0,    0,    0,    0,    0,    0,    0
1031414c4531SDave Airlie 	};
1032414c4531SDave Airlie 
1033272725c7SVille Syrjälä 	bppshift = mdev->bpp_shifts[fb->format->cpp[0] - 1];
1034414c4531SDave Airlie 
1035414c4531SDave Airlie 	switch (mdev->type) {
1036414c4531SDave Airlie 	case G200_SE_A:
1037414c4531SDave Airlie 	case G200_SE_B:
1038414c4531SDave Airlie 		dacvalue[MGA1064_VREF_CTL] = 0x03;
1039414c4531SDave Airlie 		dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL;
1040414c4531SDave Airlie 		dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_DAC_EN |
1041414c4531SDave Airlie 					     MGA1064_MISC_CTL_VGA8 |
1042414c4531SDave Airlie 					     MGA1064_MISC_CTL_DAC_RAM_CS;
1043414c4531SDave Airlie 		if (mdev->has_sdram)
1044414c4531SDave Airlie 			option = 0x40049120;
1045414c4531SDave Airlie 		else
1046414c4531SDave Airlie 			option = 0x4004d120;
1047414c4531SDave Airlie 		option2 = 0x00008000;
1048414c4531SDave Airlie 		break;
1049414c4531SDave Airlie 	case G200_WB:
10506d857c18SMathieu Larouche 	case G200_EW3:
1051414c4531SDave Airlie 		dacvalue[MGA1064_VREF_CTL] = 0x07;
1052414c4531SDave Airlie 		option = 0x41049120;
1053414c4531SDave Airlie 		option2 = 0x0000b000;
1054414c4531SDave Airlie 		break;
1055414c4531SDave Airlie 	case G200_EV:
1056414c4531SDave Airlie 		dacvalue[MGA1064_PIX_CLK_CTL] = MGA1064_PIX_CLK_CTL_SEL_PLL;
1057414c4531SDave Airlie 		dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 |
1058414c4531SDave Airlie 					     MGA1064_MISC_CTL_DAC_RAM_CS;
1059414c4531SDave Airlie 		option = 0x00000120;
1060414c4531SDave Airlie 		option2 = 0x0000b000;
1061414c4531SDave Airlie 		break;
1062414c4531SDave Airlie 	case G200_EH:
1063f0493e65SMathieu Larouche 	case G200_EH3:
1064414c4531SDave Airlie 		dacvalue[MGA1064_MISC_CTL] = MGA1064_MISC_CTL_VGA8 |
1065414c4531SDave Airlie 					     MGA1064_MISC_CTL_DAC_RAM_CS;
1066414c4531SDave Airlie 		option = 0x00000120;
1067414c4531SDave Airlie 		option2 = 0x0000b000;
1068414c4531SDave Airlie 		break;
1069414c4531SDave Airlie 	case G200_ER:
1070414c4531SDave Airlie 		break;
1071414c4531SDave Airlie 	}
1072414c4531SDave Airlie 
1073272725c7SVille Syrjälä 	switch (fb->format->cpp[0] * 8) {
1074414c4531SDave Airlie 	case 8:
1075414c4531SDave Airlie 		dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_8bits;
1076414c4531SDave Airlie 		break;
1077414c4531SDave Airlie 	case 16:
1078b00c600eSVille Syrjälä 		if (fb->format->depth == 15)
1079414c4531SDave Airlie 			dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_15bits;
1080414c4531SDave Airlie 		else
1081414c4531SDave Airlie 			dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_16bits;
1082414c4531SDave Airlie 		break;
1083414c4531SDave Airlie 	case 24:
1084414c4531SDave Airlie 		dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_24bits;
1085414c4531SDave Airlie 		break;
1086414c4531SDave Airlie 	case 32:
1087414c4531SDave Airlie 		dacvalue[MGA1064_MUL_CTL] = MGA1064_MUL_CTL_32_24bits;
1088414c4531SDave Airlie 		break;
1089414c4531SDave Airlie 	}
1090414c4531SDave Airlie 
1091414c4531SDave Airlie 	for (i = 0; i < sizeof(dacvalue); i++) {
10929d8aa55fSChristopher Harvey 		if ((i <= 0x17) ||
1093414c4531SDave Airlie 		    (i == 0x1b) ||
1094414c4531SDave Airlie 		    (i == 0x1c) ||
1095414c4531SDave Airlie 		    ((i >= 0x1f) && (i <= 0x29)) ||
1096414c4531SDave Airlie 		    ((i >= 0x30) && (i <= 0x37)))
1097414c4531SDave Airlie 			continue;
1098414c4531SDave Airlie 		if (IS_G200_SE(mdev) &&
1099414c4531SDave Airlie 		    ((i == 0x2c) || (i == 0x2d) || (i == 0x2e)))
1100414c4531SDave Airlie 			continue;
11016d857c18SMathieu Larouche 		if ((mdev->type == G200_EV ||
11026d857c18SMathieu Larouche 		    mdev->type == G200_WB ||
11036d857c18SMathieu Larouche 		    mdev->type == G200_EH ||
1104f0493e65SMathieu Larouche 		    mdev->type == G200_EW3 ||
1105f0493e65SMathieu Larouche 		    mdev->type == G200_EH3) &&
1106414c4531SDave Airlie 		    (i >= 0x44) && (i <= 0x4e))
1107414c4531SDave Airlie 			continue;
1108414c4531SDave Airlie 
1109414c4531SDave Airlie 		WREG_DAC(i, dacvalue[i]);
1110414c4531SDave Airlie 	}
1111414c4531SDave Airlie 
11121812a3dbSChristopher Harvey 	if (mdev->type == G200_ER)
11131812a3dbSChristopher Harvey 		WREG_DAC(0x90, 0);
1114414c4531SDave Airlie 
1115414c4531SDave Airlie 	if (option)
1116414c4531SDave Airlie 		pci_write_config_dword(dev->pdev, PCI_MGA_OPTION, option);
1117414c4531SDave Airlie 	if (option2)
1118414c4531SDave Airlie 		pci_write_config_dword(dev->pdev, PCI_MGA_OPTION2, option2);
1119414c4531SDave Airlie 
1120414c4531SDave Airlie 	WREG_SEQ(2, 0xf);
1121414c4531SDave Airlie 	WREG_SEQ(3, 0);
1122414c4531SDave Airlie 	WREG_SEQ(4, 0xe);
1123414c4531SDave Airlie 
1124272725c7SVille Syrjälä 	pitch = fb->pitches[0] / fb->format->cpp[0];
1125272725c7SVille Syrjälä 	if (fb->format->cpp[0] * 8 == 24)
1126da558398STakashi Iwai 		pitch = (pitch * 3) >> (4 - bppshift);
1127414c4531SDave Airlie 	else
1128414c4531SDave Airlie 		pitch = pitch >> (4 - bppshift);
1129414c4531SDave Airlie 
1130414c4531SDave Airlie 	WREG_GFX(0, 0);
1131414c4531SDave Airlie 	WREG_GFX(1, 0);
1132414c4531SDave Airlie 	WREG_GFX(2, 0);
1133414c4531SDave Airlie 	WREG_GFX(3, 0);
1134414c4531SDave Airlie 	WREG_GFX(4, 0);
1135414c4531SDave Airlie 	WREG_GFX(5, 0x40);
1136414c4531SDave Airlie 	WREG_GFX(6, 0x5);
1137414c4531SDave Airlie 	WREG_GFX(7, 0xf);
1138414c4531SDave Airlie 	WREG_GFX(8, 0xf);
1139414c4531SDave Airlie 
1140414c4531SDave Airlie 	WREG_CRT(10, 0);
1141414c4531SDave Airlie 	WREG_CRT(11, 0);
1142414c4531SDave Airlie 	WREG_CRT(12, 0);
1143414c4531SDave Airlie 	WREG_CRT(13, 0);
1144414c4531SDave Airlie 	WREG_CRT(14, 0);
1145414c4531SDave Airlie 	WREG_CRT(15, 0);
1146414c4531SDave Airlie 	WREG_CRT(19, pitch & 0xFF);
1147a6edae07SThomas Zimmermann 
1148a6edae07SThomas Zimmermann 	mgag200_set_mode_regs(mdev, mode);
1149414c4531SDave Airlie 
1150414c4531SDave Airlie 	ext_vga[0] = 0;
1151414c4531SDave Airlie 
1152414c4531SDave Airlie 	/* TODO interlace */
1153414c4531SDave Airlie 
1154414c4531SDave Airlie 	ext_vga[0] |= (pitch & 0x300) >> 4;
1155272725c7SVille Syrjälä 	if (fb->format->cpp[0] * 8 == 24)
1156414c4531SDave Airlie 		ext_vga[3] = (((1 << bppshift) * 3) - 1) | 0x80;
1157414c4531SDave Airlie 	else
1158414c4531SDave Airlie 		ext_vga[3] = ((1 << bppshift) - 1) | 0x80;
1159414c4531SDave Airlie 	ext_vga[4] = 0;
1160414c4531SDave Airlie 
1161a6edae07SThomas Zimmermann 	WREG_ECRT(0, ext_vga[0]);
1162a6edae07SThomas Zimmermann 	WREG_ECRT(3, ext_vga[3]);
1163a6edae07SThomas Zimmermann 	WREG_ECRT(4, ext_vga[4]);
1164414c4531SDave Airlie 
1165414c4531SDave Airlie 	if (mdev->type == G200_ER)
11661812a3dbSChristopher Harvey 		WREG_ECRT(0x24, 0x5);
1167414c4531SDave Airlie 
11686d857c18SMathieu Larouche 	if (mdev->type == G200_EW3)
11696d857c18SMathieu Larouche 		WREG_ECRT(0x34, 0x5);
11706d857c18SMathieu Larouche 
1171414c4531SDave Airlie 	if (mdev->type == G200_EV) {
1172414c4531SDave Airlie 		WREG_ECRT(6, 0);
1173414c4531SDave Airlie 	}
1174414c4531SDave Airlie 
1175414c4531SDave Airlie 	WREG_ECRT(0, ext_vga[0]);
1176414c4531SDave Airlie 
1177*db05f8d3SThomas Zimmermann 	misc = RREG8(MGA_MISC_IN);
1178*db05f8d3SThomas Zimmermann 	misc |= MGAREG_MISC_IOADSEL |
1179*db05f8d3SThomas Zimmermann 		MGAREG_MISC_RAMMAPEN |
1180*db05f8d3SThomas Zimmermann 		MGAREG_MISC_HIGH_PG_SEL;
1181414c4531SDave Airlie 	WREG8(MGA_MISC_OUT, misc);
1182414c4531SDave Airlie 
1183fb724f1eSThomas Zimmermann 	mga_crtc_do_set_base(mdev, fb, old_fb);
1184414c4531SDave Airlie 
1185414c4531SDave Airlie 	/* reset tagfifo */
1186414c4531SDave Airlie 	if (mdev->type == G200_ER) {
1187414c4531SDave Airlie 		u32 mem_ctl = RREG32(MGAREG_MEMCTL);
1188414c4531SDave Airlie 		u8 seq1;
1189414c4531SDave Airlie 
1190414c4531SDave Airlie 		/* screen off */
1191414c4531SDave Airlie 		WREG8(MGAREG_SEQ_INDEX, 0x01);
1192414c4531SDave Airlie 		seq1 = RREG8(MGAREG_SEQ_DATA) | 0x20;
1193414c4531SDave Airlie 		WREG8(MGAREG_SEQ_DATA, seq1);
1194414c4531SDave Airlie 
1195414c4531SDave Airlie 		WREG32(MGAREG_MEMCTL, mem_ctl | 0x00200000);
1196414c4531SDave Airlie 		udelay(1000);
1197414c4531SDave Airlie 		WREG32(MGAREG_MEMCTL, mem_ctl & ~0x00200000);
1198414c4531SDave Airlie 
1199414c4531SDave Airlie 		WREG8(MGAREG_SEQ_DATA, seq1 & ~0x20);
1200414c4531SDave Airlie 	}
1201414c4531SDave Airlie 
1202414c4531SDave Airlie 
1203414c4531SDave Airlie 	if (IS_G200_SE(mdev)) {
12040cbb7381SMathieu Larouche 		if  (mdev->unique_rev_id >= 0x04) {
12050cbb7381SMathieu Larouche 			WREG8(MGAREG_CRTCEXT_INDEX, 0x06);
12060cbb7381SMathieu Larouche 			WREG8(MGAREG_CRTCEXT_DATA, 0);
12070cbb7381SMathieu Larouche 		} else if (mdev->unique_rev_id >= 0x02) {
1208414c4531SDave Airlie 			u8 hi_pri_lvl;
1209414c4531SDave Airlie 			u32 bpp;
1210414c4531SDave Airlie 			u32 mb;
1211414c4531SDave Airlie 
1212272725c7SVille Syrjälä 			if (fb->format->cpp[0] * 8 > 16)
1213414c4531SDave Airlie 				bpp = 32;
1214272725c7SVille Syrjälä 			else if (fb->format->cpp[0] * 8 > 8)
1215414c4531SDave Airlie 				bpp = 16;
1216414c4531SDave Airlie 			else
1217414c4531SDave Airlie 				bpp = 8;
1218414c4531SDave Airlie 
1219414c4531SDave Airlie 			mb = (mode->clock * bpp) / 1000;
1220414c4531SDave Airlie 			if (mb > 3100)
1221414c4531SDave Airlie 				hi_pri_lvl = 0;
1222414c4531SDave Airlie 			else if (mb > 2600)
1223414c4531SDave Airlie 				hi_pri_lvl = 1;
1224414c4531SDave Airlie 			else if (mb > 1900)
1225414c4531SDave Airlie 				hi_pri_lvl = 2;
1226414c4531SDave Airlie 			else if (mb > 1160)
1227414c4531SDave Airlie 				hi_pri_lvl = 3;
1228414c4531SDave Airlie 			else if (mb > 440)
1229414c4531SDave Airlie 				hi_pri_lvl = 4;
1230414c4531SDave Airlie 			else
1231414c4531SDave Airlie 				hi_pri_lvl = 5;
1232414c4531SDave Airlie 
123391f8f105SChristopher Harvey 			WREG8(MGAREG_CRTCEXT_INDEX, 0x06);
123491f8f105SChristopher Harvey 			WREG8(MGAREG_CRTCEXT_DATA, hi_pri_lvl);
1235414c4531SDave Airlie 		} else {
123691f8f105SChristopher Harvey 			WREG8(MGAREG_CRTCEXT_INDEX, 0x06);
1237abbee623SJulia Lemire 			if (mdev->unique_rev_id >= 0x01)
123891f8f105SChristopher Harvey 				WREG8(MGAREG_CRTCEXT_DATA, 0x03);
1239414c4531SDave Airlie 			else
124091f8f105SChristopher Harvey 				WREG8(MGAREG_CRTCEXT_DATA, 0x04);
1241414c4531SDave Airlie 		}
1242414c4531SDave Airlie 	}
1243414c4531SDave Airlie 	return 0;
1244414c4531SDave Airlie }
1245414c4531SDave Airlie 
1246414c4531SDave Airlie #if 0 /* code from mjg to attempt D3 on crtc dpms off - revisit later */
1247414c4531SDave Airlie static int mga_suspend(struct drm_crtc *crtc)
1248414c4531SDave Airlie {
1249414c4531SDave Airlie 	struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
1250414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
1251414c4531SDave Airlie 	struct mga_device *mdev = dev->dev_private;
1252414c4531SDave Airlie 	struct pci_dev *pdev = dev->pdev;
1253414c4531SDave Airlie 	int option;
1254414c4531SDave Airlie 
1255414c4531SDave Airlie 	if (mdev->suspended)
1256414c4531SDave Airlie 		return 0;
1257414c4531SDave Airlie 
1258414c4531SDave Airlie 	WREG_SEQ(1, 0x20);
1259414c4531SDave Airlie 	WREG_ECRT(1, 0x30);
1260414c4531SDave Airlie 	/* Disable the pixel clock */
1261414c4531SDave Airlie 	WREG_DAC(0x1a, 0x05);
1262414c4531SDave Airlie 	/* Power down the DAC */
1263414c4531SDave Airlie 	WREG_DAC(0x1e, 0x18);
1264414c4531SDave Airlie 	/* Power down the pixel PLL */
1265414c4531SDave Airlie 	WREG_DAC(0x1a, 0x0d);
1266414c4531SDave Airlie 
1267414c4531SDave Airlie 	/* Disable PLLs and clocks */
1268414c4531SDave Airlie 	pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
1269414c4531SDave Airlie 	option &= ~(0x1F8024);
1270414c4531SDave Airlie 	pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
1271414c4531SDave Airlie 	pci_set_power_state(pdev, PCI_D3hot);
1272414c4531SDave Airlie 	pci_disable_device(pdev);
1273414c4531SDave Airlie 
1274414c4531SDave Airlie 	mdev->suspended = true;
1275414c4531SDave Airlie 
1276414c4531SDave Airlie 	return 0;
1277414c4531SDave Airlie }
1278414c4531SDave Airlie 
1279414c4531SDave Airlie static int mga_resume(struct drm_crtc *crtc)
1280414c4531SDave Airlie {
1281414c4531SDave Airlie 	struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
1282414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
1283414c4531SDave Airlie 	struct mga_device *mdev = dev->dev_private;
1284414c4531SDave Airlie 	struct pci_dev *pdev = dev->pdev;
1285414c4531SDave Airlie 	int option;
1286414c4531SDave Airlie 
1287414c4531SDave Airlie 	if (!mdev->suspended)
1288414c4531SDave Airlie 		return 0;
1289414c4531SDave Airlie 
1290414c4531SDave Airlie 	pci_set_power_state(pdev, PCI_D0);
1291414c4531SDave Airlie 	pci_enable_device(pdev);
1292414c4531SDave Airlie 
1293414c4531SDave Airlie 	/* Disable sysclk */
1294414c4531SDave Airlie 	pci_read_config_dword(pdev, PCI_MGA_OPTION, &option);
1295414c4531SDave Airlie 	option &= ~(0x4);
1296414c4531SDave Airlie 	pci_write_config_dword(pdev, PCI_MGA_OPTION, option);
1297414c4531SDave Airlie 
1298414c4531SDave Airlie 	mdev->suspended = false;
1299414c4531SDave Airlie 
1300414c4531SDave Airlie 	return 0;
1301414c4531SDave Airlie }
1302414c4531SDave Airlie 
1303414c4531SDave Airlie #endif
1304414c4531SDave Airlie 
1305414c4531SDave Airlie static void mga_crtc_dpms(struct drm_crtc *crtc, int mode)
1306414c4531SDave Airlie {
1307414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
13088d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(dev);
1309414c4531SDave Airlie 	u8 seq1 = 0, crtcext1 = 0;
1310414c4531SDave Airlie 
1311414c4531SDave Airlie 	switch (mode) {
1312414c4531SDave Airlie 	case DRM_MODE_DPMS_ON:
1313414c4531SDave Airlie 		seq1 = 0;
1314414c4531SDave Airlie 		crtcext1 = 0;
1315414c4531SDave Airlie 		mga_crtc_load_lut(crtc);
1316414c4531SDave Airlie 		break;
1317414c4531SDave Airlie 	case DRM_MODE_DPMS_STANDBY:
1318414c4531SDave Airlie 		seq1 = 0x20;
1319414c4531SDave Airlie 		crtcext1 = 0x10;
1320414c4531SDave Airlie 		break;
1321414c4531SDave Airlie 	case DRM_MODE_DPMS_SUSPEND:
1322414c4531SDave Airlie 		seq1 = 0x20;
1323414c4531SDave Airlie 		crtcext1 = 0x20;
1324414c4531SDave Airlie 		break;
1325414c4531SDave Airlie 	case DRM_MODE_DPMS_OFF:
1326414c4531SDave Airlie 		seq1 = 0x20;
1327414c4531SDave Airlie 		crtcext1 = 0x30;
1328414c4531SDave Airlie 		break;
1329414c4531SDave Airlie 	}
1330414c4531SDave Airlie 
1331414c4531SDave Airlie #if 0
1332414c4531SDave Airlie 	if (mode == DRM_MODE_DPMS_OFF) {
1333414c4531SDave Airlie 		mga_suspend(crtc);
1334414c4531SDave Airlie 	}
1335414c4531SDave Airlie #endif
1336414c4531SDave Airlie 	WREG8(MGAREG_SEQ_INDEX, 0x01);
1337414c4531SDave Airlie 	seq1 |= RREG8(MGAREG_SEQ_DATA) & ~0x20;
1338414c4531SDave Airlie 	mga_wait_vsync(mdev);
1339414c4531SDave Airlie 	mga_wait_busy(mdev);
1340414c4531SDave Airlie 	WREG8(MGAREG_SEQ_DATA, seq1);
1341414c4531SDave Airlie 	msleep(20);
1342414c4531SDave Airlie 	WREG8(MGAREG_CRTCEXT_INDEX, 0x01);
1343414c4531SDave Airlie 	crtcext1 |= RREG8(MGAREG_CRTCEXT_DATA) & ~0x30;
1344414c4531SDave Airlie 	WREG8(MGAREG_CRTCEXT_DATA, crtcext1);
1345414c4531SDave Airlie 
1346414c4531SDave Airlie #if 0
1347414c4531SDave Airlie 	if (mode == DRM_MODE_DPMS_ON && mdev->suspended == true) {
1348414c4531SDave Airlie 		mga_resume(crtc);
1349414c4531SDave Airlie 		drm_helper_resume_force_mode(dev);
1350414c4531SDave Airlie 	}
1351414c4531SDave Airlie #endif
1352414c4531SDave Airlie }
1353414c4531SDave Airlie 
1354414c4531SDave Airlie /*
1355414c4531SDave Airlie  * This is called before a mode is programmed. A typical use might be to
1356414c4531SDave Airlie  * enable DPMS during the programming to avoid seeing intermediate stages,
1357414c4531SDave Airlie  * but that's not relevant to us
1358414c4531SDave Airlie  */
1359414c4531SDave Airlie static void mga_crtc_prepare(struct drm_crtc *crtc)
1360414c4531SDave Airlie {
1361414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
13628d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(dev);
1363414c4531SDave Airlie 	u8 tmp;
1364414c4531SDave Airlie 
1365414c4531SDave Airlie 	/*	mga_resume(crtc);*/
1366414c4531SDave Airlie 
1367414c4531SDave Airlie 	WREG8(MGAREG_CRTC_INDEX, 0x11);
1368414c4531SDave Airlie 	tmp = RREG8(MGAREG_CRTC_DATA);
1369414c4531SDave Airlie 	WREG_CRT(0x11, tmp | 0x80);
1370414c4531SDave Airlie 
1371414c4531SDave Airlie 	if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) {
1372414c4531SDave Airlie 		WREG_SEQ(0, 1);
1373414c4531SDave Airlie 		msleep(50);
1374414c4531SDave Airlie 		WREG_SEQ(1, 0x20);
1375414c4531SDave Airlie 		msleep(20);
1376414c4531SDave Airlie 	} else {
1377414c4531SDave Airlie 		WREG8(MGAREG_SEQ_INDEX, 0x1);
1378414c4531SDave Airlie 		tmp = RREG8(MGAREG_SEQ_DATA);
1379414c4531SDave Airlie 
1380414c4531SDave Airlie 		/* start sync reset */
1381414c4531SDave Airlie 		WREG_SEQ(0, 1);
1382414c4531SDave Airlie 		WREG_SEQ(1, tmp | 0x20);
1383414c4531SDave Airlie 	}
1384414c4531SDave Airlie 
13856d857c18SMathieu Larouche 	if (mdev->type == G200_WB || mdev->type == G200_EW3)
1386414c4531SDave Airlie 		mga_g200wb_prepare(crtc);
1387414c4531SDave Airlie 
1388414c4531SDave Airlie 	WREG_CRT(17, 0);
1389414c4531SDave Airlie }
1390414c4531SDave Airlie 
1391414c4531SDave Airlie /*
1392414c4531SDave Airlie  * This is called after a mode is programmed. It should reverse anything done
1393414c4531SDave Airlie  * by the prepare function
1394414c4531SDave Airlie  */
1395414c4531SDave Airlie static void mga_crtc_commit(struct drm_crtc *crtc)
1396414c4531SDave Airlie {
1397414c4531SDave Airlie 	struct drm_device *dev = crtc->dev;
13988d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(dev);
1399d584ff82SJani Nikula 	const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
1400414c4531SDave Airlie 	u8 tmp;
1401414c4531SDave Airlie 
14026d857c18SMathieu Larouche 	if (mdev->type == G200_WB || mdev->type == G200_EW3)
1403414c4531SDave Airlie 		mga_g200wb_commit(crtc);
1404414c4531SDave Airlie 
1405414c4531SDave Airlie 	if (mdev->type == G200_SE_A || mdev->type == G200_SE_B) {
1406414c4531SDave Airlie 		msleep(50);
1407414c4531SDave Airlie 		WREG_SEQ(1, 0x0);
1408414c4531SDave Airlie 		msleep(20);
1409414c4531SDave Airlie 		WREG_SEQ(0, 0x3);
1410414c4531SDave Airlie 	} else {
1411414c4531SDave Airlie 		WREG8(MGAREG_SEQ_INDEX, 0x1);
1412414c4531SDave Airlie 		tmp = RREG8(MGAREG_SEQ_DATA);
1413414c4531SDave Airlie 
1414414c4531SDave Airlie 		tmp &= ~0x20;
1415414c4531SDave Airlie 		WREG_SEQ(0x1, tmp);
1416414c4531SDave Airlie 		WREG_SEQ(0, 3);
1417414c4531SDave Airlie 	}
1418414c4531SDave Airlie 	crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
1419414c4531SDave Airlie }
1420414c4531SDave Airlie 
1421414c4531SDave Airlie /*
1422414c4531SDave Airlie  * The core can pass us a set of gamma values to program. We actually only
1423414c4531SDave Airlie  * use this for 8-bit mode so can't perform smooth fades on deeper modes,
1424414c4531SDave Airlie  * but it's a requirement that we provide the function
1425414c4531SDave Airlie  */
14267ea77283SMaarten Lankhorst static int mga_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green,
14276d124ff8SDaniel Vetter 			      u16 *blue, uint32_t size,
14286d124ff8SDaniel Vetter 			      struct drm_modeset_acquire_ctx *ctx)
1429414c4531SDave Airlie {
1430414c4531SDave Airlie 	mga_crtc_load_lut(crtc);
14317ea77283SMaarten Lankhorst 
14327ea77283SMaarten Lankhorst 	return 0;
1433414c4531SDave Airlie }
1434414c4531SDave Airlie 
1435414c4531SDave Airlie /* Simple cleanup function */
1436414c4531SDave Airlie static void mga_crtc_destroy(struct drm_crtc *crtc)
1437414c4531SDave Airlie {
1438414c4531SDave Airlie 	struct mga_crtc *mga_crtc = to_mga_crtc(crtc);
1439414c4531SDave Airlie 
1440414c4531SDave Airlie 	drm_crtc_cleanup(crtc);
1441414c4531SDave Airlie 	kfree(mga_crtc);
1442414c4531SDave Airlie }
1443414c4531SDave Airlie 
144464c29076SEgbert Eich static void mga_crtc_disable(struct drm_crtc *crtc)
144564c29076SEgbert Eich {
144664c29076SEgbert Eich 	DRM_DEBUG_KMS("\n");
144764c29076SEgbert Eich 	mga_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
1448f4510a27SMatt Roper 	if (crtc->primary->fb) {
14495d177189SThomas Zimmermann 		struct drm_framebuffer *fb = crtc->primary->fb;
14505d177189SThomas Zimmermann 		struct drm_gem_vram_object *gbo =
14515d177189SThomas Zimmermann 			drm_gem_vram_of_gem(fb->obj[0]);
145281da87f6SThomas Zimmermann 		drm_gem_vram_unpin(gbo);
145364c29076SEgbert Eich 	}
1454f4510a27SMatt Roper 	crtc->primary->fb = NULL;
145564c29076SEgbert Eich }
145664c29076SEgbert Eich 
1457414c4531SDave Airlie /* These provide the minimum set of functions required to handle a CRTC */
1458414c4531SDave Airlie static const struct drm_crtc_funcs mga_crtc_funcs = {
1459414c4531SDave Airlie 	.gamma_set = mga_crtc_gamma_set,
1460414c4531SDave Airlie 	.set_config = drm_crtc_helper_set_config,
1461414c4531SDave Airlie 	.destroy = mga_crtc_destroy,
1462414c4531SDave Airlie };
1463414c4531SDave Airlie 
1464414c4531SDave Airlie static const struct drm_crtc_helper_funcs mga_helper_funcs = {
146564c29076SEgbert Eich 	.disable = mga_crtc_disable,
1466414c4531SDave Airlie 	.dpms = mga_crtc_dpms,
1467414c4531SDave Airlie 	.mode_set = mga_crtc_mode_set,
1468414c4531SDave Airlie 	.mode_set_base = mga_crtc_mode_set_base,
1469414c4531SDave Airlie 	.prepare = mga_crtc_prepare,
1470414c4531SDave Airlie 	.commit = mga_crtc_commit,
1471414c4531SDave Airlie };
1472414c4531SDave Airlie 
1473414c4531SDave Airlie /* CRTC setup */
1474f1998fe2SChristopher Harvey static void mga_crtc_init(struct mga_device *mdev)
1475414c4531SDave Airlie {
1476ed5877b6SThomas Zimmermann 	struct drm_device *dev = mdev->dev;
1477414c4531SDave Airlie 	struct mga_crtc *mga_crtc;
1478414c4531SDave Airlie 
1479414c4531SDave Airlie 	mga_crtc = kzalloc(sizeof(struct mga_crtc) +
1480414c4531SDave Airlie 			      (MGAG200FB_CONN_LIMIT * sizeof(struct drm_connector *)),
1481414c4531SDave Airlie 			      GFP_KERNEL);
1482414c4531SDave Airlie 
1483414c4531SDave Airlie 	if (mga_crtc == NULL)
1484414c4531SDave Airlie 		return;
1485414c4531SDave Airlie 
1486ed5877b6SThomas Zimmermann 	drm_crtc_init(dev, &mga_crtc->base, &mga_crtc_funcs);
1487414c4531SDave Airlie 
1488414c4531SDave Airlie 	drm_mode_crtc_set_gamma_size(&mga_crtc->base, MGAG200_LUT_SIZE);
1489414c4531SDave Airlie 
1490414c4531SDave Airlie 	drm_crtc_helper_add(&mga_crtc->base, &mga_helper_funcs);
1491414c4531SDave Airlie }
1492414c4531SDave Airlie 
149381a15b9aSThomas Zimmermann /*
149481a15b9aSThomas Zimmermann  * Connector
149581a15b9aSThomas Zimmermann  */
149681a15b9aSThomas Zimmermann 
1497414c4531SDave Airlie static int mga_vga_get_modes(struct drm_connector *connector)
1498414c4531SDave Airlie {
1499414c4531SDave Airlie 	struct mga_connector *mga_connector = to_mga_connector(connector);
1500414c4531SDave Airlie 	struct edid *edid;
1501414c4531SDave Airlie 	int ret = 0;
1502414c4531SDave Airlie 
1503414c4531SDave Airlie 	edid = drm_get_edid(connector, &mga_connector->i2c->adapter);
1504414c4531SDave Airlie 	if (edid) {
1505c555f023SDaniel Vetter 		drm_connector_update_edid_property(connector, edid);
1506414c4531SDave Airlie 		ret = drm_add_edid_modes(connector, edid);
1507414c4531SDave Airlie 		kfree(edid);
1508414c4531SDave Airlie 	}
1509414c4531SDave Airlie 	return ret;
1510414c4531SDave Airlie }
1511414c4531SDave Airlie 
1512abbee623SJulia Lemire static uint32_t mga_vga_calculate_mode_bandwidth(struct drm_display_mode *mode,
1513abbee623SJulia Lemire 							int bits_per_pixel)
1514abbee623SJulia Lemire {
1515abbee623SJulia Lemire 	uint32_t total_area, divisor;
1516c24ca5beSNicolas Pitre 	uint64_t active_area, pixels_per_second, bandwidth;
1517abbee623SJulia Lemire 	uint64_t bytes_per_pixel = (bits_per_pixel + 7) / 8;
1518abbee623SJulia Lemire 
1519abbee623SJulia Lemire 	divisor = 1024;
1520abbee623SJulia Lemire 
1521abbee623SJulia Lemire 	if (!mode->htotal || !mode->vtotal || !mode->clock)
1522abbee623SJulia Lemire 		return 0;
1523abbee623SJulia Lemire 
1524abbee623SJulia Lemire 	active_area = mode->hdisplay * mode->vdisplay;
1525abbee623SJulia Lemire 	total_area = mode->htotal * mode->vtotal;
1526abbee623SJulia Lemire 
1527abbee623SJulia Lemire 	pixels_per_second = active_area * mode->clock * 1000;
1528abbee623SJulia Lemire 	do_div(pixels_per_second, total_area);
1529abbee623SJulia Lemire 
1530abbee623SJulia Lemire 	bandwidth = pixels_per_second * bytes_per_pixel * 100;
1531abbee623SJulia Lemire 	do_div(bandwidth, divisor);
1532abbee623SJulia Lemire 
1533abbee623SJulia Lemire 	return (uint32_t)(bandwidth);
1534abbee623SJulia Lemire }
1535abbee623SJulia Lemire 
1536abbee623SJulia Lemire #define MODE_BANDWIDTH	MODE_BAD
1537abbee623SJulia Lemire 
1538c69e52deSLuc Van Oostenryck static enum drm_mode_status mga_vga_mode_valid(struct drm_connector *connector,
1539414c4531SDave Airlie 				 struct drm_display_mode *mode)
1540414c4531SDave Airlie {
15410ba53171SChristopher Harvey 	struct drm_device *dev = connector->dev;
15428d8ff2a9SThomas Zimmermann 	struct mga_device *mdev = to_mga_device(dev);
15430ba53171SChristopher Harvey 	int bpp = 32;
15440ba53171SChristopher Harvey 
1545abbee623SJulia Lemire 	if (IS_G200_SE(mdev)) {
1546abbee623SJulia Lemire 		if (mdev->unique_rev_id == 0x01) {
1547abbee623SJulia Lemire 			if (mode->hdisplay > 1600)
1548abbee623SJulia Lemire 				return MODE_VIRTUAL_X;
1549abbee623SJulia Lemire 			if (mode->vdisplay > 1200)
1550abbee623SJulia Lemire 				return MODE_VIRTUAL_Y;
1551abbee623SJulia Lemire 			if (mga_vga_calculate_mode_bandwidth(mode, bpp)
1552abbee623SJulia Lemire 				> (24400 * 1024))
1553abbee623SJulia Lemire 				return MODE_BANDWIDTH;
1554e829d7efSMathieu Larouche 		} else if (mdev->unique_rev_id == 0x02) {
1555abbee623SJulia Lemire 			if (mode->hdisplay > 1920)
1556abbee623SJulia Lemire 				return MODE_VIRTUAL_X;
1557abbee623SJulia Lemire 			if (mode->vdisplay > 1200)
1558abbee623SJulia Lemire 				return MODE_VIRTUAL_Y;
1559abbee623SJulia Lemire 			if (mga_vga_calculate_mode_bandwidth(mode, bpp)
1560abbee623SJulia Lemire 				> (30100 * 1024))
1561abbee623SJulia Lemire 				return MODE_BANDWIDTH;
15620cbb7381SMathieu Larouche 		} else {
15630cbb7381SMathieu Larouche 			if (mga_vga_calculate_mode_bandwidth(mode, bpp)
15640cbb7381SMathieu Larouche 				> (55000 * 1024))
15650cbb7381SMathieu Larouche 				return MODE_BANDWIDTH;
1566abbee623SJulia Lemire 		}
1567abbee623SJulia Lemire 	} else if (mdev->type == G200_WB) {
1568abbee623SJulia Lemire 		if (mode->hdisplay > 1280)
1569abbee623SJulia Lemire 			return MODE_VIRTUAL_X;
1570abbee623SJulia Lemire 		if (mode->vdisplay > 1024)
1571abbee623SJulia Lemire 			return MODE_VIRTUAL_Y;
15729eb8d7a9SDan Carpenter 		if (mga_vga_calculate_mode_bandwidth(mode, bpp) >
15739eb8d7a9SDan Carpenter 		    (31877 * 1024))
1574abbee623SJulia Lemire 			return MODE_BANDWIDTH;
1575abbee623SJulia Lemire 	} else if (mdev->type == G200_EV &&
1576abbee623SJulia Lemire 		(mga_vga_calculate_mode_bandwidth(mode, bpp)
1577abbee623SJulia Lemire 			> (32700 * 1024))) {
1578abbee623SJulia Lemire 		return MODE_BANDWIDTH;
1579ec22b4aaSDave Airlie 	} else if (mdev->type == G200_EH &&
1580abbee623SJulia Lemire 		(mga_vga_calculate_mode_bandwidth(mode, bpp)
1581abbee623SJulia Lemire 			> (37500 * 1024))) {
1582abbee623SJulia Lemire 		return MODE_BANDWIDTH;
1583ec22b4aaSDave Airlie 	} else if (mdev->type == G200_ER &&
1584abbee623SJulia Lemire 		(mga_vga_calculate_mode_bandwidth(mode,
1585abbee623SJulia Lemire 			bpp) > (55000 * 1024))) {
1586abbee623SJulia Lemire 		return MODE_BANDWIDTH;
1587abbee623SJulia Lemire 	}
1588414c4531SDave Airlie 
158925161084SAdam Jackson 	if ((mode->hdisplay % 8) != 0 || (mode->hsync_start % 8) != 0 ||
159025161084SAdam Jackson 	    (mode->hsync_end % 8) != 0 || (mode->htotal % 8) != 0) {
159125161084SAdam Jackson 		return MODE_H_ILLEGAL;
159225161084SAdam Jackson 	}
159325161084SAdam Jackson 
1594414c4531SDave Airlie 	if (mode->crtc_hdisplay > 2048 || mode->crtc_hsync_start > 4096 ||
1595414c4531SDave Airlie 	    mode->crtc_hsync_end > 4096 || mode->crtc_htotal > 4096 ||
1596414c4531SDave Airlie 	    mode->crtc_vdisplay > 2048 || mode->crtc_vsync_start > 4096 ||
1597414c4531SDave Airlie 	    mode->crtc_vsync_end > 4096 || mode->crtc_vtotal > 4096) {
1598414c4531SDave Airlie 		return MODE_BAD;
1599414c4531SDave Airlie 	}
1600414c4531SDave Airlie 
16010ba53171SChristopher Harvey 	/* Validate the mode input by the user */
1602eaf99c74SChris Wilson 	if (connector->cmdline_mode.specified) {
1603eaf99c74SChris Wilson 		if (connector->cmdline_mode.bpp_specified)
1604eaf99c74SChris Wilson 			bpp = connector->cmdline_mode.bpp;
16050ba53171SChristopher Harvey 	}
16060ba53171SChristopher Harvey 
16072c51a660SThomas Zimmermann 	if ((mode->hdisplay * mode->vdisplay * (bpp/8)) > mdev->vram_fb_available) {
1608eaf99c74SChris Wilson 		if (connector->cmdline_mode.specified)
1609eaf99c74SChris Wilson 			connector->cmdline_mode.specified = false;
16100ba53171SChristopher Harvey 		return MODE_BAD;
16110ba53171SChristopher Harvey 	}
16120ba53171SChristopher Harvey 
1613414c4531SDave Airlie 	return MODE_OK;
1614414c4531SDave Airlie }
1615414c4531SDave Airlie 
1616414c4531SDave Airlie static void mga_connector_destroy(struct drm_connector *connector)
1617414c4531SDave Airlie {
1618414c4531SDave Airlie 	struct mga_connector *mga_connector = to_mga_connector(connector);
1619414c4531SDave Airlie 	mgag200_i2c_destroy(mga_connector->i2c);
1620414c4531SDave Airlie 	drm_connector_cleanup(connector);
1621414c4531SDave Airlie }
1622414c4531SDave Airlie 
162371cb7495SVille Syrjälä static const struct drm_connector_helper_funcs mga_vga_connector_helper_funcs = {
1624414c4531SDave Airlie 	.get_modes = mga_vga_get_modes,
1625414c4531SDave Airlie 	.mode_valid = mga_vga_mode_valid,
1626414c4531SDave Airlie };
1627414c4531SDave Airlie 
162871cb7495SVille Syrjälä static const struct drm_connector_funcs mga_vga_connector_funcs = {
1629414c4531SDave Airlie 	.dpms = drm_helper_connector_dpms,
1630414c4531SDave Airlie 	.fill_modes = drm_helper_probe_single_connector_modes,
1631414c4531SDave Airlie 	.destroy = mga_connector_destroy,
1632414c4531SDave Airlie };
1633414c4531SDave Airlie 
163481a15b9aSThomas Zimmermann static int mgag200_vga_connector_init(struct mga_device *mdev)
1635414c4531SDave Airlie {
163681a15b9aSThomas Zimmermann 	struct drm_device *dev = mdev->dev;
163781a15b9aSThomas Zimmermann 	struct mga_connector *mconnector = &mdev->connector;
163881a15b9aSThomas Zimmermann 	struct drm_connector *connector = &mconnector->base;
163981a15b9aSThomas Zimmermann 	struct mga_i2c_chan *i2c;
164081a15b9aSThomas Zimmermann 	int ret;
1641414c4531SDave Airlie 
164281a15b9aSThomas Zimmermann 	i2c = mgag200_i2c_create(dev);
164381a15b9aSThomas Zimmermann 	if (!i2c)
164481a15b9aSThomas Zimmermann 		drm_warn(dev, "failed to add DDC bus\n");
1645414c4531SDave Airlie 
164681a15b9aSThomas Zimmermann 	ret = drm_connector_init_with_ddc(dev, connector,
16479572ae17SAndrzej Pietrasiewicz 					  &mga_vga_connector_funcs,
16489572ae17SAndrzej Pietrasiewicz 					  DRM_MODE_CONNECTOR_VGA,
164981a15b9aSThomas Zimmermann 					  &i2c->adapter);
165081a15b9aSThomas Zimmermann 	if (ret)
165181a15b9aSThomas Zimmermann 		goto err_mgag200_i2c_destroy;
1652414c4531SDave Airlie 	drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
1653414c4531SDave Airlie 
165481a15b9aSThomas Zimmermann 	mconnector->i2c = i2c;
16553d5a1c5eSEgbert Eich 
165681a15b9aSThomas Zimmermann 	return 0;
165781a15b9aSThomas Zimmermann 
165881a15b9aSThomas Zimmermann err_mgag200_i2c_destroy:
165981a15b9aSThomas Zimmermann 	mgag200_i2c_destroy(i2c);
166081a15b9aSThomas Zimmermann 	return ret;
1661414c4531SDave Airlie }
1662414c4531SDave Airlie 
16635635b7cfSThomas Zimmermann static const struct drm_mode_config_funcs mgag200_mode_config_funcs = {
16645635b7cfSThomas Zimmermann 	.fb_create = drm_gem_fb_create
16655635b7cfSThomas Zimmermann };
16665635b7cfSThomas Zimmermann 
16675635b7cfSThomas Zimmermann static unsigned int mgag200_preferred_depth(struct mga_device *mdev)
16685635b7cfSThomas Zimmermann {
16695635b7cfSThomas Zimmermann 	if (IS_G200_SE(mdev) && mdev->vram_fb_available < (2048*1024))
16705635b7cfSThomas Zimmermann 		return 16;
16715635b7cfSThomas Zimmermann 	else
16725635b7cfSThomas Zimmermann 		return 32;
16735635b7cfSThomas Zimmermann }
1674414c4531SDave Airlie 
1675414c4531SDave Airlie int mgag200_modeset_init(struct mga_device *mdev)
1676414c4531SDave Airlie {
1677ed5877b6SThomas Zimmermann 	struct drm_device *dev = mdev->dev;
167803e44ad1SThomas Zimmermann 	struct drm_encoder *encoder = &mdev->encoder;
167981a15b9aSThomas Zimmermann 	struct drm_connector *connector = &mdev->connector.base;
168003e44ad1SThomas Zimmermann 	int ret;
1681414c4531SDave Airlie 
16825635b7cfSThomas Zimmermann 	mdev->bpp_shifts[0] = 0;
16835635b7cfSThomas Zimmermann 	mdev->bpp_shifts[1] = 1;
16845635b7cfSThomas Zimmermann 	mdev->bpp_shifts[2] = 0;
16855635b7cfSThomas Zimmermann 	mdev->bpp_shifts[3] = 2;
16865635b7cfSThomas Zimmermann 
16875635b7cfSThomas Zimmermann 	ret = drmm_mode_config_init(dev);
16885635b7cfSThomas Zimmermann 	if (ret) {
16895635b7cfSThomas Zimmermann 		drm_err(dev, "drmm_mode_config_init() failed, error %d\n",
16905635b7cfSThomas Zimmermann 			ret);
16915635b7cfSThomas Zimmermann 		return ret;
16925635b7cfSThomas Zimmermann 	}
16935635b7cfSThomas Zimmermann 
1694ed5877b6SThomas Zimmermann 	dev->mode_config.max_width = MGAG200_MAX_FB_WIDTH;
1695ed5877b6SThomas Zimmermann 	dev->mode_config.max_height = MGAG200_MAX_FB_HEIGHT;
1696414c4531SDave Airlie 
16975635b7cfSThomas Zimmermann 	dev->mode_config.preferred_depth = mgag200_preferred_depth(mdev);
16985635b7cfSThomas Zimmermann 	dev->mode_config.prefer_shadow = 1;
16995635b7cfSThomas Zimmermann 
1700ed5877b6SThomas Zimmermann 	dev->mode_config.fb_base = mdev->mc.vram_base;
1701414c4531SDave Airlie 
17025635b7cfSThomas Zimmermann 	dev->mode_config.funcs = &mgag200_mode_config_funcs;
17035635b7cfSThomas Zimmermann 
1704f1998fe2SChristopher Harvey 	mga_crtc_init(mdev);
1705414c4531SDave Airlie 
1706ed5877b6SThomas Zimmermann 	ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_DAC);
170703e44ad1SThomas Zimmermann 	if (ret) {
1708ed5877b6SThomas Zimmermann 		drm_err(dev,
170903e44ad1SThomas Zimmermann 			"drm_simple_encoder_init() failed, error %d\n",
171003e44ad1SThomas Zimmermann 			ret);
171103e44ad1SThomas Zimmermann 		return ret;
1712414c4531SDave Airlie 	}
171303e44ad1SThomas Zimmermann 	encoder->possible_crtcs = 0x1;
1714414c4531SDave Airlie 
171581a15b9aSThomas Zimmermann 	ret = mgag200_vga_connector_init(mdev);
171681a15b9aSThomas Zimmermann 	if (ret) {
171781a15b9aSThomas Zimmermann 		drm_err(dev,
171881a15b9aSThomas Zimmermann 			"mgag200_vga_connector_init() failed, error %d\n",
171981a15b9aSThomas Zimmermann 			ret);
172081a15b9aSThomas Zimmermann 		return ret;
1721414c4531SDave Airlie 	}
1722414c4531SDave Airlie 
1723cde4c44dSDaniel Vetter 	drm_connector_attach_encoder(connector, encoder);
1724414c4531SDave Airlie 
1725414c4531SDave Airlie 	return 0;
1726414c4531SDave Airlie }
1727