File:  [DVB] / margi2 / cvdv.c
Revision 1.1: download - view: text, annotated - select for diffs
Sun Dec 3 22:41:14 2000 UTC (23 years, 6 months ago) by cvs
Branches: MAIN
CVS tags: HEAD
Initial revision

/* 
    cvdv.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.
*/

      /////////////////////////////////////////////////////////////////////
     //                                                                 //
    //   Driver for the Convergence Digital Video decoder card (pci)   //
   //   with L64017, L64021, PCM1723, and Bt864/Bt865 chipset         //
  //   (c) Christian Wolff 19990209 for convergence integrated media //
 //                                                                 //
/////////////////////////////////////////////////////////////////////

// Convergence CV2300i
#define __NO_VERSION__

#include "cvdv.h"
#include "i2c.h"

  //////////////////////  
 // global variables //
//////////////////////

// Our major device number
unsigned int major_device_number;


// my little random function for memory test
u16 rnd_seed;
u16 rnd(u16 range)
{				// returns random 0..(range-1) range<=872
	u32 b = 75 * (rnd_seed + 1) - 1;
	rnd_seed = (u16) (b & 0xFFFF);
	return ((b * range) / 0xFFFF) - ((b / 0xFFFF) * range);
}
void rnd_omize(void)
{
	rnd_seed = (u16) jiffies;
}


    /////////////////////////////////////////////
   //                                         //
  //  Controlling the L64021 MPEG-2 Decoder  //
 //                                         //
/////////////////////////////////////////////

int OSDTest(struct cvdv_cards *card)
{
	int i, j, x, y, col, xpos, ypos, x0, y0, x1, y1, aspx, aspy, width,
	    height;
	u8 row[130];

	if (!card->OSD.open)
		return -2;

	//VideoSetBackground(card,2,83,90,249);  // Red
	//VideoSetBackground(card,2,155,53,53);  // Green
	//VideoSetBackground(card,2,35,212,114);  // Blue
	//VideoSetBackground(card,2,4,128,128);  // Black

	OSDQuery(card, &x0, &y0, &x1, &y1, &aspx);
	aspy = 11;
	width = 130;
	height = width * aspy / aspx;
	xpos = ((x1 - x0 + 1) - width) / 2;
	ypos = ((y1 - y0 + 1) - height) / 2;

	OSDShow(card);

	OSDSetColor(card, 0, 0, 0, 0, 1, 0, 1);	// transparent background
	for (i = 0; i < 2; i++) {
		for (j = 0; j < 8; j++) {
			col = 1 + i * 8 + j;
			OSDSetColor(card, col, (j & 2) ? 192 : 0,
				    (j & 4) ? 192 : 0, (j & 1) ? 192 : 0,
				    1, i, 0);
		}
	}

//  for (x=1; x<=16; x++) {
//    OSDFill(card,x);
//  }
//  OSDClear(card);

	for (i = 0; i < 2; i++) {
		for (j = 0; j < 8; j++) {
			col = 1 + i * 8 + j;
			for (x = 0; x < 16; x++)
				row[1 + x + j * 16] = col;
		}
		for (y = 0; y < (height - 2) / 2; y++) {
			OSDSetRow(card, xpos,
				  ypos + 1 + y + i * ((height - 2) / 2),
				  xpos + width - 1, row);
		}
	}

	OSDFillRow(card, xpos, ypos, xpos + width - 1, 8);
	OSDFillRow(card, xpos, ypos + height - 1, xpos + width - 1, 8);
	for (y = ypos; y < ypos + height; y++) {
		OSDSetPixel(card, xpos, y, 8);
		OSDSetPixel(card, xpos + width - 1, y, 8);
	}
	for (x = 0; x < 130; x++) {
		OSDSetPixel(card, x + xpos, x * aspy / aspx + ypos, 8);
		OSDSetPixel(card, width - x + xpos, x * aspy / aspx + ypos,
			    8);
		OSDSetPixel(card, x + xpos,
			    (x >> 3) + ypos + ((height / 2) - 8), 8);
	}
	OSDLine(card, x0, y0, x1, y0, 8);
	OSDLine(card, x1, y0, x1, y1, 8);
	OSDLine(card, x1, y1, x0, y1, 8);
	OSDLine(card, x0, y1, x0, y0, 8);
	OSDLine(card, x0, y0, x1, y1, 8);
	OSDLine(card, x0, y1, x1, y0, 8);

//  OSDShow(card);

	return 0;
}


void SetVideoSystem(struct cvdv_cards *card)
{
	// set the hsync and vsync generators in the L64017 according to the video standard
	switch (card->videomode) {
	case PAL:		// 864*625*50Hz = 27MHz, 25fps
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x41 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x96);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0x13);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x54);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_PAL);
		break;
	case PALN:
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0xa1 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x96);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0x13);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x54);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_PAL);
		break;

	case PALNc:
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x81 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x8c);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x28);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xed);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_PAL);

		break;

	case NTSC:		// 858*525*59.94006Hz = 27MHz, 29.97fps
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x01 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x1c);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x3e);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0xf8);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe0);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_NTSC);

		break;

	case PALM:
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x01 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x4e);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x4a);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe1);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_PAL);

		break;

	case NTSC60:		// 857*525*60.010002Hz = 27MHz, 30fps
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x21 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x1c);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x3e);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0xf8);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe0);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_NTSC);
		break;

	case PALM60:
		I2CWrite(card, card->i2c_addr, CS_CONTROL0, 0x61 | 0x0a);
		I2CWrite(card, card->i2c_addr, CS_CONTROL1, 0x04);
		I2CWrite(card, card->i2c_addr, CS_SC_AMP, 0x15);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH0, 0x4e);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH1, 0x4a);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH2, 0xe1);
		I2CWrite(card, card->i2c_addr, CS_SC_SYNTH3, 0x43);
		write_indexed_register(card, IIO_VIDEO_CONTROL1, VMS_PAL);

		break;

	case PAL60:
		break;
	}
	// set the pixel generators according to the video standard
	L64021Setup(card);
}

int SetVideoAttr(struct cvdv_cards *card, u16 vattr)
{
	u8 video_compression_mode;
	u8 tv_system;
	u8 aspect_ratio;
	u8 display_mode;
	u8 line_21_switch_1;
	u8 line_21_switch_2;
	u8 source_picture_resolution;
	u8 source_picture_letterboxed;
	u8 reserved;
	u8 film_camera_mode;
	u16 hsize, vsize;
	if (vattr != card->lastvattr) {
		video_compression_mode = (vattr >> 14) & 0x03;
		tv_system = (vattr >> 12) & 0x03;
		aspect_ratio = (vattr >> 10) & 0x03;
		display_mode = (vattr >> 8) & 0x03;
		line_21_switch_1 = (vattr >> 7) & 0x01;
		line_21_switch_2 = (vattr >> 6) & 0x01;
		source_picture_resolution = (vattr >> 3) & 0x07;
		source_picture_letterboxed = (vattr >> 2) & 0x01;
		reserved = (vattr >> 1) & 0x01;
		film_camera_mode = (vattr >> 0) & 0x01;
		card->videomode =
			((tv_system == 0) ? NTSC : ((tv_system == 1) ? 
						    PAL : PAL));	
		SetVideoSystem(card);
		hsize =
			((source_picture_resolution == 0) ? 720
			 : ((source_picture_resolution == 1) ? 702 : 352));
		vsize = ((source_picture_resolution == 3)
			 ? ((tv_system == 0) ? 240 : 288)
			 : ((tv_system == 0) ? 480 : 576));
		if (DecoderOpen
		    (card, hsize, vsize, ((aspect_ratio) ? 3 : 2),
		     ((video_compression_mode) ? 0 : 1),
		     source_picture_letterboxed, tv_system)) {	
			printk(KERN_ERR LOGNAME
			       ": Video Decoder Open failed: On-card memory insufficient for frame stores\n");
		}
		card->lastvattr = vattr;
	} else {
		printk(KERN_ERR LOGNAME
		       ": Video attribute not set, equal to previous one.\n");
	}
	return 0;
}

int SetAudioAttr(struct cvdv_cards *card, u16 aattr)
{
	u8 audio_coding_mode;
	u8 multichannel_extension;
	u8 audio_type;
	u8 audio_application_mode;
	u8 quantization_drc;
	u8 fs;
	u8 reserved;
	u8 num_audio_ch;
	if (aattr) {
		if (aattr != card->lastaattr) {
			audio_coding_mode = (aattr >> 13) & 0x07;
			multichannel_extension = (aattr >> 12) & 0x01;
			audio_type = (aattr >> 10) & 0x03;
			audio_application_mode = (aattr >> 8) & 0x03;
			quantization_drc = (aattr >> 6) & 0x03;
			fs = (aattr >> 4) & 0x03;
			reserved = (aattr >> 3) & 0x01;
			num_audio_ch = (aattr >> 0) & 0x07;
			switch (audio_coding_mode) {
			case 0:	// AC-3
				card->setup.audioselect = audio_AC3;
				break;
			case 2:	// MPEG Audio
				card->setup.audioselect = audio_MPEG;
				break;
			case 3:	// MPEG Audio with ext.
				card->setup.audioselect = audio_MPEG_EXT;
				break;
			case 4:	// Linear Pulse Code Modulation LPCM
				card->setup.audioselect = audio_LPCM;
				break;
			case 6:	// DTS
				card->setup.audioselect = audio_DTS;
				break;
			case 7:	// SDDS
				card->setup.audioselect = audio_SDDS;
				break;
			}
			DecoderPrepareAudio(card);
			AudioInit(card, ((fs) ? 96 : 48),
				  ((audio_application_mode == 2) ? 1 : 0));
		} else {
			printk(KERN_ERR LOGNAME
			       ": Audio attribute not set, equal to previous one.\n");
		}
	} else {
		card->setup.audioselect = audio_none;
		DecoderPrepareAudio(card);
	}
	card->lastaattr = aattr;
	return 0;
}

int Prepare(struct cvdv_cards *card)
{
	int err, h;
	struct StreamSetup *setup = &card->setup;

	if (!card->ChannelBuffersAllocated) {
			
		DecoderStreamReset(card);
		if (setup->streamtype == stream_none) {
			setup->streamtype = stream_PS; 
		}
		
		if (setup->audioselect == audio_none) {
			setup->audioselect = audio_MPEG;
		}

		DecoderPrepareAudio(card);
		AudioMute(card, 1);
		DecoderPrepareVideo(card);
		VideoSetBackground(card, 1, 0, 0, 0);	// black

		switch (setup->streamtype) {
		default:
		case stream_none:	// unknown stream!
			printk(KERN_ERR LOGNAME
			       ": Video Decoder Prepare failed: unknown stream type\n");
			return -ENODEV;	// not an MPEG stream!
		case stream_ES:	// Elementary Stream
			err = DecoderPrepareES(card);
			break;
		case stream_PES:	// Packetized Elementary Stream
			err = DecoderPreparePES(card);
			break;
		case stream_PS:	// MPEG-1 System Stream / MPEG-2 Program Stream
			err = DecoderPreparePS(card, -1, 0, 0, 0, 0, 0);
			break;
		case stream_DVD:	// DVD Stream
			err = DecoderPreparePS(card, 0, 0, 0, 0, 3, 1);
			break;
		}
		if (err) {	// insufficient memory
			printk(KERN_ERR LOGNAME
			       ": Video Decoder Prepare failed: no kernel memory, please reboot if possible\n");
			CloseCard(card);
			return -ENODEV;
		}
	}

	// Set up the Video Decoder as we have the stream information
	if ((!card->FrameBuffersAllocated)
	    && (card->ChannelBuffersAllocated) && (card->stream.sh.valid)) {
//printk(KERN_DEBUG LOGNAME ": Setup Decoder %d\n",channel);
		//  Automatic PAL/NTSC-switch according to MPEG-Source
		h = card->stream.vsize;
		if (h < 480)
			h *= 2;	// catch quarter sized images
		printk(KERN_INFO LOGNAME ": Video mode: %s\n",
		       ((h == 480) ? "NTSC" : "PAL"));
		card->videomode = ((h == 480) ? NTSC : PAL);
		SetVideoSystem(card);
		// Open the Video Decoder with the parameters retreived from the stream
		if (
		    (err =
		     DecoderOpen(card, card->stream.hsize,
				 card->stream.vsize,
				 card->stream.sh.aspectratio,
				 !card->stream.MPEG2, 0,
				 (card->videomode == PAL)))) {	// TODO: include vbvbuffersize
			printk(KERN_ERR LOGNAME
			       ": Video Decoder Open failed: %s\n",
			       ((err == 1) ?
				"Picture size too big (>1440 pixel wide)" :
				"On-card memory insufficient for frame stores"));
			CloseCard(card);
			return -ENODEV;	// picture too big or insufficient memory
		}
		card->startingV = 1;	// tell the card to start playing as soon as ES-buffers are sufficiently full
		card->startingA = 1;	// tell the card to start playing as soon as ES-buffers are sufficiently full
	}
	

	return 0;
}

int SetSCRstart(struct cvdv_cards *card, u32 SCR_base)
{
	u32 SCR_compare;
	u32 SCR_compareA;
	u32 SCR_compareV;
	if (card->startingV) {
		printk(KERN_ERR LOGNAME ": SCR in DVD Pack: 0x%08X\n",
		       SCR_base);
		card->startingV = 0;
		card->startingA = 0;
		DecoderMaskByte(card, 0x007, 0xD2, 0xD2);	// Set 0x010, halt SCR counter
		SCR_compare = SCR_base + 000;
		if (SCR_base < 900)
			SCR_base = 0;
		else
			SCR_base -= 900;
		//DecoderWriteDWord(card,0x009,SCR_base);  // Set SCR counter
		DecoderWriteByte(card, 0x009, SCR_base & 0xFF);	// Set SCR counter
		DecoderWriteByte(card, 0x00A, (SCR_base >> 8) & 0xFF);
		DecoderWriteByte(card, 0x00B, (SCR_base >> 16) & 0xFF);
		DecoderWriteByte(card, 0x00C, (SCR_base >> 24) & 0xFF);
		DecoderMaskByte(card, 0x011, 0x03, 0x02);	// compare, not capture
		printk(KERN_ERR LOGNAME ": SCR compare value: 0x%08X\n",
		       SCR_compare);
		//DecoderWriteDWord(card,0x00D,SCR_compare);  // Set Compare register
		DecoderWriteByte(card, 0x00D, SCR_compare & 0xFF);	// Set Compare register
		DecoderWriteByte(card, 0x00E, (SCR_compare >> 8) & 0xFF);
		DecoderWriteByte(card, 0x00F, (SCR_compare >> 16) & 0xFF);
		DecoderWriteByte(card, 0x010, (SCR_compare >> 24) & 0xFF);
		//DecoderWriteDWord(card,0x014,SCR_compare);  // Set audio compare reg.
		DecoderWriteByte(card, 0x014, SCR_compare & 0xFF);	// Set audio compare reg.
		DecoderWriteByte(card, 0x015, (SCR_compare >> 8) & 0xFF);
		DecoderWriteByte(card, 0x016, (SCR_compare >> 16) & 0xFF);
		DecoderWriteByte(card, 0x017, (SCR_compare >> 24) & 0xFF);
		DecoderSetByte(card, 0x013, 0x03);	// Video and Audio start on cmp.
		//DecoderSetVideoPanic(card,0,DecoderGetVideoESSize(card)/4);  // video panic at 25 percent
		VideoSetBackground(card, 1, 0, 0, 0);	// black
		SCR_base = DecoderReadByte(card, 0x009);
		SCR_base =
		    SCR_base | ((u32) DecoderReadByte(card, 0x00A) << 8);
		SCR_base =
		    SCR_base | ((u32) DecoderReadByte(card, 0x00B) << 16);
		SCR_base =
		    SCR_base | ((u32) DecoderReadByte(card, 0x00C) << 24);
		SCR_compareA = DecoderReadByte(card, 0x014);
		SCR_compareA =
		    SCR_compareA | ((u32) DecoderReadByte(card, 0x015) <<
				    8);
		SCR_compareA =
		    SCR_compareA | ((u32) DecoderReadByte(card, 0x016) <<
				    16);
		SCR_compareA =
		    SCR_compareA | ((u32) DecoderReadByte(card, 0x017) <<
				    24);
		SCR_compareV = DecoderReadByte(card, 0x00D);
		SCR_compareV =
		    SCR_compareV | ((u32) DecoderReadByte(card, 0x00E) <<
				    8);
		SCR_compareV =
		    SCR_compareV | ((u32) DecoderReadByte(card, 0x00F) <<
				    16);
		SCR_compareV =
		    SCR_compareV | ((u32) DecoderReadByte(card, 0x010) <<
				    24);
		if (DecoderReadByte(card, 0x013) & 0x03)
			printk(KERN_DEBUG LOGNAME
			       ": SCR 0x%08X, videocmp=0x%08X, audiocmp=0x%08X %02X\n",
			       SCR_base, SCR_compareV, SCR_compareA,
			       DecoderReadByte(card, 0x013));
		DecoderMaskByte(card, 0x007, 0xD2, 0xC2);	// Del 0x010, SCR counter run
	}
	return 0;
}

int DecoderWriteBlock(struct cvdv_cards *card, u8 * data, int size,
		      int initial, int setSCR)
{
	//int a,v,as,vs,ap,vp;
	int res;
	u32 SCR_base;
	int co = 0;
	//  u32 SCR_compare;
	res = 0;
	//Prepare(card);

	if (size > 0) {

		if (!card->use_ring)
			MargiSetBuffers(card, 16*65536);
		
		if (card->startingDVDV || card->startingDVDA)
			setSCR = 1;

		if (initial) {
			DecoderStreamReset(card);
			DecoderSetupReset(card);
			//TODO stop and start channel interface
			setSCR = 1;
		}

		if (setSCR) {
			SCR_base = ParseSCR(data);
			SetSCR(card, SCR_base);
		}

		while (((res = MargiPush(card, size, data)) < size) 
		       && co < 100) {	
			data+=res;
			size-=res;
			co++;
//			printk(KERN_DEBUG LOGNAME
			//     ": DecoderWriteBlock - buffers only filled with %d instead of %d bytes\n",
			//     res, size);
			if (card->DMAABusy)
				interruptible_sleep_on(&card->wqA);	
		}

		if (card->startingDVDV) {
			card->startingDVDV = 0;
			DecoderStartDecode(card);
		}
		if (card->startingDVDA) {
			card->startingDVDA = 0;
			AudioSetPlayMode(card, AUDIO_PLAY);
		}


	}
	return 0;
}





    //////////////////////////////
   //                          //
  //  Char Device Procedures  //
 //                          //
//////////////////////////////

static ssize_t PSwrite(struct file *file, const char *data, size_t count,
		       loff_t * offset)
{
	struct cvdv_cards *card =
	    minorlist[MINOR(file->f_dentry->d_inode->i_rdev) % MAXDEV];	// minor number modulo 16
	int channel = MINOR(file->f_dentry->d_inode->i_rdev) / MAXDEV;	// minor number div. 16
	struct StreamSetup *setup = &card->setup;
//  int i;
//  int laststart=-1;
//  int B3=0, B5=0, C0=0, E0=0, BD=0, BF=0;
	int res;

	if (card != NULL) {
		if (count > 0) {	// Do we have data?
			if ((res = Prepare(card)))
				return res;
			
			if ((setup->streamtype != stream_ES)
			    && (setup->streamtype != stream_PES))
				channel = 0;	
// only ES and PES come in separate streams
			switch (channel) {
			case 0:
				if (!card->use_ring)
					MargiSetBuffers(card, NBBUF*
                                                         CHANNELBUFFERSIZE);
				if (!(count = MargiPush(card, count, data))){
					if (card->DMAABusy)
						interruptible_sleep_on(
							&card->wqA);
				}
				break;


			case 1:

// todo
				break;
			}
		}
		return count;
	} else {
		printk(KERN_ERR LOGNAME
		       ": Video Decoder Prepare failed: device with this minor number not found\n");
		return -ENODEV;	// device with this minor number not found
	}
}

static unsigned int PSpoll(struct file *file, poll_table * table)
{
	struct cvdv_cards *card =
	    minorlist[MINOR(file->f_dentry->d_inode->i_rdev) % MAXDEV];	// minor number modulo 16
//  int channel=MINOR(file->f_dentry->d_inode->i_rdev) / MAXDEV;  // minor number div. 16
	if (card != NULL) {

		return POLLOUT | POLLWRNORM;	// always writeable, HAS TO BE CHANGED!!!!

	} else
		return POLLERR;	// device with this minor number not found
}

static int PSioctl(struct inode *inode, struct file *file,
		   unsigned int cmd, unsigned long arg)
{
	struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV];	// minor number modulo 16
//  int channel=MINOR(inode->i_rdev) / MAXDEV;  // minor number div. 16
	struct drawcmd *dc;
	struct decodercmd *command;
	u16 attr;
	if (card != NULL) {
		if (_IOC_TYPE(cmd) == CVDV_IOCTL_MAGIC)
			switch (_IOC_NR(cmd)) {
			case IOCTL_DRAW:	// Drawing commands
				dc = (struct drawcmd *) arg;
				switch (dc->cmd) {
				case OSD_Close:
					printk(KERN_DEBUG LOGNAME
					       ": OSD Close\n");
					return OSDClose(card);
				case OSD_Open:	// Open(x0,y0,x1,y1,BitPerPixel(2/4/8),mix(0..15))
					return OSDOpen(card, dc->x0,
						       dc->y0, dc->x1,
						       dc->y1,
						       dc->color & 0x0F,
						       (dc->color >> 4) &
						       0x0F);
				case OSD_Show:
					return OSDShow(card);
				case OSD_Hide:
					return OSDHide(card);
				case OSD_Clear:
					return OSDClear(card);
				case OSD_Fill:	// Fill(color)
					return OSDFill(card, dc->color);
				case OSD_SetColor:	// SetColor(color,R(x0),G(y0),B(x1),opacity(y1))
//printk(KERN_DEBUG LOGNAME ": OSD SetColor(%d,%d,%d,%d,%d)\n",
//dc->color,dc->x0,dc->y0,dc->x1,dc->y1);
					return (OSDSetColor
						(card, dc->color, dc->x0,
						 dc->y0, dc->x1, 0,
						 (dc->y1 != 255),
						 (dc->y1 == 0)) >= 0);
				case OSD_SetPalette:	// SetPalette(firstcolor{color},lastcolor{x0},data)
					return OSDSetPalette(card,
							     dc->color,
							     dc->x0, (u8 *)
							     dc->data);
				case OSD_SetTrans:	// SetTrans(transparency{color})
					return OSDSetTrans(card,
							   (dc->color >> 4)
							   & 0x0F);
				case OSD_SetPixel:	// SetPixel(x0,y0,color)
					return OSDSetPixel(card, dc->x0,
							   dc->y0,
							   dc->color);
				case OSD_GetPixel:	// GetPixel(x0,y0);
					return OSDGetPixel(card, dc->x0,
							   dc->y0);
				case OSD_SetRow:	// SetRow(x0,y0,x1,(u8*)data)
					return OSDSetRow(card, dc->x0,
							 dc->y0, dc->x1,
							 (u8 *) dc->data);
				case OSD_SetBlock:	// SetBlock(x0,y0,x1,y1,(u8*)data)
					return OSDSetBlock(card, dc->x0,
							   dc->y0, dc->x1,
							   dc->y1,
							   dc->color,
							   (u8 *)
							   dc->data);
				case OSD_FillRow:	// FillRow(x0,y0,x1,color)
					return OSDFillRow(card, dc->x0,
							  dc->y0, dc->x1,
							  dc->color);
				case OSD_FillBlock:	// FillRow(x0,y0,x1,y1,color)
					return OSDFillBlock(card, dc->x0,
							    dc->y0, dc->x1,
							    dc->y1,
							    dc->color);
				case OSD_Line:	// Line(x0,y0,x1,y1,color);
					return OSDLine(card, dc->x0,
						       dc->y0, dc->x1,
						       dc->y1, dc->color);
				case OSD_Query:	// Query(x0,y0,x1,y1,aspect(color:11)
					return OSDQuery(card, &dc->x0,
							&dc->y0, &dc->x1,
							&dc->y1,
							&dc->color);
				case OSD_Test:
					return OSDTest(card);
				default:
					return -EINVAL;
				}
			case IOCTL_DECODER:
				command = (struct decodercmd *) arg;
				switch (command->cmd) {
				  /*
				case Decoder_CSS:
					return DecoderCSS(card,
							  command->param1,
							  command->data1);
				  */
				case Decoder_Set_Videosystem:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_Videosystem\n");
					card->videomode =
					    (videosystem) command->param1;
					SetVideoSystem(card);
					return 0;
				case Decoder_Set_Streamtype:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_Streamtype\n");
					card->setup.streamtype =
					    (stream_type) command->param1;
					return 0;
				case Decoder_Set_Audiotype:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_Audiotype\n");
					card->setup.audioselect =
					    (audio_type) command->param1;
					DecoderPrepareAudio(card);
					return 0;
				case Decoder_Set_VideoStreamID:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_VideoStreamID\n");
					card->setup.videoID =
					    command->param1;
					DecoderPrepareVideo(card);
					return 0;
				case Decoder_Set_AudioStreamID:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_AudioStreamID 0x%02X 0x%02X\n",command->param1,command->param2);
					card->setup.audioID =
					    command->param1;
					card->setup.audioIDext =
					    command->param2;
					attr = card->lastaattr;
					DecoderSelectAudioID(card);
					card->lastaattr = attr;
					return 0;
				case Decoder_Still_Put:
					return DecoderShowStill(card,
								command->
								param1,
								command->
								param2,
								command->
								data1,
								command->
								data2);
				case Decoder_Still_Get:
					return DecoderGetStill(card,
							       &command->
							       param1,
							       &command->
							       param2,
							       command->
							       data1,
							       command->
							       data2);
				case Decoder_Pause:	// pause{param1}  0=run 1=pause 2=toggle
					if (command->param1 == 2) {
						if (card->paused)
							DecoderUnPause
							    (card);
						else
							DecoderPause(card);
					} else {
						if (!command->param1)
							DecoderUnPause
							    (card);
						else
							DecoderPause(card);
					}
					return 0;
				case Decoder_Highlight:	// active{param1}, color information(SL_COLI or AC_COLI){data1[4]}, button position(BTN_POSI){data2[6]}
					return DecoderHighlight(card,
								command->
								param1,
								command->
								data1,
								command->
								data2);
				case Decoder_SPU:	// stream{param1}, active{param2}
					return DecoderSPUStream(card,
								command->
								param1,
								command->
								param2);
				case Decoder_SPU_Palette:	// length{param1}, palette{data1}
					return DecoderSPUPalette(card,
								 command->
								 param1,
								 command->
								 data1);
				case Decoder_GetNavi:	// data1 will be filled with PCI or DSI pack, and 1024 will be returned
					return DecoderGetNavi(card,
							      command->
							      data1);
				case Decoder_SetKaraoke:	// Vocal1{param1}, Vocal2{param2}, Melody{param3} 
					return DecoderKaraoke(card,
							      command->
							      param1,
							      command->
							      param2,
							      command->
							      param3);
				case Decoder_Set_Videoattribute:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_Videoattribute\n");
					if (!card->ChannelBuffersAllocated) {
						DecoderStreamReset(card);
						MargiFlush(card);

						card->setup.streamtype =
						    stream_DVD;
						card->setup.videoID = 0;
						DecoderPrepareVideo(card);
						//VideoSetBackground(card,1,0,0,0);  // black
						//DecoderPreparePS(card, -1, 1,2,2,3,1);
						DecoderPreparePS(card, 0,
								 0, 2, 2,
								 3, 1);
					}

					SetVideoAttr(card,
						     command->param1);
					card->startingDVDV = 1;	// tell the card to start playing as soon as ES-buffers are sufficiently full
					return 0;
				case Decoder_Set_Audioattribute:
					printk(KERN_DEBUG LOGNAME
					       ": -- Decoder_Set_Audioattribute\n");
					SetAudioAttr(card,
						     command->param1);
					card->startingDVDA =
					    ((card->setup.audioselect !=
					      audio_none)
					     && (card->setup.audioselect != audio_disable));	// tell the card to start playing as soon as ES-buffers are sufficiently full
					return 0;
				case Decoder_WriteBlock:	// DVD-Sector{data1}, sectorsize{param1{2048}}, initialsector{param2{bool}}, set_SCR{param3}
					return DecoderWriteBlock(card,
								 command->
								 data1,
								 command->
								 param1,
								 command->
								 param2,
								 command->
								 param3);
				default:
					return -EINVAL;
				}
			default:
				return -EINVAL;
		} else
			return -EINVAL;
	} else {
		printk(KERN_ERR LOGNAME
		       ": Video Decoder Prepare failed: device with this minor number not found\n");
		return -ENODEV;	// device with this minor number not found
	}
}

static int PSmmap(struct file *file, struct vm_area_struct *vm)
{
	return -ENODEV;
}

static int PSopen(struct inode *inode, struct file *file)
{
	struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV];	// minor number modulo 16
	int channel = MINOR(inode->i_rdev) / MAXDEV;	// minor number div. 16
	int i, closed;
	if (card != NULL) {
		printk(KERN_DEBUG LOGNAME ": -- PSopen %d\n",channel);
		CloseCard(card);
		DecoderStreamReset(card);
		DecoderSetupReset(card);
		if (card->open[channel])
			printk(KERN_DEBUG LOGNAME
			       ": PSopen - already open: channel %d\n",
			       channel);
		closed = 1;
		for (i = 0; i < MINORNUM; i++)
			if (card->open[i])
				closed = 0;
		if (closed) {	// first open() for this card?
			MargiFreeBuffers(card);
			VideoSetBackground(card, 1, 0, 0, 0);	// black
		}
		card->open[channel]++;
		//MOD_INC_USE_COUNT;
		return 0;
	} else {
		printk(KERN_ERR LOGNAME
		       ": Video Decoder Prepare failed: device with this minor number not found\n");
		return -ENODEV;	// device with this minor number not found
	}
}

static int PSrelease(struct inode *inode, struct file *file)
{
	struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV];	// minor number modulo 16
	int channel = MINOR(inode->i_rdev) / MAXDEV;	// minor number div. 16
	int i, closed;

	if (card != NULL) {
		printk(KERN_DEBUG LOGNAME ": -- PSrelease\n");
		if (!card->open[channel])
			printk(KERN_DEBUG LOGNAME
			       ": PSrelease - not open: channel %d\n",
			       channel);
		card->open[channel]--;
		//MOD_DEC_USE_COUNT;
		if (!card->open[channel]) {
			closed = 1;
			for (i = 0; i < MINORNUM; i++)
				if (card->open[i])
					closed = 0;
			if (closed) {	// last close() for this card?
				printk(KERN_DEBUG LOGNAME
				       ": PSrelease - last close\n");
				// flush remaining data
				MargiFlush(card);

				card->channelApos = 0;
				card->channelBpos = 0;

				if ((card->DMAABusy || card->DMABBusy) && (card->intdecodestatus)) {	// ongoing DMA? 
					printk(KERN_DEBUG LOGNAME
					       ": Delayed closing: A=%d B=%d\n",
					       card->DMAABusy,
					       card->DMABBusy);
					card->closing = 1;	// then delay closing until DMA finished,
				} else {	// otherwise
					CloseCard(card);	// close immediately
				}
			}
		}
		return 0;
	} else {
		printk(KERN_ERR LOGNAME
		       ": Video Decoder Prepare failed: device with this minor number not found\n");
		return -ENODEV;	// device with this minor number not found
	}
}

    //////////////////////////
   //                      //
  //  Char Device Hookup  //
 //                      //
//////////////////////////

// Hookups for a write-only device, that accepts MPEG-2 Program Stream
struct file_operations cvdv_fileops = {
	write:   PSwrite,
	poll:    PSpoll,		
	ioctl:   PSioctl,
	mmap:    PSmmap,
	open:    PSopen,
	release: PSrelease,
};

LinuxTV legacy CVS <linuxtv.org/cvs>