File:  [DVB] / margi2 / osd.c
Revision 1.2: download - view: text, annotated - select for diffs
Wed Apr 25 15:31:09 2001 UTC (23 years ago) by mocm
Branches: MAIN
CVS tags: HEAD
- various fixes
- more support for DVB API
- support for setting video stream type (works with ntuxplayer and AC3 playback)
- started with support for separate writing in audio and video buffer

/* 
    osd.c

    Copyright (C) Christian Wolff for convergence integrated media.

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

     ////////////////////////////////////////////////////////////////
    //                                                            //
   //  Functions to Draw on the On Screen Display of the L64021  //
  //  CLUT-Mode with 2, 4, or 8 bit per pixel, up to 720*576    //
 //                                                            //
////////////////////////////////////////////////////////////////
// OSD Pixel Aspect Ratio:
// CCIR601 525 Lines (NTSC,PAL-M): 11/10 (100*100 appears as 100*110)
// CCIR601 625 Lines (PAL):        11/12 (100*100 appears as 100*91.6)
//
// OSD functions for external use:
//   int OSDOpen(struct cvdv_cards *card);
//   int OSDClose(struct cvdv_cards *card);
//   int OSDQuery(struct cvdv_cards *card, int *x0, int *y0, int *x1, int *y1, int *aspx, int *aspy);
//   int OSDStartPicture(struct cvdv_cards *card, int left, int top, int width, int height, int bit, int mix);
//   void OSDShow(struct cvdv_cards *card);
//   void OSDHide(struct cvdv_cards *card);
//   void OSDClear(struct cvdv_cards *card);
//   void OSDFill(struct cvdv_cards *card, int col);
//   int OSDSetColor(struct cvdv_cards *card, int num, int R, int G, int B, int mix, int trans);
//   int OSDSetPixel(struct cvdv_cards *card, int x, int y, int col);
//   int OSDGetPixel(struct cvdv_cards *card, int x, int y);
//   int OSDSetRow(struct cvdv_cards *card, int x0, int y, int x1, u8 *data);
//   int OSDFillRow(struct cvdv_cards *card, int x0, int y, int x1, int col);
//   void OSDLine(struct cvdv_cards *card, int x0, int y0, int x1, int y1, int col);
//
// Return codes: (unless otherwise specified)
//    0: OK
//   -1: Range error
//   -2: OSD not open
//

#define __NO_VERSION__

#include "osd.h"
#include "dram.h"
#include "l64021.h"

 // Builds a 4-word picture header in buf
// returns number of words in pixel field on success, -1 on error
int OSDHeader(u16 * buf,	// 4 words
	      int *bit,		// bit per pixel: 2, 4, or 8
	      int *startrow,	// position of our block, 
	      int *stoprow,	// row: 0..313
	      int *startcol,	// col: 0..864
	      int *stopcol,	//
	      int *mix,		// opacity for mixed pixel, 0..15 (0%..94% resp.)
	      int nopal)
{				// 1: use previous palette
	int count;
	if (buf != NULL) {
		if (*bit == 8)
			*bit = 1;
		else if (*bit == 2)
			*bit = 0;
		else
			*bit = 2;
		if (*startrow < 0)
			*startrow = 0;
		if (*startrow > 312)
			*startrow = 312;
		if (*stoprow <= *startrow)
			*stoprow = *startrow + 1;
		if (*stoprow > 313)
			*stoprow = 313;
		if (*startcol < 0)
			*startcol = 0;
		if (*startcol > 863)
			*startcol = 863;
		if (*stopcol <= *startcol)
			*stopcol = *startcol + 2;
		if (*stopcol > 864)
			*stopcol = 864;
		if ((*stopcol - *startcol + 1) & 1)
			(*stopcol)--;
		if (*mix < 0)
			*mix = 0;
		if (*mix > 15)
			*mix = 15;
		buf[0] = ((*bit << 14) & 0x8000) | (*startrow & 0x01FF);
		buf[1] =
		    ((*mix << 12) & 0xF000) | ((*bit << 11) & 0x0800) |
		    ((nopal) ? 0x0400 : 0x0000) | (*stoprow & 0x01FF);
		buf[2] = *startcol & 0x03FF;
		buf[3] = *stopcol & 0x03FF;
		count =
		    (*stoprow - *startrow + 1) * (*stopcol - *startcol +
						  1);
		if (*bit == 1) {
			count =
			    ((count >> 3) + ((count & 0x07) ? 1 : 0)) << 2;
			*bit = 8;
		} else if (*bit == 0) {
			count =
			    ((count >> 5) + ((count & 0x1F) ? 1 : 0)) << 2;
			*bit = 2;
		} else if (*bit == 2) {
			count =
			    ((count >> 4) + ((count & 0x0F) ? 1 : 0)) << 2;
			*bit = 4;
		}
		return count;	// word count of pixel data
	} else
		return -1;
}

// enables OSD mode
int OSDShow(struct cvdv_cards *card)
{
	if (card->OSD.open) {
		DecoderMaskByte(card, 0x109, 0x03, 0x01);
		DecoderDelByte(card, 0x112, 0x10);	// no filter
		return 0;
	} else
		return -2;
}

// disables OSD mode
int OSDHide(struct cvdv_cards *card)
{
	if (card->OSD.open) {
		DecoderMaskByte(card, 0x109, 0x03, 0x00);
		return 0;
	} else
		return -2;
}

// creates an empty picture in the memory of the card
// ONLY ONE PICTURE PER CARD!
// maximum sizes:     NTSC: 720*525  PAL: 720*576
// maximum positions: NTSC: 858*525  PAL: 864*625
// returns 0 on success, -1 on DRAM allocation error
int OSDStartPicture(struct cvdv_cards *card, int left, int top, int width,
		    int height, int bit, int mix)
{
	u16 TermHeader[] = { 0x01FF, 0x05FF, 0x0000, 0x0000 };
	u16 header[4];
	int size, pixelsize, palsize, frametop, startrow, stoprow,
	    startcol, stopcol;

	if (card->OSD.open)
		return -2;
	if (top & 1) {
		card->OSD.evenfirst = 0;
		card->OSD.evenheight = height / 2;
		card->OSD.oddheight = height - card->OSD.evenheight;
	} else {
		card->OSD.evenfirst = 1;
		card->OSD.oddheight = height / 2;
		card->OSD.evenheight = height - card->OSD.oddheight;
	}

	// Setting the picture for the lines in the even field
	frametop = top / 2;
	startrow = frametop;
	stoprow = frametop + card->OSD.evenheight - 1;
	startcol = left;
	stopcol = left + width - 1;
	pixelsize =
	    OSDHeader(header, &bit, &startrow, &stoprow, &startcol,
		      &stopcol, &mix, 0);
	card->OSD.evenheight = stoprow - startrow + 1;
	card->OSD.bpp = bit;
	if (bit == 8)
		palsize = 256;
	else if (bit == 2)
		palsize = 4;
	else
		palsize = 16;
	size = 8 + palsize + pixelsize;
	card->OSD.evenmem = DRAMAlloc(card, size, 32);
	if (card->OSD.evenmem == BLANK)
		return -1;
	card->OSD.evendata = card->OSD.evenmem;
	card->OSD.evenpalette = card->OSD.evendata + 4;
	card->OSD.evenbitmap = card->OSD.evenpalette + palsize;
	card->OSD.eventerm = card->OSD.evenbitmap + pixelsize;
	DecoderWriteWord(card, 0x110, (u16) (card->OSD.evendata >> 5));
	DRAMWriteWord(card, card->OSD.evendata, 4, header, 0);
	DRAMFillByte(card, card->OSD.evenpalette,
		     (palsize + pixelsize) * 2, 0x00);
	DRAMWriteWord(card, card->OSD.eventerm, 4, TermHeader, 0);

	// Setting the picture for the lines in the odd frame
	frametop += card->OSD.evenfirst;
	startrow = frametop;
	stoprow = frametop + card->OSD.oddheight - 1;
	pixelsize =
	    OSDHeader(header, &bit, &startrow, &stoprow, &startcol,
		      &stopcol, &mix, 0);
	card->OSD.oddheight = stoprow - startrow + 1;
	size = 8 + palsize + pixelsize;
	card->OSD.oddmem = DRAMAlloc(card, size, 32);
	if (card->OSD.oddmem == BLANK)
		return -1;
	card->OSD.odddata = card->OSD.oddmem;
	card->OSD.oddpalette = card->OSD.odddata + 4;
	card->OSD.oddbitmap = card->OSD.oddpalette + palsize;
	card->OSD.oddterm = card->OSD.oddbitmap + pixelsize;
	DecoderWriteWord(card, 0x10E, (u16) (card->OSD.odddata >> 5));
	DRAMWriteWord(card, card->OSD.odddata, 4, header, 0);
	DRAMFillByte(card, card->OSD.oddpalette, (palsize + pixelsize) * 2,
		     0x00);
	DRAMWriteWord(card, card->OSD.oddterm, 4, TermHeader, 0);

	// Update of the picture dimensions  
	card->OSD.width = stopcol - startcol + 1;
	card->OSD.height = card->OSD.evenheight + card->OSD.oddheight;
	card->OSD.open = 1;

	MDEBUG(1,": OSD Open %dX%d, %d bit, mem 0x%08X/0x%08X\n",
	       card->OSD.width, card->OSD.height, card->OSD.bpp,
	       card->OSD.evendata, card->OSD.odddata);
	return 0;
}

// Disables OSD and releases the buffers
// returns 0 on success, 1 on "not open"
int OSDClose(struct cvdv_cards *card)
{
	if (card->OSD.open) {
		OSDHide(card);
		DRAMFree(card, card->OSD.evenmem);
		DRAMFree(card, card->OSD.oddmem);
		card->OSD.open = 0;
		return 0;
	} else
		return -2;
}

// Opens OSD with this size and bit depth
// returns 0 on success, 1 on DRAM allocation error, 2 on "already open"
int OSDOpen(struct cvdv_cards *card, int x0, int y0, int x1, int y1,
	    int bit, int mix)
{
	int ret;
	if (card->OSD.open)
		OSDClose(card);
	if (bit < 0)
		bit = 8;
	else if (bit < 2)
		bit = 2;
	else if (bit < 4)
		bit = 4;
	else
		bit = 8;
	if (x0 < 0)
		x0 = 0;
	if (x1 < 0)
		x1 = 720 - 1;
	if (x1 < x0)
		x1 = x0;
	if (y0 < 0)
		y0 = 0;
	if (y1 < 0)
		y1 = 576 - 1;
	if (y1 < y0)
		y1 = y0;
	if ((x1 + 1) > 720)
		x1 = 720 - 1;
	if (x0 > x1)
		x0 = x1;
	if (CCIR601Lines(card->videomode) == 625) {	// PAL
		if ((y1 + 1) > 576)
			y1 = 576 - 1;
		if (y0 > y1)
			y0 = y1;
		if (!
		    (ret =
		     OSDStartPicture(card, 134 + x0, 48 + y0, x1 - x0 + 1,
				     y1 - y0 + 1, bit, mix)))
			card->OSD.aspectratio = 12;	// pixel aspect ratio 12/11
	} else {		// NTSC
		if ((y1 + 1) > 484)
			y1 = 484 - 1;
		if (y0 > y1)
			y0 = y1;
		if (!
		    (ret =
		     OSDStartPicture(card, 126 + x0, 44 + y0, x1 - x0 + 1,
				     y1 - y0 + 1, bit, mix)))
			card->OSD.aspectratio = 10;	// pixel aspect ratio 10/11
	}
	return ret;
}

// fills parameters with the picture dimensions and the pixel aspect ratio (aspy=11)
int OSDQuery(struct cvdv_cards *card, int *x0, int *y0, int *x1, int *y1,
	     int *aspx)
{
	if (!card->OSD.open)
		return -2;
	*x0 = 0;
	*x1 = card->OSD.width - 1;
	*y0 = 0;
	*y1 = card->OSD.height - 1;
	*aspx = card->OSD.aspectratio;
	return 0;
}

// Sets all pixel to color 0
int OSDClear(struct cvdv_cards *card)
{
	if (!card->OSD.open)
		return -2;
	DRAMFillByte(card, card->OSD.oddbitmap,
		     (int) (card->OSD.oddterm - card->OSD.oddbitmap) * 2,
		     0x00);
	DRAMFillByte(card, card->OSD.evenbitmap,
		     (int) (card->OSD.eventerm - card->OSD.evenbitmap) * 2,
		     0x00);
	return 0;
}

// Sets all pixel to color <col>
int OSDFill(struct cvdv_cards *card, int col)
{
	u8 color;
	if (!card->OSD.open)
		return -2;
	if (card->OSD.bpp == 8) {
		color = col & 0xFF;
	} else if (card->OSD.bpp == 4) {
		color = (col & 0xF);
		color |= (color << 4);
	} else if (card->OSD.bpp == 2) {
		color = (col & 0x03);
		for (col = 1; col <= 3; col++)
			color |= (color << 2);
	} else
		color = 0x00;
	DRAMFillByte(card, card->OSD.oddbitmap,
		     (int) (card->OSD.oddterm - card->OSD.oddbitmap) * 2,
		     color);
	DRAMFillByte(card, card->OSD.evenbitmap,
		     (int) (card->OSD.eventerm - card->OSD.evenbitmap) * 2,
		     color);
	return 0;
}

// converts RGB(8 bit) to YCrCb(OSD format)
// mix: 0=opacity 100% 1=opacity at mix value
// trans: 0=mix bit applies 1=opacity 0%
// returns word in OSD palette format
u16 OSDColor(u8 R, u8 G, u8 B, int mix, int trans)
{
	u16 Y, Cr, Cb;
	Y = R * 77 + G * 150 + B * 29;	// Luma=0.299R+0.587G+0.114B 0..65535
	Cb = 2048 + B * 8 - (Y >> 5);	// Cr 0..4095
	Cr = 2048 + R * 10 - (Y >> 5);	// Cb 0..4095
	return ((trans) ? 0 :	// transparent pixel
		(Y & 0xFC00) |	// Luma 0..63
		((mix) ? 0x0100 : 0x0000) |	// Opacity applies
		((Cb >> 4) & 0x00F0) |	// Cb 0..15
		((Cr >> 8) & 0x000F)	// Cr 0..15
	    );
}

// set palette entry <num> to <r,g,b>, <mix> and <trans> apply
// R,G,B: 0..255
// RGB=1: R=Red, G=Green, B=Blue  RGB=0: R=Y G=Cb B=Cr
// mix=0, trans=0: pixel opacity 100% (only OSD pixel shows)
// mix=1, trans=0: pixel opacity as specified in header
// trans=1: pixel opacity 0% (only video pixel shows)
// returns 0 on success, 1 on error
int OSDSetColor(struct cvdv_cards *card, int num, int R, int G, int B,
		int YUV, int mix, int trans)
{
	u16 burst[4];		// minimal memory unit
	u32 addr;
	u16 color;
	if (!card->OSD.open)
		return -2;
	if (R < 0)
		R = 0;
	if (R > 255)
		R = 255;
	if (G < 0)
		G = 0;
	if (G > 255)
		G = 255;
	if (B < 0)
		B = 0;
	if (B > 255)
		B = 255;
	if ((num >= 0) && (num < (1 << card->OSD.bpp))) {
		if (num==0) MDEBUG(4,"OSD SetColor num=%d, R=%d, G=%d, B=%d, YUV=%d, mix=%d, trans=%d\n",
				   num,R,G,B,YUV,mix,trans);
		color = ((YUV)
			 ? ((trans) ? 0 : ((R << 8) & 0xFC00) |
			    ((mix) ? 0x0100 : 0x0000) | (G & 0x00F0) |
			    ((B >> 4) & 0x000F)) : OSDColor(R, G, B, mix,
							    trans));

		addr = card->OSD.oddpalette + num;
		DRAMReadWord(card, addr & ~3, 4, burst, 0);
		burst[addr & 3] = color;
		DRAMWriteWord(card, addr & ~3, 4, burst, 0);

		addr = card->OSD.evenpalette + num;
		DRAMReadWord(card, addr & ~3, 4, burst, 0);
		burst[addr & 3] = color;
		DRAMWriteWord(card, addr & ~3, 4, burst, 0);

		return 0;
	} else
		return -1;
}

// Set a number of entries in the palette
// sets the entries "firstcolor" through "lastcolor" from the array "data"
// data has 4 byte for each color:
// R,G,B, and a transparency value: 0->tranparent, 1..254->mix, 255->no mix
int OSDSetPalette(struct cvdv_cards *card, int firstcolor, int lastcolor,
		  u8 * data)
{
	u16 burst[4];		// minimal memory unit
	u32 addr;
	u16 color;
	int num, i = 0;
	if (!card->OSD.open)
		return -2;
	for (num = firstcolor; num <= lastcolor; num++)
		if ((num >= 0) && (num < (1 << card->OSD.bpp))) {
			color =
			    OSDColor(data[i], data[i + 1], data[i + 2],
				     ((data[i + 3] < 255) ? 1 : 0),
				     ((data[i + 3] == 0) ? 1 : 0));
			i += 4;

			addr = card->OSD.oddpalette + num;
			DRAMReadWord(card, addr & ~3, 4, burst, 0);
			burst[addr & 3] = color;
			DRAMWriteWord(card, addr & ~3, 4, burst, 0);

			addr = card->OSD.evenpalette + num;
			DRAMReadWord(card, addr & ~3, 4, burst, 0);
			burst[addr & 3] = color;
			DRAMWriteWord(card, addr & ~3, 4, burst, 0);
		}
	return 0;
}

// Sets transparency of mixed pixel (0..15)
int OSDSetTrans(struct cvdv_cards *card, int trans)
{
	u16 burst[4];		// minimal memory unit
	if (!card->OSD.open)
		return -2;
	trans &= 0x000F;
	DRAMReadWord(card, card->OSD.evendata, 4, burst, 0);
	burst[1] = (burst[1] & 0x0FFF) | (trans << 12);
	DRAMWriteWord(card, card->OSD.evendata, 4, burst, 0);

	DRAMReadWord(card, card->OSD.odddata, 4, burst, 0);
	burst[1] = (burst[1] & 0x0FFF) | (trans << 12);
	DRAMWriteWord(card, card->OSD.odddata, 4, burst, 0);
	return 0;
}

// sets pixel <x>,<y> to color number <col>
// returns 0 on success, 1 on error
int OSDSetPixel(struct cvdv_cards *card, int x, int y, int col)
{
	u16 burst[4];		// minimal memory unit od DRAM
	u32 addr;
	int offset, ppw, pos, shift, height, posmask;
	u16 mask;

	if (!card->OSD.open)
		return -2;
	if ((y & 1) == card->OSD.evenfirst) {	// even or odd frame?
		addr = card->OSD.oddbitmap;
		height = card->OSD.oddheight;
	} else {
		addr = card->OSD.evenbitmap;
		height = card->OSD.evenheight;
	}
	y >>= 1;
	if ((x >= 0) && (x < card->OSD.width) && (y >= 0) && (y < height)) {	// clipping
		ppw =
		    ((card->OSD.bpp == 4) ? 2 : ((card->OSD.bpp == 8) ? 1 : 3));	// OK, 4-(ln(bpp)/ln(2)) would have worked, too...
		pos = x + y * card->OSD.width;	// pixel number in bitfield
		addr += (pos >> ppw);	// 21 bit address of word with our pixel
		offset = addr & 3;	// offset in burst
		addr &= ~3;	// 21 bit burst address
		posmask = (1 << ppw) - 1;	// mask for position inside word
		shift = ((posmask - (pos & posmask)) << (4 - ppw));	// pixel shift inside word
		mask = (1 << (1 << (4 - ppw))) - 1;	// pixel mask
		DRAMReadWord(card, addr, 4, burst, 0);	// get the burst with our pixel...
		burst[offset] =
		    (burst[offset] & ~(mask << shift)) | ((col & mask) <<
							  shift);
		DRAMWriteWord(card, addr, 4, burst, 0);	// ...and write it back
		return 0;
	} else
		return -1;
}

// returns color number of pixel <x>,<y>,  or -1
int OSDGetPixel(struct cvdv_cards *card, int x, int y)
{
	u16 burst[4];		// minimal memory unit
	u32 addr;
	int offset, ppw, pos, shift, height, posmask;
	u16 mask;

	if (!card->OSD.open)
		return -2;
	if ((y & 1) == card->OSD.evenfirst) {	// even or odd frame?
		addr = card->OSD.oddbitmap;
		height = card->OSD.oddheight;
	} else {
		addr = card->OSD.evenbitmap;
		height = card->OSD.evenheight;
	}
	y >>= 1;
	if ((x >= 0) && (x < card->OSD.width) && (y >= 0) && (y < height)) {	// clipping
		ppw =
		    ((card->OSD.bpp == 4) ? 2 : ((card->OSD.bpp == 8) ? 1 : 3));	// OK, 4-(ln(bpp)/ln(2)) would have worked, too...
		pos = x + y * card->OSD.width;	// pixel number in bitfield
		addr += (pos >> ppw);	// 21 bit address of word with our pixel
		offset = addr & 3;	// offset in burst
		addr &= ~3;	// 21 bit burst address
		posmask = (1 << ppw) - 1;	// mask for position inside word
		shift = ((posmask - (pos & posmask)) << (4 - ppw));	// pixel shift inside word
		mask = (1 << (1 << (4 - ppw))) - 1;	// pixel mask
		DRAMReadWord(card, addr, 4, burst, 0);	// get the burst with our pixel...
		return (burst[offset] >> shift) & mask;	// ...and return it's value 
	} else
		return -1;
}

// fills pixels x0,y through  x1,y with the content of data[]
// returns 0 on success, -1 on clipping all pixel
int OSDSetRow(struct cvdv_cards *card, int x0, int y, int x1, u8 * data)
{
	u16 burst[4];		// minimal memory unit
	u32 addr, addr1, bitmap;
	int offset, offset1, ppw, pos, pos1, shift, shift0, shift1,
	    shiftstep, height, bpp, x, i, endburst, endword;
	u16 mask, posmask;

	if (!card->OSD.open)
		return -2;
	if ((y & 1) == card->OSD.evenfirst) {
		bitmap = card->OSD.oddbitmap;
		height = card->OSD.oddheight;
	} else {
		bitmap = card->OSD.evenbitmap;
		height = card->OSD.evenheight;
	}
	y >>= 1;
	if ((y >= 0) && (y < height)) {
		i = 0;
		if (x0 > x1) {
			x = x1;
			x1 = x0;
			x0 = x;
		}
		if ((x0 >= card->OSD.width) || (x1 < 0))
			return -1;
		if (x0 < 0) {
			i -= x0;
			x0 = 0;
		}
		if (x1 >= card->OSD.width)
			x1 = card->OSD.width - 1;
		bpp = card->OSD.bpp;	// bits per pixel
		ppw = ((bpp == 4) ? 2 : ((bpp == 8) ? 1 : 3));	// positional parameter
		mask = (1 << bpp) - 1;	// mask for one pixel
		posmask = (1 << ppw) - 1;	// mask for position inside word

		pos = x0 + (y * card->OSD.width);	// pixel number of first pixel
		pos1 = pos + x1 - x0;	// pixel number of last pixel
		shift0 = ((posmask - (pos & posmask)) << (4 - ppw));
		shift1 = ((posmask - (pos1 & posmask)) << (4 - ppw));
		shiftstep = 1 << (4 - ppw);

		addr = bitmap + (pos >> ppw);	// DRAM address of word with first pixel
		addr1 = bitmap + (pos1 >> ppw);	//  "      "    "    "   "   last    "
		offset = (int) (addr & 3);	// word position inside burst
		offset1 = (int) (addr1 & 3);	// number of last word in the last burst
		addr &= ~3;	// burst address
		addr1 &= ~3;	// burst address of last pixel

		endburst = (addr1 != addr);	// end in other burst
		endword = (offset1 != offset);	// end in other word

		// read old content of first burst if the row start after the beginning or 
		// end before the end of the first burst
		if (offset || (pos & posmask) ||
		    (!endburst
		     && ((offset1 != 3)
			 || ((pos1 & posmask) != posmask)))) {
			DRAMReadWord(card, addr, 4, burst, 0);
		}
		// End beyond or at the end of this word?
		if (endburst || endword || ((pos1 & posmask) == posmask)) {
			// Fill first word
			for (shift = shift0; shift >= 0; shift -= shiftstep) {	// bit position inside word
				burst[offset] =
				    (burst[offset] & ~(mask << shift)) |
				    ((data[i++] & mask) << shift);
			}
			if (endburst || endword) {	// Any more words to fill?
				shift0 = posmask << (4 - ppw);	// from here on, we start at the beginning of each word
				offset++;	// fill the rest of the burst
				if (endburst) {	// end not in this burst?
					while (offset <= 3) {	// fill remaining words
						burst[offset] = 0x0000;	// clear first
						for (shift = shift0;
						     shift >= 0;
						     shift -= shiftstep) {
							burst[offset] |=
							    ((data
							      [i++] & mask)
							     << shift);
						}
						offset++;
					}
					DRAMWriteWord(card, addr, 4, burst, 0);	// write first burst
					addr += 4;	// go on to the next burst
					while (addr < addr1) {	// all bursts between start and end burst
						for (offset = 0;
						     offset <= 3; offset++) {	// 4 words per burst
							burst[offset] = 0x0000;	// clear first
							for (shift =
							     shift0;
							     shift >= 0;
							     shift -=
							     shiftstep) {
								burst
								    [offset]
								    |=
								    ((data
								      [i++]
								      &
								      mask)
								     <<
								     shift);
							}
						}
						DRAMWriteWord(card, addr,
							      4, burst, 0);	// write full burst
						addr += 4;	// next burst
					}
					offset = 0;
					if ((offset1 < 3) || shift1) {	// does the row ends before the end of the burst?
						DRAMReadWord(card, addr, 4,
							     burst, 0);	// then we have to read the old content
					}
				}
				while (offset < offset1) {	// end not in this word
					burst[offset] = 0x0000;	// clear first
					for (shift = shift0; shift >= 0;
					     shift -= shiftstep) {
						burst[offset] |=
						    ((data[i++] & mask) <<
						     shift);
					}
					offset++;
				}
				for (shift = shift0; shift >= shift1;
				     shift -= shiftstep) {	// last word
					burst[offset] =
					    (burst[offset] &
					     ~(mask << shift)) |
					    ((data[i++] & mask) << shift);
				}
			}
		} else {	// row starts and ends in one word
			for (shift = shift0; shift >= shift1; shift -= shiftstep) {	// bit position inside word
				burst[offset] =
				    (burst[offset] & ~(mask << shift)) |
				    ((data[i++] & mask) << shift);
			}
		}
		DRAMWriteWord(card, addr, 4, burst, 0);	// write only/last burst
		return 0;
	} else
		return -1;
}

// fills pixels x0,y0 through  x1,y1 with the content of data[]
// inc contains the width of one line in the data block,
// inc<=0 uses blockwidth as linewidth
// returns 0 on success, -1 on clipping all pixel
int OSDSetBlock(struct cvdv_cards *card, int x0, int y0, int x1, int y1,
		int inc, u8 * data)
{
	int i, w = x1 - x0 + 1, ret = 0;
	if (inc > 0)
		w = inc;
	for (i = y0; i <= y1; i++) {
		ret |= OSDSetRow(card, x0, i, x1, data);
		data += w;
	}
	return ret;
}

// fills pixels x0,y through  x1,y with the color <col>
// returns 0 on success, -1 on clipping all pixel
int OSDFillRow(struct cvdv_cards *card, int x0, int y, int x1, int col)
{
	u16 burst[4];		// minimal memory unit
	u32 addr, addr1, bitmap;
	int offset, offset1, ppw, pos, pos1, shift, shift0, shift1,
	    shiftstep, height, bpp, x, i, endburst, endword;
	u16 mask, posmask;

	if (!card->OSD.open)
		return -2;
	if ((y & 1) == card->OSD.evenfirst) {
		bitmap = card->OSD.oddbitmap;
		height = card->OSD.oddheight;
	} else {
		bitmap = card->OSD.evenbitmap;
		height = card->OSD.evenheight;
	}
	y >>= 1;
	if ((y >= 0) && (y < height)) {
		i = 0;
		if (x0 > x1) {
			x = x1;
			x1 = x0;
			x0 = x;
		}
		if ((x0 >= card->OSD.width) || (x1 < 0))
			return -1;
		if (x0 < 0) {
			i -= x0;
			x0 = 0;
		}
		if (x1 >= card->OSD.width)
			x1 = card->OSD.width - 1;
		bpp = card->OSD.bpp;	// bits per pixel
		ppw = ((bpp == 4) ? 2 : ((bpp == 8) ? 1 : 3));	// positional parameter
		mask = (1 << bpp) - 1;	// mask for one pixel
		posmask = (1 << ppw) - 1;	// mask for position inside word

		pos = x0 + (y * card->OSD.width);	// pixel number of first pixel
		pos1 = pos + x1 - x0;	// pixel number of last pixel
		shift0 = ((posmask - (pos & posmask)) << (4 - ppw));
		shift1 = ((posmask - (pos1 & posmask)) << (4 - ppw));
		shiftstep = 1 << (4 - ppw);

		addr = bitmap + (pos >> ppw);	// DRAM address of word with first pixel
		addr1 = bitmap + (pos1 >> ppw);	//  "      "    "    "   "   last    "
		offset = (int) (addr & 3);	// word position inside burst
		offset1 = (int) (addr1 & 3);	// number of last word in the last burst
		addr &= ~3;	// burst address
		addr1 &= ~3;	// burst address of last pixel

		endburst = (addr1 != addr);	// end in other burst
		endword = (offset1 != offset);	// end in other word

		// read old content of first burst if the row start after the beginning or 
		// end before the end of the first burst
		if (offset || (pos & posmask) ||
		    (!endburst
		     && ((offset1 != 3)
			 || ((pos1 & posmask) != posmask)))) {
			DRAMReadWord(card, addr, 4, burst, 0);
		}
		if (endburst || endword || ((pos1 & posmask) == posmask)) {	// end beyond or at the end of this word?
			for (shift = shift0; shift >= 0; shift -= shiftstep) {	// bit position inside word
				burst[offset] =
				    (burst[offset] & ~(mask << shift)) |
				    ((col & mask) << shift);
			}
			if (endburst || endword) {
				shift0 = posmask << (4 - ppw);	// from here on, we start at the beginning of each word
				offset++;	// fill the rest of the burst
				if (endburst) {	// end not in this burst?
					while (offset <= 3) {	// fill remaining words
						burst[offset] = 0x0000;	// clear first
						for (shift = shift0;
						     shift >= 0;
						     shift -= shiftstep) {
							burst[offset] |=
							    ((col & mask)
							     << shift);
						}
						offset++;
					}
					DRAMWriteWord(card, addr, 4, burst, 0);	// write first burst
					addr += 4;	// next burst
					while (addr < addr1) {	// write all the bursts between start and end burst
						for (offset = 0;
						     offset <= 3; offset++) {
							burst[offset] =
							    0x0000;
							for (shift =
							     shift0;
							     shift >= 0;
							     shift -=
							     shiftstep) {
								burst
								    [offset]
								    |=
								    ((col
								      &
								      mask)
								     <<
								     shift);
							}
						}
						DRAMWriteWord(card, addr,
							      4, burst, 0);
						addr += 4;
					}
					offset = 0;
					if ((offset1 < 3) || shift1) {	// does the row ends before the end of the burst?
						DRAMReadWord(card, addr, 4,
							     burst, 0);	// then we have to read the old content
					}
				}
				while (offset < offset1) {	// end not in this word
					burst[offset] = 0x0000;
					for (shift = shift0; shift >= 0;
					     shift -= shiftstep) {
						burst[offset] |=
						    ((col & mask) <<
						     shift);
					}
					offset++;
				}
				for (shift = shift0; shift >= shift1;
				     shift -= shiftstep) {
					burst[offset] =
					    (burst[offset] &
					     ~(mask << shift)) | ((col &
								   mask) <<
								  shift);
				}
			}
		} else {	// row starts and ends in one word
			for (shift = shift0; shift >= shift1; shift -= shiftstep) {	// bit position inside word
				burst[offset] =
				    (burst[offset] & ~(mask << shift)) |
				    ((col & mask) << shift);
			}
		}
		DRAMWriteWord(card, addr, 4, burst, 0);
		return 0;
	} else
		return -1;
}

// fills pixels x0,y0 through  x1,y1 with the color <col>
// returns 0 on success, -1 on clipping all pixel
int OSDFillBlock(struct cvdv_cards *card, int x0, int y0, int x1, int y1,
		 int col)
{
	int i, ret = 0;
	for (i = y0; i <= y1; i++)
		ret |= OSDFillRow(card, x0, i, x1, col);
	return ret;
}

// draw a line from x0,y0 to x1,y1 with the color <col>
int OSDLine(struct cvdv_cards *card, int x0, int y0, int x1, int y1,
	    int col)
{
	int ct, ix, iy, ax, ay, dx, dy, off;
#define sgn(a) ((a)?(((a)>0)?1:-1):0)
	if (!card->OSD.open)
		return -2;
	dx = x1 - x0;
	dy = y1 - y0;
	if (dx == 0) {
		if (dy < 0)
			for (iy = y1; iy <= y0; iy++)
				OSDSetPixel(card, x0, iy, col);
		else
			for (iy = y0; iy <= y1; iy++)
				OSDSetPixel(card, x0, iy, col);
	} else if (dy == 0) {
		OSDFillRow(card, x0, y0, x1, col);
	} else {
		ay = 0;
		ax = 0;
		ix = sgn(dx);
		dx = abs(dx);
		iy = sgn(dy);
		dy = abs(dy);
		if (dx < dy) {
			off = dx;
			dx = dy;
			dy = off;
			ay = ix;
			ax = iy;
			ix = 0;
			iy = 0;
		}
		off = dx >> 1;
		ct = 1;
		OSDSetPixel(card, x0, y0, col);
		x1 = x0;
		y1 = y0;
		while (dx >= ct) {
			x0 += ix;
			y0 += ax;
			ct++;
			off += dy;
			if (off > dx) {
				off -= dx;
				x0 += ay;
				y0 += iy;
			}
			if (ax) {
				OSDSetPixel(card, x0, y0, col);
			} else {
				if (y0 != y1) {
					OSDFillRow(card, x1, y1, x0 - ay,
						   col);
					x1 = x0;
					y1 = y0;
				}
			}
		}
		if (!ax)
			OSDFillRow(card, x1, y0, x0, col);
	}
	return 0;
}

LinuxTV legacy CVS <linuxtv.org/cvs>