text: /* NeoMagic Back End Scaler functions */
  /* Written by Rudolf Cornelissen 05/2002-1/2006 */
  
  #define MODULE_BIT 0x00000200
  
  #include "nm_std.h"
  
  static void nm_bes_calc_move_overlay(move_overlay_info *moi);
  static void nm_bes_program_move_overlay(move_overlay_info moi);
  
  /* move the overlay output window in virtualscreens */
  /* Note:
   * si->dm.h_display_start and si->dm.v_display_start determine where the new
   * output window is located! */
  void nm_bes_move_overlay()
  {
  	move_overlay_info moi;
  
  	/* abort if overlay is not active */
  	if (!si->overlay.active) return;
  
  	nm_bes_calc_move_overlay(&moi);
  	nm_bes_program_move_overlay(moi);
  }
  
  static void nm_bes_calc_move_overlay(move_overlay_info *moi)
  {
  	/* misc used variables */
  	uint16 temp1, temp2;
  	/* visible screen window in virtual workspaces */
  	uint16 crtc_hstart, crtc_vstart, crtc_hend, crtc_vend;
  	/* horizontal source start in source buffer (clipping) */
  	uint32 hsrcstv;
  
  	/* the BES does not respect virtual_workspaces, but adheres to CRTC
  	 * constraints only */
  	crtc_hstart = si->dm.h_display_start;
  	/* horizontal end is the first position beyond the displayed range on the CRTC */
  	crtc_hend = crtc_hstart + si->dm.timing.h_display;
  	crtc_vstart = si->dm.v_display_start;
  	/* vertical end is the first position beyond the displayed range on the CRTC */
  	crtc_vend = crtc_vstart + si->dm.timing.v_display;
  
  
  	/****************************************
  	 *** setup all edges of output window ***
  	 ****************************************/
  
  	/* setup left and right edges of output window */
  	moi->hcoordv = 0;
  	/* left edge coordinate of output window, must be inside desktop */
  	/* clipping on the left side */
  	if (si->overlay.ow.h_start < crtc_hstart)
  	{
  		temp1 = 0;
  	}
  	else
  	{
  		/* clipping on the right side */
  		if (si->overlay.ow.h_start >= (crtc_hend - 1))
  		{
  			/* width < 2 is not allowed */
  			temp1 = (crtc_hend - crtc_hstart - 2);
  		} 
  		else
  		/* no clipping here */
  		{
  			temp1 = (si->overlay.ow.h_start - crtc_hstart);
  		}
  	} 
  	moi->hcoordv |= temp1 << 16;
  
  	/* right edge coordinate of output window, must be inside desktop */
  	/* width < 2 is not allowed */
  	if (si->overlay.ow.width < 2) 
  	{
  		temp2 = (temp1 + 1);
  	}
  	else 
  	{
  		/* clipping on the right side */
  		if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) > (crtc_hend - 1))
  		{
  			temp2 = (crtc_hend - crtc_hstart - 1);
  		}
  		else
  		{
  			/* clipping on the left side */
  			if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) < (crtc_hstart + 1))
  			{
  				/* width < 2 is not allowed */
  				temp2 = 1;
  			}
  			else
  			/* no clipping here */
  			{
  				temp2 = ((uint16)(si->overlay.ow.h_start + si->overlay.ow.width - crtc_hstart - 1));
  			}
  		}
  	}
  	moi->hcoordv |= temp2 << 0;
  	LOG(4,("Overlay: CRTC left-edge output %d, right-edge output %d\n",temp1, temp2));
  
  	/* setup top and bottom edges of output window */
  	moi->vcoordv = 0;
  	/* top edge coordinate of output window, must be inside desktop */
  	/* clipping on the top side */
  	if (si->overlay.ow.v_start < crtc_vstart)
  	{
  		temp1 = 0;
  	}
  	else
  	{
  		/* clipping on the bottom side */
  		if (si->overlay.ow.v_start >= (crtc_vend - 1))
  		{
  			/* height < 2 is not allowed */
  			temp1 = (crtc_vend - crtc_vstart - 2);
  		} 
  		else
  		/* no clipping here */
  		{
  			temp1 = (si->overlay.ow.v_start - crtc_vstart);
  		}
  	} 
  	moi->vcoordv |= temp1 << 16;
  
  	/* bottom edge coordinate of output window, must be inside desktop */
  	/* height < 2 is not allowed */
  	if (si->overlay.ow.height < 2) 
  	{
  		temp2 = (temp1 + 1);
  	}
  	else 
  	{
  		/* clipping on the bottom side */
  		if ((si->overlay.ow.v_start + si->overlay.ow.height - 1) > (crtc_vend - 1))
  		{
  			temp2 = (crtc_vend - crtc_vstart - 1);
  		}
  		else
  		{
  			/* clipping on the top side */
  			if ((si->overlay.ow.v_start + si->overlay.ow.height - 1) < (crtc_vstart + 1))
  			{
  				/* height < 2 is not allowed */
  				temp2 = 1;
  			}
  			else
  			/* no clipping here */
  			{
  				temp2 = ((uint16)(si->overlay.ow.v_start + si->overlay.ow.height - crtc_vstart - 1));
  			}
  		}
  	}
  	moi->vcoordv |= temp2 << 0;
  	LOG(4,("Overlay: CRTC top-edge output %d, bottom-edge output %d\n",temp1, temp2));
  
  
  	/*********************************
  	 *** setup horizontal clipping ***
  	 *********************************/
  
  	/* Setup horizontal source start: first (sub)pixel contributing to output picture */
  	/* Note:
  	 * The method is to calculate, based on 1:1 scaling, based on the output window.
  	 * After this is done, include the scaling factor so you get a value based on the input bitmap.
  	 * Then add the left starting position of the bitmap's view (zoom function) to get the final value needed.
  	 * Note: The input bitmaps slopspace is automatically excluded from the calculations this way! */
  	/* Note also:
  	 * Even if the scaling factor is clamping we instruct the BES to use the correct source start pos.! */
  	hsrcstv = 0;
  	/* check for destination horizontal clipping at left side */
  	if (si->overlay.ow.h_start < crtc_hstart)
  	{
  		/* check if entire destination picture is clipping left:
  		 * (2 pixels will be clamped onscreen at least) */
  		if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) < (crtc_hstart + 1))
  		{
  			/* increase 'first contributing pixel' with 'fixed value': (total dest. width - 2) */
  			hsrcstv += (si->overlay.ow.width - 2);
  		}
  		else
  		{
  			/* increase 'first contributing pixel' with actual number of dest. clipping pixels */
  			hsrcstv += (crtc_hstart - si->overlay.ow.h_start);
  		}
  		LOG(4,("Overlay: clipping left...\n"));
  
  		/* The calculated value is based on scaling = 1x. So we now compensate for scaling.
  		 * Note that this also already takes care of aligning the value to the BES register! */
  		hsrcstv *= (si->overlay.h_ifactor << 4);
  	}
  	/* take zoom into account */
  	hsrcstv += ((uint32)si->overlay.my_ov.h_start) << 16;
  	LOG(4,("Overlay: first hor. (sub)pixel of input bitmap contributing %f\n", hsrcstv / (float)65536));
  
  	/* Setup horizontal source end: last (sub)pixel contributing to output picture */
  	/* Note:
  	 * The method is to calculate, based on 1:1 scaling, based on the output window.
  	 * After this is done, include the scaling factor so you get a value based on the input bitmap.
  	 * Then add the right ending position of the bitmap's view (zoom function) to get the final value needed. */
  	/* Note also:
  	 * Even if the scaling factor is clamping we instruct the BES to use the correct source end pos.! */
  
  	moi->hsrcendv = 0;
  	/* check for destination horizontal clipping at right side */
  	if ((si->overlay.ow.h_start + si->overlay.ow.width - 1) > (crtc_hend - 1))
  	{
  		/* check if entire destination picture is clipping right:
  		 * (2 pixels will be clamped onscreen at least) */
  		if (si->overlay.ow.h_start > (crtc_hend - 2))
  		{
  			/* increase 'number of clipping pixels' with 'fixed value': (total dest. width - 2) */
  			moi->hsrcendv += (si->overlay.ow.width - 2);
  		}
  		else
  		{
  			/* increase 'number of clipping pixels' with actual number of dest. clipping pixels */
  			moi->hsrcendv += ((si->overlay.ow.h_start + si->overlay.ow.width - 1) - (crtc_hend - 1));
  		}
  		LOG(4,("Overlay: clipping right...\n"));
  
  		/* The calculated value is based on scaling = 1x. So we now compensate for scaling.
  		 * Note that this also already takes care of aligning the value to the BES register! */
  		moi->hsrcendv *= (si->overlay.h_ifactor << 4);
  		/* now subtract this value from the last used pixel in (zoomed) inputbuffer, aligned to BES */
  		moi->hsrcendv = (((uint32)((si->overlay.my_ov.h_start + si->overlay.my_ov.width) - 1)) << 16) - moi->hsrcendv;
  	}
  	else
  	{
  		/* set last contributing pixel to last used pixel in (zoomed) inputbuffer, aligned to BES */
  		moi->hsrcendv = (((uint32)((si->overlay.my_ov.h_start + si->overlay.my_ov.width) - 1)) << 16);
  	}
  	/* AND below required by hardware */
  	moi->hsrcendv &= 0x03ffffff;
  	LOG(4,("Overlay: last horizontal (sub)pixel of input bitmap contributing %f\n", moi->hsrcendv / (float)65536));
  
  
  	/*******************************
  	 *** setup vertical clipping ***
  	 *******************************/
  
  	/* calculate inputbitmap origin adress */
  	moi->a1orgv = (uint32)((vuint32 *)si->overlay.ob.buffer);
  	moi->a1orgv -= (uint32)((vuint32 *)si->framebuffer);
  	LOG(4,("Overlay: topleft corner of input bitmap (cardRAM offset) $%08x\n", moi->a1orgv));
  
  	/* Setup vertical source start: first (sub)pixel contributing to output picture. */
  	/* Note:
  	 * The method is to calculate, based on 1:1 scaling, based on the output window.
  	 * 'After' this is done, include the scaling factor so you get a value based on the input bitmap. 
  	 * Then add the top starting position of the bitmap's view (zoom function) to get the final value needed. */
  	/* Note also:
  	 * Even if the scaling factor is clamping we instruct the BES to use the correct source start pos.! */
  
  	/* check for destination vertical clipping at top side */
  	if (si->overlay.ow.v_start < crtc_vstart)
  	{
  		/* check if entire destination picture is clipping at top:
  		 * (2 pixels will be clamped onscreen at least) */
  		if ((si->overlay.ow.v_start + si->overlay.ow.height - 1) < (crtc_vstart + 1))
  		{
  			/* increase source buffer origin with 'fixed value':
  			 * (integer part of ('total height - 2' of dest. picture in pixels * inverse scaling factor)) *
  			 * bytes per row source picture */
  			//fixme: rounding down would be better than just chopping off the fractional part...
  			moi->a1orgv +=
  				((((si->overlay.ow.height - 2) * (si->overlay.v_ifactor << 4)) >> 16) *
  				si->overlay.ob.bytes_per_row);
  		}
  		else
  		{
  			/* increase source buffer origin with:
  			 * (integer part of (number of destination picture clipping pixels * inverse scaling factor)) *
  			 * bytes per row source picture */
  			//fixme: rounding down would be better than just chopping off the fractional part...
  			moi->a1orgv +=
  				((((crtc_vstart - si->overlay.ow.v_start) * (si->overlay.v_ifactor << 4)) >> 16) *
  				si->overlay.ob.bytes_per_row);
  		}
  		LOG(4,("Overlay: clipping at top...\n"));
  	}
  	/* take zoom into account */
  	moi->a1orgv += (si->overlay.my_ov.v_start * si->overlay.ob.bytes_per_row);
  	/* now include 'pixel precise' left clipping...
  	 * (subpixel precision is not supported by NeoMagic cards) */
  	moi->a1orgv += ((hsrcstv >> 16) * 2);
  	/* we need to step in 4-byte (2 pixel) granularity due to the nature of yuy2 */
  	moi->a1orgv &= ~0x03;
  	LOG(4,("Overlay: 'contributing part of buffer' origin is (cardRAM offset) $%08x\n", moi->a1orgv));
  }
  
  static void nm_bes_program_move_overlay(move_overlay_info moi)
  {
  	/*************************************
  	 *** sync to BES (Back End Scaler) ***
  	 *************************************/
  
  	/* Make sure reprogramming the BES completes before the next retrace occurs,
  	 * to prevent register-update glitches (double buffer feature). */
  
  	//fixme if needed...
  
  
  	/**************************************
  	 *** actually program the registers ***
  	 **************************************/
  	if (si->ps.card_type >= NM2097)
  	{
  		/* PCI card */
  		LOG(4,("Overlay: accelerant is programming BES\n"));
  		/* unlock card overlay sequencer registers (b5 = 1) */
  		PCIGRPHW(GENLOCK, (PCIGRPHR(GENLOCK) | 0x20));
  		/* destination rectangle #1 (output window position and size) */
  		PCIGRPHW(HD1COORD1L, ((moi.hcoordv >> 16) & 0xff));
  		PCIGRPHW(HD1COORD2L, (moi.hcoordv & 0xff));
  		PCIGRPHW(HD1COORD21H, (((moi.hcoordv >> 4) & 0xf0) | ((moi.hcoordv >> 24) & 0x0f)));
  		PCIGRPHW(VD1COORD1L, ((moi.vcoordv >> 16) & 0xff));
  		PCIGRPHW(VD1COORD2L, (moi.vcoordv & 0xff));
  		PCIGRPHW(VD1COORD21H, (((moi.vcoordv >> 4) & 0xf0) | ((moi.vcoordv >> 24) & 0x0f)));
  		/* inputbuffer #1 origin */
  		/* (we don't program buffer #2 as it's unused.) */
  		if (si->ps.card_type < NM2200)
  		{
  			moi.a1orgv >>= 1;
  			/* horizontal source end does not use subpixelprecision: granularity is 8 pixels */
  			/* notes:
  			 * - correctly programming horizontal source end minimizes used bandwidth;
  			 * - adding 9 below is in fact:
  			 *   - adding 1 to round-up to the nearest whole source-end value
  			       (making SURE we NEVER are a (tiny) bit too low);
  			     - adding 1 to convert 'last used position' to 'number of used pixels';
  			     - adding 7 to round-up to the nearest higher (or equal) valid register
  			       value (needed because of it's 8-pixel granularity). */
  			PCIGRPHW(0xbc, ((((moi.hsrcendv >> 16) + 9) >> 3) - 1));
  		}
  		else
  		{
  			/* horizontal source end does not use subpixelprecision: granularity is 16 pixels */
  			/* notes:
  			 * - programming this register just a tiny bit too low messes up vertical
  			 *   scaling badly (also distortion stripes and flickering are reported)!
  			 * - not programming this register correctly will mess-up the picture when
  			 *   it's partly clipping on the right side of the screen...
  			 * - make absolutely sure the engine can fetch the last pixel needed from
  			 *   the sourcebitmap even if only to generate a tiny subpixel from it!
  			 *   (see remarks for < NM2200 cards regarding programming this register) */
  			PCIGRPHW(0xbc, ((((moi.hsrcendv >> 16) + 17) >> 4) - 1));
  		}
  		PCIGRPHW(BUF1ORGL, (moi.a1orgv & 0xff));
  		PCIGRPHW(BUF1ORGM, ((moi.a1orgv >> 8) & 0xff));
  		PCIGRPHW(BUF1ORGH, ((moi.a1orgv >> 16) & 0xff));
  		/* ??? */
  		PCIGRPHW(0xbd, 0x02);
  		PCIGRPHW(0xbe, 0x00);
  		/* b2 = 0: don't use horizontal mirroring (NM2160) */
  		/* other bits do ??? */
  		PCIGRPHW(0xbf, 0x02);
  		/* ??? */
  	    PCISEQW(0x1c, 0xfb);
      	PCISEQW(0x1d, 0x00);
  		PCISEQW(0x1e, 0xe2);
      	PCISEQW(0x1f, 0x02);
   		/* b1 = 0: disable alternating hardware buffers (NM2160) */
  		/* other bits do ??? */
   		PCISEQW(0x09, 0x11);
  		/* we don't use PCMCIA Zoomed Video port capturing, set 1:1 scale just in case */
  		/* (b6-4 = Y downscale = 100%, b2-0 = X downscale = 100%;
  		 *  downscaling selectable in 12.5% steps on increasing setting by 1) */
  		PCISEQW(ZVCAP_DSCAL, 0x00);
  	}
  	else
  	{
  		/* bes setup data */
  		nm_bes_data bi;
  
  		/* ISA card. Speed required, so:
  		 * program entire sequence in kerneldriver in one context switch! */
  		LOG(4,("Overlay: kerneldriver programs BES\n"));
  
  		/* setup BES info struct... */
  		bi.moi = moi;
  		bi.card_type = si->ps.card_type;
  		bi.move_only = true;
  		/* ... and call kerneldriver to program the BES */
  		bi.magic = NM_PRIVATE_DATA_MAGIC;
  		ioctl(fd, NM_PGM_BES, &bi, sizeof(bi));
  	}
  }
  
  status_t nm_configure_bes
  	(const overlay_buffer *ob, const overlay_window *ow, const overlay_view *ov, int offset)
  {
  	/* yuy2 (4:2:2) colorspace calculations */
  	/* Note: Some calculations will have to be modified for other colorspaces if they are incorporated. */
  
  	/* Note:
  	 * in BeOS R5.0.3 and DANO:
  	 * 'ow->offset_xxx' is always 0, so not used;
  	 * 'ow->width' and 'ow->height' are the output window size: does not change
  	 * if window is clipping;
  	 * 'ow->h_start' and 'ow->v_start' are the left-top position of the output
  	 * window. These values can be negative: this means the window is clipping
  	 * at the left or the top of the display, respectively. */
  
  	/* 'ov' is the view in the source bitmap, so which part of the bitmap is actually
  	 * displayed on screen. This is used for the 'hardware zoom' function. */
   
  	/* bes setup data */
  	nm_bes_data bi;
  	/* inverse scaling factor, used for source positioning */
  	uint32 ifactor;
  	/* copy of overlay view which has checked valid values */
  	overlay_view my_ov;
  
  
  	/**************************************************************************************
  	 *** copy, check and limit if needed the user-specified view into the intput bitmap ***
  	 **************************************************************************************/
  	my_ov = *ov;
  	/* check for valid 'coordinates' */
  	if (my_ov.width == 0) my_ov.width++;
  	if (my_ov.height == 0) my_ov.height++;
  	if (my_ov.h_start > ((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1))
  		my_ov.h_start = ((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1);
  	if (((my_ov.h_start + my_ov.width) - 1) > ((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1))
  		my_ov.width = ((((ob->width - si->overlay.myBufInfo[offset].slopspace) - 1) - my_ov.h_start) + 1);
  	if (my_ov.v_start > (ob->height - 1))
  		my_ov.v_start = (ob->height - 1);
  	if (((my_ov.v_start + my_ov.height) - 1) > (ob->height - 1))
  		my_ov.height = (((ob->height - 1) - my_ov.v_start) + 1);
  
  	LOG(6,("Overlay: inputbuffer view (zoom) left %d, top %d, width %d, height %d\n",
  		my_ov.h_start, my_ov.v_start, my_ov.width, my_ov.height));
  
  	/* save for nm_bes_calc_move_overlay() */
  	si->overlay.ow = *ow;
  	si->overlay.ob = *ob;
  	si->overlay.my_ov = my_ov;
  
  
  	/********************************
  	 *** setup horizontal scaling ***
  	 ********************************/
  
  	LOG(6,("Overlay: total input picture width = %d, height = %d\n",
  			(ob->width - si->overlay.myBufInfo[offset].slopspace), ob->height));
  	LOG(6,("Overlay: output picture width = %d, height = %d\n", ow->width, ow->height));
  
  	/* calculate inverse horizontal scaling factor, taking zoom into account */
  	ifactor = ((((uint32)my_ov.width) << 12) / ow->width); 
  
  	/* correct factor to prevent most-right visible 'line' from distorting */
  	ifactor -= 1;
  	bi.hiscalv = ifactor;
  	/* save for nv_bes_calc_move_overlay() */
  	si->overlay.h_ifactor = ifactor;
  	LOG(4,("Overlay: horizontal scaling factor is %f\n", (float)4096 / ifactor));
  
  	/* check scaling factor (and modify if needed) to be within scaling limits */
  	/* the upscaling limit is 8.0 (see official Neomagic specsheets) */
  	if (bi.hiscalv < 0x00000200)
  	{
  		/* (non-inverse) factor too large, set factor to max. valid value */
  		bi.hiscalv = 0x00000200;
  		LOG(4,("Overlay: horizontal scaling factor too large, clamping at %f\n", (float)4096 / bi.hiscalv));
  	}
  	/* horizontal downscaling cannot be done by NM BES hardware */
  	if (bi.hiscalv > (1 << 12))
  	{
  		/* (non-inverse) factor too small, set factor to min. valid value */
  		bi.hiscalv = 0x1000;
  		LOG(4,("Overlay: horizontal scaling factor too small, clamping at %f\n", (float)4096 / bi.hiscalv));
  	}
  
  
  	/******************************
  	 *** setup vertical scaling ***
  	 ******************************/
  
  	/* calculate inverse vertical scaling factor, taking zoom into account */
  	ifactor = ((((uint32)my_ov.height) << 12) / ow->height); 
  
  	/* correct factor to prevent lowest visible line from distorting */
  	ifactor -= 1;
  	LOG(4,("Overlay: vertical scaling factor is %f\n", (float)4096 / ifactor));
  
  	/* preserve ifactor for source positioning calculations later on */
  	bi.viscalv = ifactor;
  	/* save for nv_bes_calc_move_overlay() */
  	si->overlay.v_ifactor = ifactor;
  
  	/* check scaling factor (and modify if needed) to be within scaling limits */
  	/* the upscaling limit is 8.0 (see official Neomagic specsheets) */
  	if (bi.viscalv < 0x00000200)
  	{
  		/* (non-inverse) factor too large, set factor to max. valid value */
  		bi.viscalv = 0x00000200;
  		LOG(4,("Overlay: vertical scaling factor too large, clamping at %f\n", (float)4096 / bi.viscalv));
  	}
  	/* vertical downscaling cannot be done by NM BES hardware */
  	if (bi.viscalv > (1 << 12))
  	{
  		/* (non-inverse) factor too small, set factor to min. valid value */
  		bi.viscalv = 0x1000;
  		LOG(4,("Overlay: vertical scaling factor too small, clamping at %f\n", (float)4096 / bi.viscalv));
  	}
  
  
  	/********************************************************************************
  	 *** setup all edges of output window, setup horizontal and vertical clipping ***
  	 ********************************************************************************/
  	nm_bes_calc_move_overlay(&(bi.moi));
  
  
  	/*****************************
  	 *** log color keying info ***
  	 *****************************/
  
  	LOG(6,("Overlay: key_red %d, key_green %d, key_blue %d, key_alpha %d\n",
  		ow->red.value, ow->green.value, ow->blue.value, ow->alpha.value));
  	LOG(6,("Overlay: mask_red %d, mask_green %d, mask_blue %d, mask_alpha %d\n",
  		ow->red.mask, ow->green.mask, ow->blue.mask, ow->alpha.mask));
  
  
  	/*************************
  	 *** setup BES control ***
  	 *************************/
  
  	/* BES global control: setup functions */
  	bi.globctlv = 0;
  
  	/* enable BES */
  	bi.globctlv |= 1 << 0;
  	/* enable colorkeying if requested */
  	if (ow->flags & B_OVERLAY_COLOR_KEY) bi.globctlv |= 1 << 1;
  	/* b3 = 1: distorts right-half of overlay output. Keeping it zero. */
  	/* colorspace is YV12, I420 or YUY2 (no RV15 or RV16) */
  	bi.globctlv |= 0 << 5;
  
  	/* enable auto-alternating hardware buffers if alternating buffers is enabled (NM2160) */
  	bi.globctlv |= 1 << 8;
  	/* disable capture */
  	bi.globctlv |= 1 << 13;
  	/* capture: display one buffer (no alternating buffers) */
  	bi.globctlv |= 0 << 14;
  	/* capture: display frame (no field) */
  	bi.globctlv |= 0 << 15;
  
  	/* BTW: horizontal and vertical filtering are always turned on in NM hardware. */
  
  
  	/*************************************
  	 *** sync to BES (Back End Scaler) ***
  	 *************************************/
  
  	/* Make sure reprogramming the BES completes before the next retrace occurs,
  	 * to prevent register-update glitches (double buffer feature). */
  
  	//fixme if needed...
  
  
  	/**************************************
  	 *** actually program the registers ***
  	 **************************************/
  
  	if (si->ps.card_type >= NM2097)
  	{
  		/* helper: some cards use pixels to define buffer pitch, others use bytes */
  		uint16 buf_pitch = ob->width;
  
  		/* PCI card */
  		LOG(4,("Overlay: accelerant is programming BES\n"));
  		/* unlock card overlay sequencer registers (b5 = 1) */
  		PCIGRPHW(GENLOCK, (PCIGRPHR(GENLOCK) | 0x20));
  		/* destination rectangle #1 (output window position and size) */
  		PCIGRPHW(HD1COORD1L, ((bi.moi.hcoordv >> 16) & 0xff));
  		PCIGRPHW(HD1COORD2L, (bi.moi.hcoordv & 0xff));
  		PCIGRPHW(HD1COORD21H, (((bi.moi.hcoordv >> 4) & 0xf0) | ((bi.moi.hcoordv >> 24) & 0x0f)));
  		PCIGRPHW(VD1COORD1L, ((bi.moi.vcoordv >> 16) & 0xff));
  		PCIGRPHW(VD1COORD2L, (bi.moi.vcoordv & 0xff));
  		PCIGRPHW(VD1COORD21H, (((bi.moi.vcoordv >> 4) & 0xf0) | ((bi.moi.vcoordv >> 24) & 0x0f)));
  		/* scaling */
  		PCIGRPHW(XSCALEL, (bi.hiscalv & 0xff));
  		PCIGRPHW(XSCALEH, ((bi.hiscalv >> 8) & 0xff));
  		PCIGRPHW(YSCALEL, (bi.viscalv & 0xff));
  		PCIGRPHW(YSCALEH, ((bi.viscalv >> 8) & 0xff));
  		/* inputbuffer #1 origin */
  		/* (we don't program buffer #2 as it's unused.) */
  		if (si->ps.card_type < NM2200)
  		{
  			bi.moi.a1orgv >>= 1;
  			/* horizontal source end does not use subpixelprecision: granularity is 8 pixels */
  			/* notes:
  			 * - correctly programming horizontal source end minimizes used bandwidth;
  			 * - adding 9 below is in fact:
  			 *   - adding 1 to round-up to the nearest whole source-end value
  			       (making SURE we NEVER are a (tiny) bit too low);
  			     - adding 1 to convert 'last used position' to 'number of used pixels';
  			     - adding 7 to round-up to the nearest higher (or equal) valid register
  			       value (needed because of it's 8-pixel granularity). */
  			PCIGRPHW(0xbc, ((((bi.moi.hsrcendv >> 16) + 9) >> 3) - 1));
  		}
  		else
  		{
  			/* NM2200 and later cards use bytes to define buffer pitch */
  			buf_pitch <<= 1;
  			/* horizontal source end does not use subpixelprecision: granularity is 16 pixels */
  			/* notes:
  			 * - programming this register just a tiny bit too low messes up vertical
  			 *   scaling badly (also distortion stripes and flickering are reported)!
  			 * - not programming this register correctly will mess-up the picture when
  			 *   it's partly clipping on the right side of the screen...
  			 * - make absolutely sure the engine can fetch the last pixel needed from
  			 *   the sourcebitmap even if only to generate a tiny subpixel from it!
  			 *   (see remarks for < NM2200 cards regarding programming this register) */
  			PCIGRPHW(0xbc, ((((bi.moi.hsrcendv >> 16) + 17) >> 4) - 1));
  		}
  		PCIGRPHW(BUF1ORGL, (bi.moi.a1orgv & 0xff));
  		PCIGRPHW(BUF1ORGM, ((bi.moi.a1orgv >> 8) & 0xff));
  		PCIGRPHW(BUF1ORGH, ((bi.moi.a1orgv >> 16) & 0xff));
  		/* ??? */
  		PCIGRPHW(0xbd, 0x02);
  		PCIGRPHW(0xbe, 0x00);
  		/* b2 = 0: don't use horizontal mirroring (NM2160) */
  		/* other bits do ??? */
  		PCIGRPHW(0xbf, 0x02);
  		/* ??? */
  	    PCISEQW(0x1c, 0xfb);
      	PCISEQW(0x1d, 0x00);
  		PCISEQW(0x1e, 0xe2);
      	PCISEQW(0x1f, 0x02);
   		/* b1 = 0: disable alternating hardware buffers (NM2160) */
  		/* other bits do ??? */
   		PCISEQW(0x09, 0x11);
  		/* we don't use PCMCIA Zoomed Video port capturing, set 1:1 scale just in case */
  		/* (b6-4 = Y downscale = 100%, b2-0 = X downscale = 100%;
  		 *  downscaling selectable in 12.5% steps on increasing setting by 1) */
  		PCISEQW(ZVCAP_DSCAL, 0x00);
  		/* global BES control */
  		PCIGRPHW(BESCTRL1, (bi.globctlv & 0xff));
  		PCISEQW(BESCTRL2, ((bi.globctlv >> 8) & 0xff));
  
  
  		/**************************
  		 *** setup color keying ***
  		 **************************/
  
  		PCIGRPHW(COLKEY_R, (ow->red.value & ow->red.mask));
  		PCIGRPHW(COLKEY_G, (ow->green.value & ow->green.mask));
  		PCIGRPHW(COLKEY_B, (ow->blue.value & ow->blue.mask));
  
  
  		/*************************
  		 *** setup misc. stuff ***
  		 *************************/
  
  		/* setup brightness to be 'neutral' (two's complement number) */
  		PCIGRPHW(BRIGHTNESS, 0x00);
  
  		/* setup inputbuffer #1 pitch including slopspace */
  		/* (we don't program the pitch for inputbuffer #2 as it's unused.) */
  		PCIGRPHW(BUF1PITCHL, (buf_pitch & 0xff));
  		PCIGRPHW(BUF1PITCHH, ((buf_pitch >> 8) & 0xff));
  	}
  	else
  	{
  		/* ISA card. Speed required, so:
  		 * program entire sequence in kerneldriver in one context switch! */
  		LOG(4,("Overlay: kerneldriver programs BES\n"));
  
  		/* complete BES info struct... */
  		bi.card_type = si->ps.card_type;
  		bi.colkey_r = (ow->red.value & ow->red.mask);
  		bi.colkey_g = (ow->green.value & ow->green.mask);
  		bi.colkey_b = (ow->blue.value & ow->blue.mask);
  		bi.ob_width = ob->width;
  		bi.move_only = false;
  		/* ... and call kerneldriver to program the BES */
  		bi.magic = NM_PRIVATE_DATA_MAGIC;
  		ioctl(fd, NM_PGM_BES, &bi, sizeof(bi));
  	}
  
  	/* note that overlay is in use (for nm_bes_move_overlay()) */
  	si->overlay.active = true;
  
  	return B_OK;
  }
  
  status_t nm_release_bes()
  {
  	/* setup BES control: disable scaler */
  	if (si->ps.card_type >= NM2097)
  	{
  		/* PCI card */
  		PCIGRPHW(BESCTRL1, 0x02);
  		PCISEQW(BESCTRL2, 0xa0);
  	}
  	else
  	{
  		/* ISA card */
  		ISAGRPHW(BESCTRL1, 0x02);
  		ISASEQW(BESCTRL2, 0xa0);
  	}
  
  	/* note that overlay is not in use (for nm_bes_move_overlay()) */
  	si->overlay.active = false;
  
  	return B_OK;
  }
  