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