Edit

IABSD.fr/xenocara/driver/xf86-video-sunffb/src/ffb_dac.c

Branch :

  • Show log

    Commit

  • Author : matthieu
    Date : 2013-06-01 19:20:43
    Hash : 6858fca6
    Message : Update to xf86-video-sunffb 1.2.2

  • driver/xf86-video-sunffb/src/ffb_dac.c
  • /*
     * Acceleration for the Creator and Creator3D framebuffer - DAC programming.
     *
     * Copyright (C) 2000 David S. Miller (davem@redhat.com)
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     * DAVID MILLER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     */
    
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "ffb.h"
    #include "ffb_rcache.h"
    #include "ffb_fifo.h"
    
    #include "xf86.h"
    #include "xf86_OSproc.h"
    
    #include "xf86DDC.h"
    
    /*
     * Used for stabilize time after playing with power management on the display
     */
    
    #ifndef DPMS_SPIN_COUNT
    #define DPMS_SPIN_COUNT 100
    #endif  /* DPMS_SPIN_COUNT */
    
    /* Cursor programming */
    
    void
    FFBDacLoadCursorPos(FFBPtr pFfb, int x, int y)
    {
    	ffb_dacPtr dac = pFfb->dac;
    	int posval;
    
    	posval = ((y & 0xffff) << 16) | (x & 0xffff);
    	posval &= (FFBDAC_CUR_POS_Y_SIGN |
    		   FFBDAC_CUR_POS_Y |
    		   FFBDAC_CUR_POS_X_SIGN |
    		   FFBDAC_CUR_POS_X);
    
    	DACCUR_WRITE(dac, FFBDAC_CUR_POS, posval);
    }
    
    void
    FFBDacLoadCursorColor(FFBPtr pFfb, int fg, int bg)
    {
    	ffb_dacPtr dac = pFfb->dac;
    
    	dac->cur = FFBDAC_CUR_COLOR1;
    	dac->curdata = bg;
    	dac->curdata = fg;
    }
    
    void
    FFBDacCursorEnableDisable(FFBPtr pFfb, int enable)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    	ffb_dacPtr dac = pFfb->dac;
    	int val;
    
    	val = 0;
    	if (!enable)
    		val = (FFBDAC_CUR_CTRL_P0 | FFBDAC_CUR_CTRL_P1);
    
    	/* PAC1 ramdacs with manufacturing revision less than
    	 * '3' invert these control bits, wheee...
    	 */
    	if (p->flags & FFB_DAC_ICURCTL)
    		val ^= (FFBDAC_CUR_CTRL_P0 | FFBDAC_CUR_CTRL_P1);
    
    	DACCUR_WRITE(dac, FFBDAC_CUR_CTRL, val);
    }
    
    void
    FFBDacCursorLoadBitmap(FFBPtr pFfb, int xshift, int yshift, unsigned int *bitmap)
    {
    	ffb_dacPtr dac = pFfb->dac;
    	int i, j;
    
    	dac->cur = FFBDAC_CUR_BITMAP_P0;
    	for (j = 0; j < 2; j++) {
    		bitmap += yshift * 2;
    		if (!xshift) {
    			for (i = yshift * 2; i < 128; i++)
    				dac->curdata = *bitmap++;
    		} else if (xshift < 32) {
    			for (i = yshift; i < 64; i++, bitmap += 2) {
    				dac->curdata = (bitmap[0] << xshift) |
    					(bitmap[1] >> (32 - xshift));
    				dac->curdata = bitmap[1] << xshift;
    			}
    		} else {
    			for (i = yshift; i < 64; i++, bitmap += 2) {
    				dac->curdata = bitmap[1] << (xshift - 32);
    				dac->curdata = 0;
    			}
    		}
    
    		for (i = 0; i < yshift * 2; i++)
    			dac->curdata = 0;
    	}
    }
    
    /* Config space programming */
    
    /* XF86 LoadPalette callback. */
    
    void
    FFBDacLoadPalette(ScrnInfoPtr pScrn, int ncolors, int *indices, LOCO *colors, VisualPtr pVisual)
    {
    	FFBPtr pFfb = GET_FFB_FROM_SCRN(pScrn);
    	ffb_dac_info_t *p = &pFfb->dac_info;
    	ffb_dacPtr dac = pFfb->dac;
    	unsigned int *cluts;
    	int i, index, palette;
    
    	if ((pVisual->nplanes != 8 && pVisual->class != DirectColor) ||
    	    (pVisual->nplanes == 8 && pVisual->class == StaticGray))
    		return;
    
    	palette = 0;
    	if (p->flags & FFB_DAC_PAC2) {
    		if (pVisual->class == PseudoColor)
    			palette = 0;
    		if (pVisual->class == GrayScale)
    			palette = 1;
    		if (pVisual->class == DirectColor)
    			palette = 2;
    	}
    
    	cluts = &p->x_dac_state.clut[256 * palette];
    	for (i = 0; i < ncolors; i++) {
    		unsigned int regval;
    
    		index = indices[i];
    		if (pVisual->class == GrayScale) {
    			regval = cluts[index] =
    				((colors[index].red << FFBDAC_COLOR_RED_SHFT) |
    				 (colors[index].red << FFBDAC_COLOR_GREEN_SHFT) |
    				 (colors[index].red << FFBDAC_COLOR_BLUE_SHFT));
    		} else {
    			regval = cluts[index] =
    				((colors[index].red   << FFBDAC_COLOR_RED_SHFT) |
    				 (colors[index].green << FFBDAC_COLOR_GREEN_SHFT) |
    				 (colors[index].blue  << FFBDAC_COLOR_BLUE_SHFT));
    		}
    
    		FFBLOG(("FFBDacLoadPalette: visclass(%d) index(%d) val[%08x]\n",
    			pVisual->class, index, regval));
    
    		/* Now update the hardware copy. */
    		dac->cfg = FFBDAC_CFG_CLUP(palette) + index;
    		dac->cfgdata = regval;
    	}
    }
    
    /* WARNING: Very dangerous function, use with extreme care. */
    static void
    dac_stop(FFBPtr pFfb)
    {
    	ffb_dacPtr dac = pFfb->dac;
    	unsigned int tgctrl;
    
    	tgctrl = DACCFG_READ(dac, FFBDAC_CFG_TGEN);
    	if (tgctrl & FFBDAC_CFG_TGEN_TGE) {
    		long limit = 1000000;
    
    		/* We try to shut off the timing generation
    		 * precisely at the beginning of a vertical
    		 * retrace.  This is really just to make it
    		 * look nice, it's not a functional necessity.
    		 *
    		 * The limit is so that malfunctioning hardware
    		 * does not end up hanging the server.
    		 */
    		while (limit--) {
    			unsigned int vctr = DACCFG_READ(dac, FFBDAC_CFG_TGVC);
    
    			if (vctr == 0)
    				break;
    		}		
    
    		DACCFG_WRITE(dac, FFBDAC_CFG_TGEN, 0);
    	}
    }
    
    /* This is made slightly complex because the ordering matters
     * between several operations.  We have to stop the DAC while
     * restoring the timing registers so that some intermediate
     * state does not emit wild retrace signals to the monitor.
     *
     * Another further complication is that we need to mess with
     * some portions of the FFB framebuffer config registers to
     * do this all properly.
     */
    static void
    dac_state_restore(FFBPtr pFfb, ffb_dac_hwstate_t *state)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    	ffb_dacPtr dac = pFfb->dac;
    	ffb_fbcPtr ffb = pFfb->regs;
    	int i, nluts;
    
    	/* Step 1: Shut off all pixel timing generation. */
    	dac_stop(pFfb);
    	ffb->fbcfg0 = 0;
    
    	/* Step 2: Restore timing settings. */
    	DACCFG_WRITE(dac, FFBDAC_CFG_VBNP, state->vbnp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_VBAP, state->vbap);
    	DACCFG_WRITE(dac, FFBDAC_CFG_VSNP, state->vsnp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_VSAP, state->vsap);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HSNP, state->hsnp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HBNP, state->hbnp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HBAP, state->hbap);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HSYNCNP, state->hsyncnp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HSYNCAP, state->hsyncap);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HSCENNP, state->hscennp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_HSCENAP, state->hscenap);
    	DACCFG_WRITE(dac, FFBDAC_CFG_EPNP, state->epnp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_EINP, state->einp);
    	DACCFG_WRITE(dac, FFBDAC_CFG_EIAP, state->eiap);
    
    	/* Step 3: Restore rest of DAC hw state. */
    	DACCFG_WRITE(dac, FFBDAC_CFG_PPLLCTRL, state->ppllctrl);
    	DACCFG_WRITE(dac, FFBDAC_CFG_GPLLCTRL, state->gpllctrl);
    	DACCFG_WRITE(dac, FFBDAC_CFG_PFCTRL, state->pfctrl);
    	DACCFG_WRITE(dac, FFBDAC_CFG_UCTRL, state->uctrl);
    
    	nluts = (p->flags & FFB_DAC_PAC1) ? 256 : (4 * 256);
    	dac->cfg = FFBDAC_CFG_CLUP_BASE;
    	for (i = 0; i < nluts; i++)
    		dac->cfgdata = state->clut[i];
    
    	if (p->flags & FFB_DAC_PAC2) {
    		dac->cfg = FFBDAC_PAC2_AOVWLUT0;
    		for (i = 0; i < 4; i++)
    			dac->cfgdata = state->ovluts[i];
    	}
    
    	DACCFG_WRITE(dac, FFBDAC_CFG_WTCTRL, state->wtctrl);
    	DACCFG_WRITE(dac, FFBDAC_CFG_TMCTRL, state->tmctrl);
    	DACCFG_WRITE(dac, FFBDAC_CFG_TCOLORKEY, state->tcolorkey);
    	if (p->flags & FFB_DAC_PAC2)
    		DACCFG_WRITE(dac, FFBDAC_CFG_WAMASK, state->wamask);
    
    	if (p->flags & FFB_DAC_PAC1) {
    		dac->cfg = FFBDAC_PAC1_APWLUT_BASE;
    		for (i = 0; i < 32; i++)
    			dac->cfgdata = state->pwluts[i];
    	} else {
    		dac->cfg = FFBDAC_PAC2_APWLUT_BASE;
    		for (i = 0; i < 64; i++)
    			dac->cfgdata = state->pwluts[i];
    	}
    
    	DACCFG_WRITE(dac, FFBDAC_CFG_DACCTRL, state->dacctrl);
    
    	/* Step 4: Restore FFB framebuffer config state. */
    	if (pFfb->ffb_type == ffb2_vertical_plus ||
    	    pFfb->ffb_type == ffb2_horizontal_plus ||
    	    pFfb->ffb_type == afb_m3 ||
    	    pFfb->ffb_type == afb_m6)
    		ffb->passin = p->ffb_passin_ctrl;
    	ffb->fbcfg0 = p->ffbcfg0;
    	ffb->fbcfg2 = p->ffbcfg2;
    
    	/* Step 5: Restore the timing generator control reg. */
    	DACCFG_WRITE(dac, FFBDAC_CFG_TGEN, state->tgen);
    
    	/* Step 6: Pause for a bit. */
    	for (i = 0; i < 100; i++)
    		(void) DACCFG_READ(dac, FFBDAC_CFG_TGVC);
    }
    
    static void
    dac_state_save(FFBPtr pFfb, ffb_dac_hwstate_t *state)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    	ffb_dacPtr dac = pFfb->dac;
    	int i, nluts;
    
    	state->ppllctrl = DACCFG_READ(dac, FFBDAC_CFG_PPLLCTRL);
    	state->gpllctrl = DACCFG_READ(dac, FFBDAC_CFG_GPLLCTRL);
    	state->pfctrl   = DACCFG_READ(dac, FFBDAC_CFG_PFCTRL);
    	state->uctrl    = DACCFG_READ(dac, FFBDAC_CFG_UCTRL);
    
    	nluts = (p->flags & FFB_DAC_PAC1) ? 256 : (4 * 256);
    	dac->cfg = FFBDAC_CFG_CLUP_BASE;
    	for (i = 0; i < nluts; i++)
    		state->clut[i] = dac->cfgdata;
    
    	if (p->flags & FFB_DAC_PAC2) {
    		dac->cfg = FFBDAC_PAC2_AOVWLUT0;
    		for (i = 0; i < 4; i++)
    			state->ovluts[i] = dac->cfgdata;
    	}
    
    	state->wtctrl    = DACCFG_READ(dac, FFBDAC_CFG_WTCTRL);
    	state->tmctrl    = DACCFG_READ(dac, FFBDAC_CFG_TMCTRL);
    	state->tcolorkey = DACCFG_READ(dac, FFBDAC_CFG_TCOLORKEY);
    	if (p->flags & FFB_DAC_PAC2)
    		state->wamask = DACCFG_READ(dac, FFBDAC_CFG_WAMASK);
    
    	if (p->flags & FFB_DAC_PAC1) {
    		dac->cfg = FFBDAC_PAC1_APWLUT_BASE;
    		for (i = 0; i < 32; i++)
    			state->pwluts[i] = dac->cfgdata;
    	} else {
    		dac->cfg = FFBDAC_PAC2_APWLUT_BASE;
    		for (i = 0; i < 64; i++)
    			state->pwluts[i] = dac->cfgdata;
    	}
    
    	state->dacctrl = DACCFG_READ(dac, FFBDAC_CFG_DACCTRL);
    
    	state->tgen = DACCFG_READ(dac, FFBDAC_CFG_TGEN);
    	state->vbnp = DACCFG_READ(dac, FFBDAC_CFG_VBNP);
    	state->vbap = DACCFG_READ(dac, FFBDAC_CFG_VBAP);
    	state->vsnp = DACCFG_READ(dac, FFBDAC_CFG_VSNP);
    	state->vsap = DACCFG_READ(dac, FFBDAC_CFG_VSAP);
    	state->hsnp = DACCFG_READ(dac, FFBDAC_CFG_HSNP);
    	state->hbnp = DACCFG_READ(dac, FFBDAC_CFG_HBNP);
    	state->hbap = DACCFG_READ(dac, FFBDAC_CFG_HBAP);
    	state->hsyncnp = DACCFG_READ(dac, FFBDAC_CFG_HSYNCNP);
    	state->hsyncap = DACCFG_READ(dac, FFBDAC_CFG_HSYNCAP);
    	state->hscennp = DACCFG_READ(dac, FFBDAC_CFG_HSCENNP);
    	state->hscenap = DACCFG_READ(dac, FFBDAC_CFG_HSCENAP);
    	state->epnp = DACCFG_READ(dac, FFBDAC_CFG_EPNP);
    	state->einp = DACCFG_READ(dac, FFBDAC_CFG_EINP);
    	state->eiap = DACCFG_READ(dac, FFBDAC_CFG_EIAP);
    }
    
    static void
    init_dac_flags(FFBPtr pFfb)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    	ffb_dacPtr dac = pFfb->dac;
    	unsigned int did, manuf_rev, partnum;
    	char *device;
    
    	/* Fetch kernel WID. */
    	p->kernel_wid = *((volatile unsigned char *)pFfb->dfb8x);
    
    	/* For AFB, assume it is PAC2 which also implies not having
    	 * the inverted cursor control attribute.
    	 */
    	if (pFfb->ffb_type == afb_m3 || pFfb->ffb_type == afb_m6) {
    		p->flags = FFB_DAC_PAC2;
    		manuf_rev = 4;
    	} else {
    		p->flags = 0;
    
    		did = DACCFG_READ(dac, FFBDAC_CFG_DID);
    
    		manuf_rev = DACCFG_READ(dac, FFBDAC_CFG_UCTRL);
    		manuf_rev = (manuf_rev & FFBDAC_UCTRL_MANREV) >> 8;
    
    		partnum = ((did & FFBDAC_CFG_DID_PNUM) >> 12);
    		if (partnum == 0x236e)
    			p->flags |= FFB_DAC_PAC2;
    		else
    			p->flags |= FFB_DAC_PAC1;
    	}
    
    	device = pFfb->psdp->device;
    	if ((p->flags & FFB_DAC_PAC1) != 0) {
    		if (manuf_rev < 3) {
    			p->flags |= FFB_DAC_ICURCTL;
    			xf86Msg(X_INFO, "%s: BT9068 (PAC1) ramdac detected (with "
    				"inverted cursor control)\n", device);
    		} else {
    			xf86Msg(X_INFO, "%s: BT9068 (PAC1) ramdac detected (with "
    				"normal cursor control)\n", device);
    		}
    	} else {
    		xf86Msg(X_INFO, "%s: BT498 (PAC2) ramdac detected\n", device);
    	}
    }
    
    /* The registers of the chip must be mapped, and the FFB/AFB
     * board type must be probed before this is invoked.
     */
    Bool
    FFBDacInit(FFBPtr pFfb)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    	ffb_fbcPtr ffb = pFfb->regs;
    
    	init_dac_flags(pFfb);
    
    	p->ffbcfg0 = ffb->fbcfg0;
    	p->ffbcfg2 = ffb->fbcfg2;
    	if (pFfb->ffb_type == ffb2_vertical_plus ||
    	    pFfb->ffb_type == ffb2_horizontal_plus ||
    	    pFfb->ffb_type == afb_m3 ||
    	    pFfb->ffb_type == afb_m6)
    		p->ffb_passin_ctrl = ffb->passin;
    
    	/* Save the kernel DAC state.  We also save to the
    	 * X server state here as well even though we have
    	 * not modified anything yet.
    	 */
    	dac_state_save(pFfb, &p->kern_dac_state);
    	dac_state_save(pFfb, &p->x_dac_state);
    
    	/* Fire up the WID layer. */
    	FFBWidPoolInit(pFfb);
    
    	return TRUE;
    }
    
    /* We need to reset the A buffer X planes to the value 0xff
     * when giving the hardware back to the kernel too, thus...
     * Also need to do this for the B buffer X planes when double
     * buffering is available.
     */
    static void
    restore_kernel_xchannel(FFBPtr pFfb)
    {
    	ffb_fbcPtr ffb = pFfb->regs;
    	unsigned int fbc, ppc, ppc_mask, drawop, wid;
    
    	wid = pFfb->dac_info.kernel_wid;
    
    	if (pFfb->has_double_buffer)
    		fbc = FFB_FBC_WB_AB;
    	else
    		fbc = FFB_FBC_WB_A;
    
    	fbc |= (FFB_FBC_WM_COMBINED | FFB_FBC_RB_A | FFB_FBC_SB_BOTH |
    		FFB_FBC_ZE_OFF | FFB_FBC_YE_OFF |
    		FFB_FBC_XE_ON | FFB_FBC_RGBE_MASK);
    
    	ppc      = (FFB_PPC_APE_DISABLE | FFB_PPC_CS_CONST | FFB_PPC_XS_WID);
    	ppc_mask = (FFB_PPC_APE_MASK | FFB_PPC_CS_MASK | FFB_PPC_XS_MASK);
    
    	drawop = FFB_DRAWOP_RECTANGLE;
    
    	FFB_ATTR_RAW(pFfb, ppc, ppc_mask, ~0,
    		     (FFB_ROP_EDIT_BIT | GXcopy)|(FFB_ROP_NEW<<8),
    		     drawop, 0x0, fbc, wid);
    
    	FFBFifo(pFfb, 4);
    	FFB_WRITE64(&ffb->by, 0, 0);
    	FFB_WRITE64_2(&ffb->bh, pFfb->psdp->height, pFfb->psdp->width);
    	pFfb->rp_active = 1;
    	FFBWait(pFfb, ffb);
    }
    
    void
    FFBDacFini(FFBPtr pFfb)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    
    	/* Just restore the kernel ramdac/x-channel state. */
    	dac_state_restore(pFfb, &p->kern_dac_state);
    	restore_kernel_xchannel(pFfb);
    }
    
    
    /* Restore X server DAC state. */
    void
    FFBDacEnterVT(FFBPtr pFfb)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    
    	/* Save kernel DAC state. */
    	dac_state_save(pFfb, &p->kern_dac_state);
    
    	/* Restore X DAC state. */
    	dac_state_restore(pFfb, &p->x_dac_state);
    }
    
    /* Restore kernel DAC state. */
    void
    FFBDacLeaveVT(FFBPtr pFfb)
    {
    	ffb_dac_info_t *p = &pFfb->dac_info;
    
    	/* Save X DAC state. */
    	dac_state_save(pFfb, &p->x_dac_state);
    
    	/* Restore kernel DAC and x-channel state. */
    	dac_state_restore(pFfb, &p->kern_dac_state);
    	restore_kernel_xchannel(pFfb);
    }
    
    /*  DPMS stuff, courtesy of a hint from David S. Miller.
     *  05.xii.01, FEM
     */
    
    /*
     * I don't know why, if at all, this is needed, but JJ or DSM do it
     * on restore. I observe that when just blanking/unblanking, everything
     * works fine without it, but that sometimes DPMS -> Standby actually
     * results in Off.  Maybe related?
     */
    static void
    SPIN(ffb_dacPtr d, int count) {
      while(count-- > 0) {
        (void) DACCFG_READ(d, FFBDAC_CFG_TGVC);
      }
      return;
    }
    
    /*  Screen save (blank) restore */
    Bool
    FFBDacSaveScreen(FFBPtr pFfb, int mode) {
      int tmp;
      ffb_dacPtr dac;
      if(!pFfb) return FALSE;   /* Is there any way at all this could happen? */
      else dac = pFfb -> dac;
    
      tmp = DACCFG_READ(dac, FFBDAC_CFG_TGEN);  /* Get the timing information */
    
      switch(mode) {
        case SCREEN_SAVER_ON:
        case SCREEN_SAVER_CYCLE:
          tmp &= ~FFBDAC_CFG_TGEN_VIDE;  /* Kill the video */
          break;
    
        case SCREEN_SAVER_OFF:
        case SCREEN_SAVER_FORCER:
          tmp |= FFBDAC_CFG_TGEN_VIDE;  /* Turn the video on */
          break;
    
        default:
          return FALSE;  /* Don't know what to do; gently fail. */
      }
      DACCFG_WRITE(dac, FFBDAC_CFG_TGEN, tmp);  /* Restore timing register, video set as asked */
      SPIN(dac, DPMS_SPIN_COUNT/10);
      return TRUE;
    }
    
    /*  DPMS Control, also hinted at by David Miller.
    
        The rule seems to be:
        
        StandBy  =  -HSYNC +VSYNC -VIDEO
        Suspend  =  +HSYNC -VSYNC -VIDEO
        Off      =  -HSYNC -VSYNC -VIDEO
        On       =  +HSYNC +VSINC +VIDEO
    
        If you don't force video off, someone periodically tries to turn the
        monitor on for some reason.  I don't know who or why, so I kill the video
        when trying to go into some sort of energy saving mode.  (In real life,
        'xset s blank s xx' could well have taken care of this.)
    
        Also, on MY monitor, StandBy as above defined (-H+V-Vid) in fact
        gives the same as Off, which I don't want.  Hence, I just do (-Vid)
    
        05.xii.01, FEM
        08.xii.01, FEM
    */
    void
    FFBDacDPMSMode(FFBPtr pFfb, int DPMSMode, int flags) {
      int tmp;
      ffb_dacPtr dac = pFfb -> dac;
    
      tmp = DACCFG_READ(dac, FFBDAC_CFG_TGEN);  /* Get timing control */
    
      switch(DPMSMode) {
    
        case DPMSModeOn:
          tmp &= ~(FFBDAC_CFG_TGEN_VSD | FFBDAC_CFG_TGEN_HSD); /* Turn off VSYNC, HSYNC
    							      disable bits */
          tmp |= FFBDAC_CFG_TGEN_VIDE;  /* Turn the video on */
           break;
    
        case DPMSModeStandby:
    #ifdef  DPMS_TRUE_STANDBY
          tmp |=  FFBDAC_CFG_TGEN_HSD;  /* HSYNC = OFF    */
    #endif  /* DPMS_TRUE_STANDBY */
          tmp &= ~FFBDAC_CFG_TGEN_VSD;  /* VSYNC = ON     */
          tmp &= ~FFBDAC_CFG_TGEN_VIDE; /* Kill the video */
          break;
    
        case DPMSModeSuspend:
          tmp |=  FFBDAC_CFG_TGEN_VSD;  /* VSYNC = OFF    */
          tmp &= ~FFBDAC_CFG_TGEN_HSD;  /* HSYNC = ON     */
          tmp &= ~FFBDAC_CFG_TGEN_VIDE; /* Kill the video */
          break;
    
        case DPMSModeOff:
          tmp |= (FFBDAC_CFG_TGEN_VSD | FFBDAC_CFG_TGEN_HSD);  /* Kill HSYNC, VSYNC both */
          tmp &= ~FFBDAC_CFG_TGEN_VIDE;                        /* Kill the video         */
          break;
          
        default:
          return;     /* If we get here, we really should log an error */
      }
      DACCFG_WRITE(dac, FFBDAC_CFG_TGEN,tmp);  /* Restore timing register, video set as asked */
      SPIN(dac, DPMS_SPIN_COUNT);  /* Is this necessary?  Why?  */
    }