ffb_dac.c revision dbbd9e4b
1dbbd9e4bSmacallan/*
2dbbd9e4bSmacallan * Acceleration for the Creator and Creator3D framebuffer - DAC programming.
3dbbd9e4bSmacallan *
4dbbd9e4bSmacallan * Copyright (C) 2000 David S. Miller (davem@redhat.com)
5dbbd9e4bSmacallan *
6dbbd9e4bSmacallan * Permission is hereby granted, free of charge, to any person obtaining a copy
7dbbd9e4bSmacallan * of this software and associated documentation files (the "Software"), to deal
8dbbd9e4bSmacallan * in the Software without restriction, including without limitation the rights
9dbbd9e4bSmacallan * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10dbbd9e4bSmacallan * copies of the Software, and to permit persons to whom the Software is
11dbbd9e4bSmacallan * furnished to do so, subject to the following conditions:
12dbbd9e4bSmacallan *
13dbbd9e4bSmacallan * The above copyright notice and this permission notice shall be included in
14dbbd9e4bSmacallan * all copies or substantial portions of the Software.
15dbbd9e4bSmacallan *
16dbbd9e4bSmacallan * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17dbbd9e4bSmacallan * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18dbbd9e4bSmacallan * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19dbbd9e4bSmacallan * DAVID MILLER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
20dbbd9e4bSmacallan * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21dbbd9e4bSmacallan * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22dbbd9e4bSmacallan *
23dbbd9e4bSmacallan */
24dbbd9e4bSmacallan/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/sunffb/ffb_dac.c,v 1.3tsi Exp $ */
25dbbd9e4bSmacallan
26dbbd9e4bSmacallan#ifdef HAVE_CONFIG_H
27dbbd9e4bSmacallan#include "config.h"
28dbbd9e4bSmacallan#endif
29dbbd9e4bSmacallan
30dbbd9e4bSmacallan#include "ffb.h"
31dbbd9e4bSmacallan#include "ffb_rcache.h"
32dbbd9e4bSmacallan#include "ffb_fifo.h"
33dbbd9e4bSmacallan
34dbbd9e4bSmacallan#include "xf86.h"
35dbbd9e4bSmacallan#include "xf86_OSproc.h"
36dbbd9e4bSmacallan
37dbbd9e4bSmacallan#include "xf86DDC.h"
38dbbd9e4bSmacallan
39dbbd9e4bSmacallan/*
40dbbd9e4bSmacallan * Used for stabilize time after playing with power management on the display
41dbbd9e4bSmacallan */
42dbbd9e4bSmacallan
43dbbd9e4bSmacallan#ifndef DPMS_SPIN_COUNT
44dbbd9e4bSmacallan#define DPMS_SPIN_COUNT 100
45dbbd9e4bSmacallan#endif  /* DPMS_SPIN_COUNT */
46dbbd9e4bSmacallan
47dbbd9e4bSmacallan/* Cursor programming */
48dbbd9e4bSmacallan
49dbbd9e4bSmacallanvoid
50dbbd9e4bSmacallanFFBDacLoadCursorPos(FFBPtr pFfb, int x, int y)
51dbbd9e4bSmacallan{
52dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
53dbbd9e4bSmacallan	int posval;
54dbbd9e4bSmacallan
55dbbd9e4bSmacallan	posval = ((y & 0xffff) << 16) | (x & 0xffff);
56dbbd9e4bSmacallan	posval &= (FFBDAC_CUR_POS_Y_SIGN |
57dbbd9e4bSmacallan		   FFBDAC_CUR_POS_Y |
58dbbd9e4bSmacallan		   FFBDAC_CUR_POS_X_SIGN |
59dbbd9e4bSmacallan		   FFBDAC_CUR_POS_X);
60dbbd9e4bSmacallan
61dbbd9e4bSmacallan	DACCUR_WRITE(dac, FFBDAC_CUR_POS, posval);
62dbbd9e4bSmacallan}
63dbbd9e4bSmacallan
64dbbd9e4bSmacallanvoid
65dbbd9e4bSmacallanFFBDacLoadCursorColor(FFBPtr pFfb, int fg, int bg)
66dbbd9e4bSmacallan{
67dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
68dbbd9e4bSmacallan
69dbbd9e4bSmacallan	dac->cur = FFBDAC_CUR_COLOR1;
70dbbd9e4bSmacallan	dac->curdata = bg;
71dbbd9e4bSmacallan	dac->curdata = fg;
72dbbd9e4bSmacallan}
73dbbd9e4bSmacallan
74dbbd9e4bSmacallanvoid
75dbbd9e4bSmacallanFFBDacCursorEnableDisable(FFBPtr pFfb, int enable)
76dbbd9e4bSmacallan{
77dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
78dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
79dbbd9e4bSmacallan	int val;
80dbbd9e4bSmacallan
81dbbd9e4bSmacallan	val = 0;
82dbbd9e4bSmacallan	if (!enable)
83dbbd9e4bSmacallan		val = (FFBDAC_CUR_CTRL_P0 | FFBDAC_CUR_CTRL_P1);
84dbbd9e4bSmacallan
85dbbd9e4bSmacallan	/* PAC1 ramdacs with manufacturing revision less than
86dbbd9e4bSmacallan	 * '3' invert these control bits, wheee...
87dbbd9e4bSmacallan	 */
88dbbd9e4bSmacallan	if (p->flags & FFB_DAC_ICURCTL)
89dbbd9e4bSmacallan		val ^= (FFBDAC_CUR_CTRL_P0 | FFBDAC_CUR_CTRL_P1);
90dbbd9e4bSmacallan
91dbbd9e4bSmacallan	DACCUR_WRITE(dac, FFBDAC_CUR_CTRL, val);
92dbbd9e4bSmacallan}
93dbbd9e4bSmacallan
94dbbd9e4bSmacallanvoid
95dbbd9e4bSmacallanFFBDacCursorLoadBitmap(FFBPtr pFfb, int xshift, int yshift, unsigned int *bitmap)
96dbbd9e4bSmacallan{
97dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
98dbbd9e4bSmacallan	int i, j;
99dbbd9e4bSmacallan
100dbbd9e4bSmacallan	dac->cur = FFBDAC_CUR_BITMAP_P0;
101dbbd9e4bSmacallan	for (j = 0; j < 2; j++) {
102dbbd9e4bSmacallan		bitmap += yshift * 2;
103dbbd9e4bSmacallan		if (!xshift) {
104dbbd9e4bSmacallan			for (i = yshift * 2; i < 128; i++)
105dbbd9e4bSmacallan				dac->curdata = *bitmap++;
106dbbd9e4bSmacallan		} else if (xshift < 32) {
107dbbd9e4bSmacallan			for (i = yshift; i < 64; i++, bitmap += 2) {
108dbbd9e4bSmacallan				dac->curdata = (bitmap[0] << xshift) |
109dbbd9e4bSmacallan					(bitmap[1] >> (32 - xshift));
110dbbd9e4bSmacallan				dac->curdata = bitmap[1] << xshift;
111dbbd9e4bSmacallan			}
112dbbd9e4bSmacallan		} else {
113dbbd9e4bSmacallan			for (i = yshift; i < 64; i++, bitmap += 2) {
114dbbd9e4bSmacallan				dac->curdata = bitmap[1] << (xshift - 32);
115dbbd9e4bSmacallan				dac->curdata = 0;
116dbbd9e4bSmacallan			}
117dbbd9e4bSmacallan		}
118dbbd9e4bSmacallan
119dbbd9e4bSmacallan		for (i = 0; i < yshift * 2; i++)
120dbbd9e4bSmacallan			dac->curdata = 0;
121dbbd9e4bSmacallan	}
122dbbd9e4bSmacallan}
123dbbd9e4bSmacallan
124dbbd9e4bSmacallan/* Config space programming */
125dbbd9e4bSmacallan
126dbbd9e4bSmacallan/* XF86 LoadPalette callback. */
127dbbd9e4bSmacallan
128dbbd9e4bSmacallanvoid
129dbbd9e4bSmacallanFFBDacLoadPalette(ScrnInfoPtr pScrn, int ncolors, int *indices, LOCO *colors, VisualPtr pVisual)
130dbbd9e4bSmacallan{
131dbbd9e4bSmacallan	FFBPtr pFfb = GET_FFB_FROM_SCRN(pScrn);
132dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
133dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
134dbbd9e4bSmacallan	unsigned int *cluts;
135dbbd9e4bSmacallan	int i, index, palette;
136dbbd9e4bSmacallan
137dbbd9e4bSmacallan	if ((pVisual->nplanes != 8 && pVisual->class != DirectColor) ||
138dbbd9e4bSmacallan	    (pVisual->nplanes == 8 && pVisual->class == StaticGray))
139dbbd9e4bSmacallan		return;
140dbbd9e4bSmacallan
141dbbd9e4bSmacallan	palette = 0;
142dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC2) {
143dbbd9e4bSmacallan		if (pVisual->class == PseudoColor)
144dbbd9e4bSmacallan			palette = 0;
145dbbd9e4bSmacallan		if (pVisual->class == GrayScale)
146dbbd9e4bSmacallan			palette = 1;
147dbbd9e4bSmacallan		if (pVisual->class == DirectColor)
148dbbd9e4bSmacallan			palette = 2;
149dbbd9e4bSmacallan	}
150dbbd9e4bSmacallan
151dbbd9e4bSmacallan	cluts = &p->x_dac_state.clut[256 * palette];
152dbbd9e4bSmacallan	for (i = 0; i < ncolors; i++) {
153dbbd9e4bSmacallan		unsigned int regval;
154dbbd9e4bSmacallan
155dbbd9e4bSmacallan		index = indices[i];
156dbbd9e4bSmacallan		if (pVisual->class == GrayScale) {
157dbbd9e4bSmacallan			regval = cluts[index] =
158dbbd9e4bSmacallan				((colors[index].red << FFBDAC_COLOR_RED_SHFT) |
159dbbd9e4bSmacallan				 (colors[index].red << FFBDAC_COLOR_GREEN_SHFT) |
160dbbd9e4bSmacallan				 (colors[index].red << FFBDAC_COLOR_BLUE_SHFT));
161dbbd9e4bSmacallan		} else {
162dbbd9e4bSmacallan			regval = cluts[index] =
163dbbd9e4bSmacallan				((colors[index].red   << FFBDAC_COLOR_RED_SHFT) |
164dbbd9e4bSmacallan				 (colors[index].green << FFBDAC_COLOR_GREEN_SHFT) |
165dbbd9e4bSmacallan				 (colors[index].blue  << FFBDAC_COLOR_BLUE_SHFT));
166dbbd9e4bSmacallan		}
167dbbd9e4bSmacallan
168dbbd9e4bSmacallan		FFBLOG(("FFBDacLoadPalette: visclass(%d) index(%d) val[%08x]\n",
169dbbd9e4bSmacallan			pVisual->class, index, regval));
170dbbd9e4bSmacallan
171dbbd9e4bSmacallan		/* Now update the hardware copy. */
172dbbd9e4bSmacallan		dac->cfg = FFBDAC_CFG_CLUP(palette) + index;
173dbbd9e4bSmacallan		dac->cfgdata = regval;
174dbbd9e4bSmacallan	}
175dbbd9e4bSmacallan}
176dbbd9e4bSmacallan
177dbbd9e4bSmacallan/* WARNING: Very dangerous function, use with extreme care. */
178dbbd9e4bSmacallanstatic void
179dbbd9e4bSmacallandac_stop(FFBPtr pFfb)
180dbbd9e4bSmacallan{
181dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
182dbbd9e4bSmacallan	unsigned int tgctrl;
183dbbd9e4bSmacallan
184dbbd9e4bSmacallan	tgctrl = DACCFG_READ(dac, FFBDAC_CFG_TGEN);
185dbbd9e4bSmacallan	if (tgctrl & FFBDAC_CFG_TGEN_TGE) {
186dbbd9e4bSmacallan		long limit = 1000000;
187dbbd9e4bSmacallan
188dbbd9e4bSmacallan		/* We try to shut off the timing generation
189dbbd9e4bSmacallan		 * precisely at the beginning of a vertical
190dbbd9e4bSmacallan		 * retrace.  This is really just to make it
191dbbd9e4bSmacallan		 * look nice, it's not a functional necessity.
192dbbd9e4bSmacallan		 *
193dbbd9e4bSmacallan		 * The limit is so that malfunctioning hardware
194dbbd9e4bSmacallan		 * does not end up hanging the server.
195dbbd9e4bSmacallan		 */
196dbbd9e4bSmacallan		while (limit--) {
197dbbd9e4bSmacallan			unsigned int vctr = DACCFG_READ(dac, FFBDAC_CFG_TGVC);
198dbbd9e4bSmacallan
199dbbd9e4bSmacallan			if (vctr == 0)
200dbbd9e4bSmacallan				break;
201dbbd9e4bSmacallan		}
202dbbd9e4bSmacallan
203dbbd9e4bSmacallan		DACCFG_WRITE(dac, FFBDAC_CFG_TGEN, 0);
204dbbd9e4bSmacallan	}
205dbbd9e4bSmacallan}
206dbbd9e4bSmacallan
207dbbd9e4bSmacallan/* This is made slightly complex because the ordering matters
208dbbd9e4bSmacallan * between several operations.  We have to stop the DAC while
209dbbd9e4bSmacallan * restoring the timing registers so that some intermediate
210dbbd9e4bSmacallan * state does not emit wild retrace signals to the monitor.
211dbbd9e4bSmacallan *
212dbbd9e4bSmacallan * Another further complication is that we need to mess with
213dbbd9e4bSmacallan * some portions of the FFB framebuffer config registers to
214dbbd9e4bSmacallan * do this all properly.
215dbbd9e4bSmacallan */
216dbbd9e4bSmacallanstatic void
217dbbd9e4bSmacallandac_state_restore(FFBPtr pFfb, ffb_dac_hwstate_t *state)
218dbbd9e4bSmacallan{
219dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
220dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
221dbbd9e4bSmacallan	ffb_fbcPtr ffb = pFfb->regs;
222dbbd9e4bSmacallan	int i, nluts;
223dbbd9e4bSmacallan
224dbbd9e4bSmacallan	/* Step 1: Shut off all pixel timing generation. */
225dbbd9e4bSmacallan	dac_stop(pFfb);
226dbbd9e4bSmacallan	ffb->fbcfg0 = 0;
227dbbd9e4bSmacallan
228dbbd9e4bSmacallan	/* Step 2: Restore timing settings. */
229dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_VBNP, state->vbnp);
230dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_VBAP, state->vbap);
231dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_VSNP, state->vsnp);
232dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_VSAP, state->vsap);
233dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HSNP, state->hsnp);
234dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HBNP, state->hbnp);
235dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HBAP, state->hbap);
236dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HSYNCNP, state->hsyncnp);
237dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HSYNCAP, state->hsyncap);
238dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HSCENNP, state->hscennp);
239dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_HSCENAP, state->hscenap);
240dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_EPNP, state->epnp);
241dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_EINP, state->einp);
242dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_EIAP, state->eiap);
243dbbd9e4bSmacallan
244dbbd9e4bSmacallan	/* Step 3: Restore rest of DAC hw state. */
245dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_PPLLCTRL, state->ppllctrl);
246dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_GPLLCTRL, state->gpllctrl);
247dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_PFCTRL, state->pfctrl);
248dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_UCTRL, state->uctrl);
249dbbd9e4bSmacallan
250dbbd9e4bSmacallan	nluts = (p->flags & FFB_DAC_PAC1) ? 256 : (4 * 256);
251dbbd9e4bSmacallan	dac->cfg = FFBDAC_CFG_CLUP_BASE;
252dbbd9e4bSmacallan	for (i = 0; i < nluts; i++)
253dbbd9e4bSmacallan		dac->cfgdata = state->clut[i];
254dbbd9e4bSmacallan
255dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC2) {
256dbbd9e4bSmacallan		dac->cfg = FFBDAC_PAC2_AOVWLUT0;
257dbbd9e4bSmacallan		for (i = 0; i < 4; i++)
258dbbd9e4bSmacallan			dac->cfgdata = state->ovluts[i];
259dbbd9e4bSmacallan	}
260dbbd9e4bSmacallan
261dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_WTCTRL, state->wtctrl);
262dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_TMCTRL, state->tmctrl);
263dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_TCOLORKEY, state->tcolorkey);
264dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC2)
265dbbd9e4bSmacallan		DACCFG_WRITE(dac, FFBDAC_CFG_WAMASK, state->wamask);
266dbbd9e4bSmacallan
267dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC1) {
268dbbd9e4bSmacallan		dac->cfg = FFBDAC_PAC1_APWLUT_BASE;
269dbbd9e4bSmacallan		for (i = 0; i < 32; i++)
270dbbd9e4bSmacallan			dac->cfgdata = state->pwluts[i];
271dbbd9e4bSmacallan	} else {
272dbbd9e4bSmacallan		dac->cfg = FFBDAC_PAC2_APWLUT_BASE;
273dbbd9e4bSmacallan		for (i = 0; i < 64; i++)
274dbbd9e4bSmacallan			dac->cfgdata = state->pwluts[i];
275dbbd9e4bSmacallan	}
276dbbd9e4bSmacallan
277dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_DACCTRL, state->dacctrl);
278dbbd9e4bSmacallan
279dbbd9e4bSmacallan	/* Step 4: Restore FFB framebuffer config state. */
280dbbd9e4bSmacallan	if (pFfb->ffb_type == ffb2_vertical_plus ||
281dbbd9e4bSmacallan	    pFfb->ffb_type == ffb2_horizontal_plus ||
282dbbd9e4bSmacallan	    pFfb->ffb_type == afb_m3 ||
283dbbd9e4bSmacallan	    pFfb->ffb_type == afb_m6)
284dbbd9e4bSmacallan		ffb->passin = p->ffb_passin_ctrl;
285dbbd9e4bSmacallan	ffb->fbcfg0 = p->ffbcfg0;
286dbbd9e4bSmacallan	ffb->fbcfg2 = p->ffbcfg2;
287dbbd9e4bSmacallan
288dbbd9e4bSmacallan	/* Step 5: Restore the timing generator control reg. */
289dbbd9e4bSmacallan	DACCFG_WRITE(dac, FFBDAC_CFG_TGEN, state->tgen);
290dbbd9e4bSmacallan
291dbbd9e4bSmacallan	/* Step 6: Pause for a bit. */
292dbbd9e4bSmacallan	for (i = 0; i < 100; i++)
293dbbd9e4bSmacallan		(void) DACCFG_READ(dac, FFBDAC_CFG_TGVC);
294dbbd9e4bSmacallan}
295dbbd9e4bSmacallan
296dbbd9e4bSmacallanstatic void
297dbbd9e4bSmacallandac_state_save(FFBPtr pFfb, ffb_dac_hwstate_t *state)
298dbbd9e4bSmacallan{
299dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
300dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
301dbbd9e4bSmacallan	int i, nluts;
302dbbd9e4bSmacallan
303dbbd9e4bSmacallan	state->ppllctrl = DACCFG_READ(dac, FFBDAC_CFG_PPLLCTRL);
304dbbd9e4bSmacallan	state->gpllctrl = DACCFG_READ(dac, FFBDAC_CFG_GPLLCTRL);
305dbbd9e4bSmacallan	state->pfctrl   = DACCFG_READ(dac, FFBDAC_CFG_PFCTRL);
306dbbd9e4bSmacallan	state->uctrl    = DACCFG_READ(dac, FFBDAC_CFG_UCTRL);
307dbbd9e4bSmacallan
308dbbd9e4bSmacallan	nluts = (p->flags & FFB_DAC_PAC1) ? 256 : (4 * 256);
309dbbd9e4bSmacallan	dac->cfg = FFBDAC_CFG_CLUP_BASE;
310dbbd9e4bSmacallan	for (i = 0; i < nluts; i++)
311dbbd9e4bSmacallan		state->clut[i] = dac->cfgdata;
312dbbd9e4bSmacallan
313dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC2) {
314dbbd9e4bSmacallan		dac->cfg = FFBDAC_PAC2_AOVWLUT0;
315dbbd9e4bSmacallan		for (i = 0; i < 4; i++)
316dbbd9e4bSmacallan			state->ovluts[i] = dac->cfgdata;
317dbbd9e4bSmacallan	}
318dbbd9e4bSmacallan
319dbbd9e4bSmacallan	state->wtctrl    = DACCFG_READ(dac, FFBDAC_CFG_WTCTRL);
320dbbd9e4bSmacallan	state->tmctrl    = DACCFG_READ(dac, FFBDAC_CFG_TMCTRL);
321dbbd9e4bSmacallan	state->tcolorkey = DACCFG_READ(dac, FFBDAC_CFG_TCOLORKEY);
322dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC2)
323dbbd9e4bSmacallan		state->wamask = DACCFG_READ(dac, FFBDAC_CFG_WAMASK);
324dbbd9e4bSmacallan
325dbbd9e4bSmacallan	if (p->flags & FFB_DAC_PAC1) {
326dbbd9e4bSmacallan		dac->cfg = FFBDAC_PAC1_APWLUT_BASE;
327dbbd9e4bSmacallan		for (i = 0; i < 32; i++)
328dbbd9e4bSmacallan			state->pwluts[i] = dac->cfgdata;
329dbbd9e4bSmacallan	} else {
330dbbd9e4bSmacallan		dac->cfg = FFBDAC_PAC2_APWLUT_BASE;
331dbbd9e4bSmacallan		for (i = 0; i < 64; i++)
332dbbd9e4bSmacallan			state->pwluts[i] = dac->cfgdata;
333dbbd9e4bSmacallan	}
334dbbd9e4bSmacallan
335dbbd9e4bSmacallan	state->dacctrl = DACCFG_READ(dac, FFBDAC_CFG_DACCTRL);
336dbbd9e4bSmacallan
337dbbd9e4bSmacallan	state->tgen = DACCFG_READ(dac, FFBDAC_CFG_TGEN);
338dbbd9e4bSmacallan	state->vbnp = DACCFG_READ(dac, FFBDAC_CFG_VBNP);
339dbbd9e4bSmacallan	state->vbap = DACCFG_READ(dac, FFBDAC_CFG_VBAP);
340dbbd9e4bSmacallan	state->vsnp = DACCFG_READ(dac, FFBDAC_CFG_VSNP);
341dbbd9e4bSmacallan	state->vsap = DACCFG_READ(dac, FFBDAC_CFG_VSAP);
342dbbd9e4bSmacallan	state->hsnp = DACCFG_READ(dac, FFBDAC_CFG_HSNP);
343dbbd9e4bSmacallan	state->hbnp = DACCFG_READ(dac, FFBDAC_CFG_HBNP);
344dbbd9e4bSmacallan	state->hbap = DACCFG_READ(dac, FFBDAC_CFG_HBAP);
345dbbd9e4bSmacallan	state->hsyncnp = DACCFG_READ(dac, FFBDAC_CFG_HSYNCNP);
346dbbd9e4bSmacallan	state->hsyncap = DACCFG_READ(dac, FFBDAC_CFG_HSYNCAP);
347dbbd9e4bSmacallan	state->hscennp = DACCFG_READ(dac, FFBDAC_CFG_HSCENNP);
348dbbd9e4bSmacallan	state->hscenap = DACCFG_READ(dac, FFBDAC_CFG_HSCENAP);
349dbbd9e4bSmacallan	state->epnp = DACCFG_READ(dac, FFBDAC_CFG_EPNP);
350dbbd9e4bSmacallan	state->einp = DACCFG_READ(dac, FFBDAC_CFG_EINP);
351dbbd9e4bSmacallan	state->eiap = DACCFG_READ(dac, FFBDAC_CFG_EIAP);
352dbbd9e4bSmacallan}
353dbbd9e4bSmacallan
354dbbd9e4bSmacallanstatic void
355dbbd9e4bSmacallaninit_dac_flags(FFBPtr pFfb)
356dbbd9e4bSmacallan{
357dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
358dbbd9e4bSmacallan	ffb_dacPtr dac = pFfb->dac;
359dbbd9e4bSmacallan	unsigned int did, manuf_rev, partnum;
360dbbd9e4bSmacallan	char *device;
361dbbd9e4bSmacallan
362dbbd9e4bSmacallan	/* Fetch kernel WID. */
363dbbd9e4bSmacallan	p->kernel_wid = *((volatile unsigned char *)pFfb->dfb8x);
364dbbd9e4bSmacallan
365dbbd9e4bSmacallan	/* For AFB, assume it is PAC2 which also implies not having
366dbbd9e4bSmacallan	 * the inverted cursor control attribute.
367dbbd9e4bSmacallan	 */
368dbbd9e4bSmacallan	if (pFfb->ffb_type == afb_m3 || pFfb->ffb_type == afb_m6) {
369dbbd9e4bSmacallan		p->flags = FFB_DAC_PAC2;
370dbbd9e4bSmacallan		manuf_rev = 4;
371dbbd9e4bSmacallan	} else {
372dbbd9e4bSmacallan		p->flags = 0;
373dbbd9e4bSmacallan
374dbbd9e4bSmacallan		did = DACCFG_READ(dac, FFBDAC_CFG_DID);
375dbbd9e4bSmacallan
376dbbd9e4bSmacallan		manuf_rev = DACCFG_READ(dac, FFBDAC_CFG_UCTRL);
377dbbd9e4bSmacallan		manuf_rev = (manuf_rev & FFBDAC_UCTRL_MANREV) >> 8;
378dbbd9e4bSmacallan
379dbbd9e4bSmacallan		partnum = ((did & FFBDAC_CFG_DID_PNUM) >> 12);
380dbbd9e4bSmacallan		if (partnum == 0x236e)
381dbbd9e4bSmacallan			p->flags |= FFB_DAC_PAC2;
382dbbd9e4bSmacallan		else
383dbbd9e4bSmacallan			p->flags |= FFB_DAC_PAC1;
384dbbd9e4bSmacallan	}
385dbbd9e4bSmacallan
386dbbd9e4bSmacallan	device = pFfb->psdp->device;
387dbbd9e4bSmacallan	if ((p->flags & FFB_DAC_PAC1) != 0) {
388dbbd9e4bSmacallan		if (manuf_rev < 3) {
389dbbd9e4bSmacallan			p->flags |= FFB_DAC_ICURCTL;
390dbbd9e4bSmacallan			xf86Msg(X_INFO, "%s: BT9068 (PAC1) ramdac detected (with "
391dbbd9e4bSmacallan				"inverted cursor control)\n", device);
392dbbd9e4bSmacallan		} else {
393dbbd9e4bSmacallan			xf86Msg(X_INFO, "%s: BT9068 (PAC1) ramdac detected (with "
394dbbd9e4bSmacallan				"normal cursor control)\n", device);
395dbbd9e4bSmacallan		}
396dbbd9e4bSmacallan	} else {
397dbbd9e4bSmacallan		xf86Msg(X_INFO, "%s: BT498 (PAC2) ramdac detected\n", device);
398dbbd9e4bSmacallan	}
399dbbd9e4bSmacallan}
400dbbd9e4bSmacallan
401dbbd9e4bSmacallan/* The registers of the chip must be mapped, and the FFB/AFB
402dbbd9e4bSmacallan * board type must be probed before this is invoked.
403dbbd9e4bSmacallan */
404dbbd9e4bSmacallanBool
405dbbd9e4bSmacallanFFBDacInit(FFBPtr pFfb)
406dbbd9e4bSmacallan{
407dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
408dbbd9e4bSmacallan	ffb_fbcPtr ffb = pFfb->regs;
409dbbd9e4bSmacallan
410dbbd9e4bSmacallan	init_dac_flags(pFfb);
411dbbd9e4bSmacallan
412dbbd9e4bSmacallan	p->ffbcfg0 = ffb->fbcfg0;
413dbbd9e4bSmacallan	p->ffbcfg2 = ffb->fbcfg2;
414dbbd9e4bSmacallan	if (pFfb->ffb_type == ffb2_vertical_plus ||
415dbbd9e4bSmacallan	    pFfb->ffb_type == ffb2_horizontal_plus ||
416dbbd9e4bSmacallan	    pFfb->ffb_type == afb_m3 ||
417dbbd9e4bSmacallan	    pFfb->ffb_type == afb_m6)
418dbbd9e4bSmacallan		p->ffb_passin_ctrl = ffb->passin;
419dbbd9e4bSmacallan
420dbbd9e4bSmacallan	/* Save the kernel DAC state.  We also save to the
421dbbd9e4bSmacallan	 * X server state here as well even though we have
422dbbd9e4bSmacallan	 * not modified anything yet.
423dbbd9e4bSmacallan	 */
424dbbd9e4bSmacallan	dac_state_save(pFfb, &p->kern_dac_state);
425dbbd9e4bSmacallan	dac_state_save(pFfb, &p->x_dac_state);
426dbbd9e4bSmacallan
427dbbd9e4bSmacallan	/* Fire up the WID layer. */
428dbbd9e4bSmacallan	FFBWidPoolInit(pFfb);
429dbbd9e4bSmacallan
430dbbd9e4bSmacallan	return TRUE;
431dbbd9e4bSmacallan}
432dbbd9e4bSmacallan
433dbbd9e4bSmacallan/* We need to reset the A buffer X planes to the value 0xff
434dbbd9e4bSmacallan * when giving the hardware back to the kernel too, thus...
435dbbd9e4bSmacallan * Also need to do this for the B buffer X planes when double
436dbbd9e4bSmacallan * buffering is available.
437dbbd9e4bSmacallan */
438dbbd9e4bSmacallanstatic void
439dbbd9e4bSmacallanrestore_kernel_xchannel(FFBPtr pFfb)
440dbbd9e4bSmacallan{
441dbbd9e4bSmacallan	ffb_fbcPtr ffb = pFfb->regs;
442dbbd9e4bSmacallan	unsigned int fbc, ppc, ppc_mask, drawop, wid;
443dbbd9e4bSmacallan
444dbbd9e4bSmacallan	wid = pFfb->dac_info.kernel_wid;
445dbbd9e4bSmacallan
446dbbd9e4bSmacallan	if (pFfb->has_double_buffer)
447dbbd9e4bSmacallan		fbc = FFB_FBC_WB_AB;
448dbbd9e4bSmacallan	else
449dbbd9e4bSmacallan		fbc = FFB_FBC_WB_A;
450dbbd9e4bSmacallan
451dbbd9e4bSmacallan	fbc |= (FFB_FBC_WM_COMBINED | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
452dbbd9e4bSmacallan		FFB_FBC_ZE_OFF | FFB_FBC_YE_OFF |
453dbbd9e4bSmacallan		FFB_FBC_XE_ON | FFB_FBC_RGBE_MASK);
454dbbd9e4bSmacallan
455dbbd9e4bSmacallan	ppc      = (FFB_PPC_APE_DISABLE | FFB_PPC_CS_CONST | FFB_PPC_XS_WID);
456dbbd9e4bSmacallan	ppc_mask = (FFB_PPC_APE_MASK | FFB_PPC_CS_MASK | FFB_PPC_XS_MASK);
457dbbd9e4bSmacallan
458dbbd9e4bSmacallan	drawop = FFB_DRAWOP_RECTANGLE;
459dbbd9e4bSmacallan
460dbbd9e4bSmacallan	FFB_ATTR_RAW(pFfb, ppc, ppc_mask, ~0,
461dbbd9e4bSmacallan		     (FFB_ROP_EDIT_BIT | GXcopy)|(FFB_ROP_NEW<<8),
462dbbd9e4bSmacallan		     drawop, 0x0, fbc, wid);
463dbbd9e4bSmacallan
464dbbd9e4bSmacallan	FFBFifo(pFfb, 4);
465dbbd9e4bSmacallan	FFB_WRITE64(&ffb->by, 0, 0);
466dbbd9e4bSmacallan	FFB_WRITE64_2(&ffb->bh, pFfb->psdp->height, pFfb->psdp->width);
467dbbd9e4bSmacallan	pFfb->rp_active = 1;
468dbbd9e4bSmacallan	FFBWait(pFfb, ffb);
469dbbd9e4bSmacallan}
470dbbd9e4bSmacallan
471dbbd9e4bSmacallanvoid
472dbbd9e4bSmacallanFFBDacFini(FFBPtr pFfb)
473dbbd9e4bSmacallan{
474dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
475dbbd9e4bSmacallan
476dbbd9e4bSmacallan	/* Just restore the kernel ramdac/x-channel state. */
477dbbd9e4bSmacallan	dac_state_restore(pFfb, &p->kern_dac_state);
478dbbd9e4bSmacallan	restore_kernel_xchannel(pFfb);
479dbbd9e4bSmacallan}
480dbbd9e4bSmacallan
481dbbd9e4bSmacallan
482dbbd9e4bSmacallan/* Restore X server DAC state. */
483dbbd9e4bSmacallanvoid
484dbbd9e4bSmacallanFFBDacEnterVT(FFBPtr pFfb)
485dbbd9e4bSmacallan{
486dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
487dbbd9e4bSmacallan
488dbbd9e4bSmacallan	/* Save kernel DAC state. */
489dbbd9e4bSmacallan	dac_state_save(pFfb, &p->kern_dac_state);
490dbbd9e4bSmacallan
491dbbd9e4bSmacallan	/* Restore X DAC state. */
492dbbd9e4bSmacallan	dac_state_restore(pFfb, &p->x_dac_state);
493dbbd9e4bSmacallan}
494dbbd9e4bSmacallan
495dbbd9e4bSmacallan/* Restore kernel DAC state. */
496dbbd9e4bSmacallanvoid
497dbbd9e4bSmacallanFFBDacLeaveVT(FFBPtr pFfb)
498dbbd9e4bSmacallan{
499dbbd9e4bSmacallan	ffb_dac_info_t *p = &pFfb->dac_info;
500dbbd9e4bSmacallan
501dbbd9e4bSmacallan	/* Save X DAC state. */
502dbbd9e4bSmacallan	dac_state_save(pFfb, &p->x_dac_state);
503dbbd9e4bSmacallan
504dbbd9e4bSmacallan	/* Restore kernel DAC and x-channel state. */
505dbbd9e4bSmacallan	dac_state_restore(pFfb, &p->kern_dac_state);
506dbbd9e4bSmacallan	restore_kernel_xchannel(pFfb);
507dbbd9e4bSmacallan}
508dbbd9e4bSmacallan
509dbbd9e4bSmacallan/*  DPMS stuff, courtesy of a hint from David S. Miller.
510dbbd9e4bSmacallan *  05.xii.01, FEM
511dbbd9e4bSmacallan */
512dbbd9e4bSmacallan
513dbbd9e4bSmacallan/*
514dbbd9e4bSmacallan * I don't know why, if at all, this is needed, but JJ or DSM do it
515dbbd9e4bSmacallan * on restore. I observe that when just blanking/unblanking, everything
516dbbd9e4bSmacallan * works fine without it, but that sometimes DPMS -> Standby actually
517dbbd9e4bSmacallan * results in Off.  Maybe related?
518dbbd9e4bSmacallan */
519dbbd9e4bSmacallanstatic void
520dbbd9e4bSmacallanSPIN(ffb_dacPtr d, int count) {
521dbbd9e4bSmacallan  while(count-- > 0) {
522dbbd9e4bSmacallan    (void) DACCFG_READ(d, FFBDAC_CFG_TGVC);
523dbbd9e4bSmacallan  }
524dbbd9e4bSmacallan  return;
525dbbd9e4bSmacallan}
526dbbd9e4bSmacallan
527dbbd9e4bSmacallan/*  Screen save (blank) restore */
528dbbd9e4bSmacallanBool
529dbbd9e4bSmacallanFFBDacSaveScreen(FFBPtr pFfb, int mode) {
530dbbd9e4bSmacallan  int tmp;
531dbbd9e4bSmacallan  ffb_dacPtr dac;
532dbbd9e4bSmacallan  if(!pFfb) return FALSE;   /* Is there any way at all this could happen? */
533dbbd9e4bSmacallan  else dac = pFfb -> dac;
534dbbd9e4bSmacallan
535dbbd9e4bSmacallan  tmp = DACCFG_READ(dac, FFBDAC_CFG_TGEN);  /* Get the timing information */
536dbbd9e4bSmacallan
537dbbd9e4bSmacallan  switch(mode) {
538dbbd9e4bSmacallan    case SCREEN_SAVER_ON:
539dbbd9e4bSmacallan    case SCREEN_SAVER_CYCLE:
540dbbd9e4bSmacallan      tmp &= ~FFBDAC_CFG_TGEN_VIDE;  /* Kill the video */
541dbbd9e4bSmacallan      break;
542dbbd9e4bSmacallan
543dbbd9e4bSmacallan    case SCREEN_SAVER_OFF:
544dbbd9e4bSmacallan    case SCREEN_SAVER_FORCER:
545dbbd9e4bSmacallan      tmp |= FFBDAC_CFG_TGEN_VIDE;  /* Turn the video on */
546dbbd9e4bSmacallan      break;
547dbbd9e4bSmacallan
548dbbd9e4bSmacallan    default:
549dbbd9e4bSmacallan      return FALSE;  /* Don't know what to do; gently fail. */
550dbbd9e4bSmacallan  }
551dbbd9e4bSmacallan  DACCFG_WRITE(dac, FFBDAC_CFG_TGEN, tmp);  /* Restore timing register, video set as asked */
552dbbd9e4bSmacallan  SPIN(dac, DPMS_SPIN_COUNT/10);
553dbbd9e4bSmacallan  return TRUE;
554dbbd9e4bSmacallan}
555dbbd9e4bSmacallan
556dbbd9e4bSmacallan/*  DPMS Control, also hinted at by David Miller.
557dbbd9e4bSmacallan
558dbbd9e4bSmacallan    The rule seems to be:
559dbbd9e4bSmacallan
560dbbd9e4bSmacallan    StandBy  =  -HSYNC +VSYNC -VIDEO
561dbbd9e4bSmacallan    Suspend  =  +HSYNC -VSYNC -VIDEO
562dbbd9e4bSmacallan    Off      =  -HSYNC -VSYNC -VIDEO
563dbbd9e4bSmacallan    On       =  +HSYNC +VSINC +VIDEO
564dbbd9e4bSmacallan
565dbbd9e4bSmacallan    If you don't force video off, someone periodically tries to turn the
566dbbd9e4bSmacallan    monitor on for some reason.  I don't know who or why, so I kill the video
567dbbd9e4bSmacallan    when trying to go into some sort of energy saving mode.  (In real life,
568dbbd9e4bSmacallan    'xset s blank s xx' could well have taken care of this.)
569dbbd9e4bSmacallan
570dbbd9e4bSmacallan    Also, on MY monitor, StandBy as above defined (-H+V-Vid) in fact
571dbbd9e4bSmacallan    gives the same as Off, which I don't want.  Hence, I just do (-Vid)
572dbbd9e4bSmacallan
573dbbd9e4bSmacallan    05.xii.01, FEM
574dbbd9e4bSmacallan    08.xii.01, FEM
575dbbd9e4bSmacallan*/
576dbbd9e4bSmacallanvoid
577dbbd9e4bSmacallanFFBDacDPMSMode(FFBPtr pFfb, int DPMSMode, int flags) {
578dbbd9e4bSmacallan  int tmp;
579dbbd9e4bSmacallan  ffb_dacPtr dac = pFfb -> dac;
580dbbd9e4bSmacallan
581dbbd9e4bSmacallan  tmp = DACCFG_READ(dac, FFBDAC_CFG_TGEN);  /* Get timing control */
582dbbd9e4bSmacallan
583dbbd9e4bSmacallan  switch(DPMSMode) {
584dbbd9e4bSmacallan
585dbbd9e4bSmacallan    case DPMSModeOn:
586dbbd9e4bSmacallan      tmp &= ~(FFBDAC_CFG_TGEN_VSD | FFBDAC_CFG_TGEN_HSD); /* Turn off VSYNC, HSYNC
587dbbd9e4bSmacallan							      disable bits */
588dbbd9e4bSmacallan      tmp |= FFBDAC_CFG_TGEN_VIDE;  /* Turn the video on */
589dbbd9e4bSmacallan       break;
590dbbd9e4bSmacallan
591dbbd9e4bSmacallan    case DPMSModeStandby:
592dbbd9e4bSmacallan#ifdef  DPMS_TRUE_STANDBY
593dbbd9e4bSmacallan      tmp |=  FFBDAC_CFG_TGEN_HSD;  /* HSYNC = OFF    */
594dbbd9e4bSmacallan#endif  /* DPMS_TRUE_STANDBY */
595dbbd9e4bSmacallan      tmp &= ~FFBDAC_CFG_TGEN_VSD;  /* VSYNC = ON     */
596dbbd9e4bSmacallan      tmp &= ~FFBDAC_CFG_TGEN_VIDE; /* Kill the video */
597dbbd9e4bSmacallan      break;
598dbbd9e4bSmacallan
599dbbd9e4bSmacallan    case DPMSModeSuspend:
600dbbd9e4bSmacallan      tmp |=  FFBDAC_CFG_TGEN_VSD;  /* VSYNC = OFF    */
601dbbd9e4bSmacallan      tmp &= ~FFBDAC_CFG_TGEN_HSD;  /* HSYNC = ON     */
602dbbd9e4bSmacallan      tmp &= ~FFBDAC_CFG_TGEN_VIDE; /* Kill the video */
603dbbd9e4bSmacallan      break;
604dbbd9e4bSmacallan
605dbbd9e4bSmacallan    case DPMSModeOff:
606dbbd9e4bSmacallan      tmp |= (FFBDAC_CFG_TGEN_VSD | FFBDAC_CFG_TGEN_HSD);  /* Kill HSYNC, VSYNC both */
607dbbd9e4bSmacallan      tmp &= ~FFBDAC_CFG_TGEN_VIDE;                        /* Kill the video         */
608dbbd9e4bSmacallan      break;
609dbbd9e4bSmacallan
610dbbd9e4bSmacallan    default:
611dbbd9e4bSmacallan      return;     /* If we get here, we really should log an error */
612dbbd9e4bSmacallan  }
613dbbd9e4bSmacallan  DACCFG_WRITE(dac, FFBDAC_CFG_TGEN,tmp);  /* Restore timing register, video set as asked */
614dbbd9e4bSmacallan  SPIN(dac, DPMS_SPIN_COUNT);  /* Is this necessary?  Why?  */
615dbbd9e4bSmacallan}
616