/*
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;
}
static char *cimlogo[] = {
".............................................",
".............................................",
"......................###....................",
".....................#####...................",
".....................######..................",
"..............#......#####...................",
"...........#####....######...................",
".........########...######...................",
"........########....######...................",
".......#########...######....................",
"......########.....######...####.............",
".....#######.......#####...#####.............",
".....######.......######...######............",
"....#######.......######....######...........",
"....######........######....######...........",
"....#####........######......#####...........",
"...######........######......#####...........",
"...#####.........######......######..........",
"...#####.........#####.......######..........",
"...#####........######........#####..........",
"...#####........######.......######..........",
"...#####........#####.........#####..........",
"...#####.......######........######..........",
"...#####.......######........#####...........",
"...######.......####.........#####...........",
"....#####........##.........######...........",
"....######..................######...........",
"....######.................######............",
".....#######..............######.....#####...",
".....########............#######....#######..",
"......#########........########.....#######..",
".......#######################......########.",
"........#####################.......#######..",
"..........#################.........#######..",
"............#############............#####...",
"...............#.#####.................##....",
".............................................",
"............................................."
};
/////////////////////////////////////////////
// //
// Controlling the L64021 MPEG-2 Decoder //
// //
/////////////////////////////////////////////
int OSDTest(struct cvdv_cards *card)
{
int i, j, col, x0, y0, x1, y1,aspx;
u8 b;
if (!card->OSD.open)
return -2;
OSDQuery(card, &x0, &y0, &x1, &y1, &aspx);
OSDShow(card);
OSDSetColor(card, 0, 0, 0, 0, 0, 0, 0);
OSDSetColor(card, 1, 128, 255, 255, 0, 0, 0);
for ( i = 0; i < cimlogo_width; i++){
for ( j = 0; j < cimlogo_height; j++){
b = cimlogo[j][i];
col = (b == '#') ? 1: 0;
OSDSetPixel(card, x0+i, y0+j, col);
}
}
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)) {
// 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->stream.hsize > 480)))) { // 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
}
printk(KERN_DEBUG LOGNAME ": Ready to go\n");
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, NBBUF* 128 *
CHANNELBUFFERSIZE);
if (card->startingDVDV || card->startingDVDA)
setSCR = 1;
if (initial) {
DecoderStreamReset(card);
//TODO stop and start channel interface
setSCR = 1;
}
if (setSCR) {
SCR_base = ParseSCR(data);
SetSCR(card, SCR_base);
}
card->DMAABusy = 0;
while (((res = MargiPush(card, size, data)) < size)
&& co < 1000) {
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;
card->startingV = 1;
DecoderStartDecode(card);
}
if (card->startingDVDA) {
card->startingDVDA = 0;
card->startingA = 1;
AudioSetPlayMode(card, MAUDIO_PLAY);
}
}
return 0;
}
//////////////////////////////
// //
// Char Device Procedures //
// //
//////////////////////////////
static long margi_write(struct video_device *dev, const char *data,
unsigned long count, int nonblock)
{
struct cvdv_cards *card = (struct cvdv_cards*)dev;
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;
int channel=0;
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 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
return margi_write((struct video_device *)card, data, count, 0);
}
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
if (card != NULL) {
return POLLOUT | POLLWRNORM; // always writeable, HAS TO BE CHANGED!!!!
} else
return POLLERR; // device with this minor number not found
}
static int margi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
// struct cvdv_cards *card = (struct cvdv_cards*)dev;
return 0;
}
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
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);
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 margi_open(struct video_device *dev, int flags)
{
struct cvdv_cards *card = (struct cvdv_cards*)dev;
int closed;
if (card != NULL) {
printk(KERN_DEBUG LOGNAME ": -- PSopen \n");
CloseCard(card);
OSDClose(card);
#ifdef NOINT
card->timer.function = Timerfunction;
card->timer.data=(unsigned long) card;
card->timer.expires=jiffies+1;
add_timer(&card->timer);
#endif
if (card->open)
printk(KERN_DEBUG LOGNAME
": PSopen - already open\n");
closed = 1;
if (card->open)
closed = 0;
if (closed) { // first open() for this card?
MargiFreeBuffers(card);
VideoSetBackground(card, 1, 0, 0, 0); // black
}
card->open++;
//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 PSopen(struct inode *inode, struct file *file)
{
struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV]; // minor number modulo 16
return margi_open((struct video_device *) card,0);
}
static int all_margi_close(struct video_device *dev)
{
struct cvdv_cards *card = (struct cvdv_cards*)dev;
int closed;
int count;
count = ring_read_rest(&(card->rbuf));
if (count > 0 ) card->DMAABusy = 1;
if (card != NULL) {
printk(KERN_DEBUG LOGNAME ": -- PSrelease\n");
if (!card->open)
printk(KERN_DEBUG LOGNAME
": PSrelease - not open\n");
card->open--;
//MOD_DEC_USE_COUNT;
if (!card->open) {
closed = 1;
if (closed) { // last close() for this card?
printk(KERN_DEBUG LOGNAME
": PSrelease - last close\n");
// flush remaining data
card->channelApos = 0;
card->channelBpos = 0;
if ((card->DMAABusy || card->DMABBusy
|| count >1000 ) &&
(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
printk(KERN_DEBUG LOGNAME
": closing now\n");
}
}
}
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 void margi_close(struct video_device *dev)
{
all_margi_close(dev);
}
static int PSrelease(struct inode *inode, struct file *file)
{
struct cvdv_cards *card = minorlist[MINOR(inode->i_rdev) % MAXDEV]; // minor number modulo 16
return all_margi_close((struct video_device *)card);
}
//////////////////////////
// //
// 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,
};
static int margi_init_done(struct video_device *dev)
{
printk(KERN_ERR "margi: margi_init_done called\n");
return 0;
}
static int margi_mmap(struct video_device *dev,
const char *adr, unsigned long size)
{
return 0;
}
static struct video_device margi_template = {
name: "Margi MPEG2 Decoder",
type: (VID_TYPE_MPEG_DECODER),
open: margi_open,
close: margi_close,
write: margi_write,
ioctl: margi_ioctl,
mmap: margi_mmap,
initialize: margi_init_done,
};
#ifdef DVB
static inline int
num2type(struct cvdv_cards *card, int num)
{
if (!card->dvb_devs)
return -2;
if (num>=card->dvb_devs->num)
return -2;
return card->dvb_devs->tab[num];
}
static int
dvbdev_open(struct dvb_device *dvbdev, int num,
struct inode *inode, struct file *file)
{
struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv;
int type=num2type(card, num);
int ret=0;
if (type<0)
return -EINVAL;
if (card->users[num] >= card->dvb_devs->max_users[num])
return -EBUSY;
if ((file->f_flags&O_ACCMODE)!=O_RDONLY)
if (card->writers[num] >= card->dvb_devs->max_writers[num])
return -EBUSY;
switch (type) {
case DVB_DEVICE_VIDEO_0:
card->video_blank=true;
card->audiostate.AVSyncState=true;
card->videostate.streamSource=VIDEO_SOURCE_DEMUX;
break;
case DVB_DEVICE_AUDIO_0:
card->audiostate.streamSource=AUDIO_SOURCE_DEMUX;
break;
case DVB_DEVICE_DEMUX_0:
if ((file->f_flags&O_ACCMODE)!=O_RDWR)
return -EINVAL;
ret=DmxDevFilterAlloc(&card->dmxdev, file);
break;
case DVB_DEVICE_DVR_0:
ret=DmxDevDVROpen(&card->dmxdev, file);
break;
default:
return -EINVAL;
}
if (ret<0)
return ret;
if ((file->f_flags&O_ACCMODE)!=O_RDONLY)
card->writers[num]++;
card->users[num]++;
return ret;
}
static int
dvbdev_close(struct dvb_device *dvbdev, int num,
struct inode *inode, struct file *file)
{
struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv;
int type=num2type(card, num);
int ret=0;
if (type<0)
return -EINVAL;
switch (type) {
case DVB_DEVICE_VIDEO_0:
all_margi_close((struct video_device *)card);
break;
case DVB_DEVICE_AUDIO_0:
all_margi_close((struct video_device *)card);
break;
case DVB_DEVICE_DEMUX_0:
ret=DmxDevFilterFree(&card->dmxdev, file);
break;
case DVB_DEVICE_DVR_0:
ret=DmxDevDVRClose(&card->dmxdev, file);
break;
default:
return -EINVAL;
}
if (ret<0)
return ret;
if ((file->f_flags&O_ACCMODE)!=O_RDONLY)
card->writers[num]--;
card->users[num]--;
return ret;
}
static ssize_t
dvbdev_write(struct dvb_device *dvbdev, int num,
struct file *file,
const char *buf, size_t count, loff_t *ppos)
{
struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv;
int type=num2type(card, num);
switch (type) {
case DVB_DEVICE_VIDEO_0:
if (card->videostate.streamSource!=VIDEO_SOURCE_MEMORY)
return -EPERM;
return margi_write((struct video_device *)card, buf, count,
file->f_flags&O_NONBLOCK);
case DVB_DEVICE_AUDIO_0:
if (card->audiostate.streamSource!=AUDIO_SOURCE_MEMORY)
return -EPERM;
return count;
case DVB_DEVICE_DVR_0:
return DmxDevDVRWrite(&card->dmxdev, file, buf, count, ppos);
default:
return -EOPNOTSUPP;
}
return 0;
}
static int
dvbdev_ioctl(struct dvb_device *dvbdev, int num,
struct file *file, unsigned int cmd, unsigned long arg)
{
struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv;
void *parg=(void *)arg;
int type=num2type(card, num);
switch (type) {
case DVB_DEVICE_VIDEO_0:
if (((file->f_flags&O_ACCMODE)==O_RDONLY) &&
(cmd!=VIDEO_GET_STATUS))
return -EPERM;
switch (cmd) {
case VIDEO_STOP:
card->videostate.playState=VIDEO_STOPPED;
DecoderPause(card);
return 0;
case VIDEO_PLAY:
if (card->videostate.streamSource==
VIDEO_SOURCE_MEMORY) {
DecoderUnPause(card);
}
card->videostate.playState=VIDEO_PLAYING;
break;
case VIDEO_FREEZE:
card->videostate.playState=VIDEO_FREEZED;
DecoderPause(card);
break;
case VIDEO_CONTINUE:
if (card->videostate.playState==VIDEO_FREEZED) {
DecoderUnPause(card);
}
break;
case VIDEO_SELECT_SOURCE:
card->videostate.streamSource=(videoStreamSource_t) arg;
break;
case VIDEO_SET_BLANK:
card->videostate.videoBlank=(boolean) arg;
break;
case VIDEO_GET_STATUS:
if(copy_to_user(parg, &card->videostate,
sizeof(struct videoStatus)))
return -EFAULT;
break;
case VIDEO_GET_EVENT:
return -EOPNOTSUPP;
case VIDEO_SET_DISPLAY_FORMAT:
{
videoDisplayFormat_t format=(videoDisplayFormat_t) arg;
u16 val=0;
switch(format) {
case VIDEO_PAN_SCAN:
val=VID_PAN_SCAN_PREF;
break;
case VIDEO_LETTER_BOX:
val=VID_VC_AND_PS_PREF;
break;
case VIDEO_CENTER_CUT_OUT:
val=VID_CENTRE_CUT_PREF;
break;
default:
return -EINVAL;
}
card->videostate.videoFormat=format;
return 0;
}
case VIDEO_STILLPICTURE:
{
struct videoDisplayStillPicture pic;
if(copy_from_user(&pic, parg,
sizeof(struct videoDisplayStillPicture)))
return -EFAULT;
break;
}
case VIDEO_FAST_FORWARD:
if (card->videostate.streamSource!=VIDEO_SOURCE_MEMORY)
return -EPERM;
break;
case VIDEO_SLOWMOTION:
if (card->videostate.streamSource!=VIDEO_SOURCE_MEMORY)
return -EPERM;
break;
case VIDEO_GET_CAPABILITIES:
{
int cap=VIDEO_CAP_MPEG1|
VIDEO_CAP_MPEG2|
VIDEO_CAP_SYS|
VIDEO_CAP_PROG;
if (copy_to_user(parg, &cap,
sizeof(cap)))
return -EFAULT;
break;
}
default:
return -ENOIOCTLCMD;
}
return 0;
case DVB_DEVICE_AUDIO_0:
if (((file->f_flags&O_ACCMODE)==O_RDONLY) &&
(cmd!=AUDIO_GET_STATUS))
return -EPERM;
switch (cmd) {
case AUDIO_STOP:
if (card->audiostate.streamSource!=AUDIO_SOURCE_MEMORY)
break;
AudioStopDecode(card);
card->audiostate.playState=AUDIO_STOPPED;
break;
case AUDIO_PLAY:
if (card->audiostate.streamSource!=AUDIO_SOURCE_MEMORY)
break;
AudioSetPlayMode(card, MAUDIO_PLAY);
card->audiostate.playState=AUDIO_PLAYING;
break;
case AUDIO_PAUSE:
card->audiostate.playState=AUDIO_PAUSED;
AudioSetPlayMode(card, MAUDIO_PAUSE);
break;
case AUDIO_CONTINUE:
if (card->audiostate.playState==AUDIO_PAUSED) {
card->audiostate.playState=AUDIO_PLAYING;
AudioSetPlayMode(card, MAUDIO_PLAY);
}
break;
case AUDIO_SELECT_SOURCE:
card->audiostate.streamSource=(audioStreamSource_t) arg;
break;
case AUDIO_SET_MUTE:
{
AudioMute(card, arg);
card->audiostate.muteState=(boolean) arg;
break;
}
case AUDIO_SET_AV_SYNC:
card->audiostate.AVSyncState=(boolean) arg;
break;
case AUDIO_SET_BYPASS_MODE:
return -EINVAL;
case AUDIO_CHANNEL_SELECT:
card->audiostate.channelSelect=(audioChannelSelect_t) arg;
switch(card->audiostate.channelSelect) {
case AUDIO_STEREO:
break;
case AUDIO_MONO_LEFT:
break;
case AUDIO_MONO_RIGHT:
break;
default:
return -EINVAL;
}
return 0;
case AUDIO_GET_STATUS:
if(copy_to_user(parg, &card->audiostate,
sizeof(struct audioStatus)))
return -EFAULT;
break;
case AUDIO_GET_CAPABILITIES:
{
int cap=AUDIO_CAP_LPCM|
AUDIO_CAP_MP1|
AUDIO_CAP_MP2;
if (copy_to_user(parg, &cap,
sizeof(cap)))
return -EFAULT;
break;
}
default:
return -ENOIOCTLCMD;
}
break;
case DVB_DEVICE_DEMUX_0:
return DmxDevIoctl(&card->dmxdev, file, cmd, arg);
default:
return -EOPNOTSUPP;
}
return 0;
}
static unsigned int
dvbdev_poll(struct dvb_device *dvbdev, int num,
struct file *file, poll_table * wait)
{
struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv;
int type=num2type(card, num);
switch (type) {
case DVB_DEVICE_DEMUX_0:
return DmxDevPoll(&card->dmxdev, file, wait);
case DVB_DEVICE_VIDEO_0:
return PSpoll(file, wait);
case DVB_DEVICE_AUDIO_0:
return PSpoll(file, wait);
case DVB_DEVICE_CA_0:
break;
default:
return -EOPNOTSUPP;
}
return 0;
}
static int
dvbdev_device_type(struct dvb_device *dvbdev, unsigned int num)
{
struct cvdv_cards *card=(struct cvdv_cards *) dvbdev->priv;
return num2type(card, num);
}
#endif
/******************************************************************************
* driver registration
******************************************************************************/
void v4l_init(struct cvdv_cards *card)
{
memcpy( &card->video, &margi_template, sizeof(struct video_device));
if( 0 > (video_register_device(&card->video, VFL_TYPE_GRABBER))) {
printk(KERN_ERR "margi: can't register videodevice\n");
}
}
#ifdef DVB
#define INFU 32768
static dvb_devs_t mdvb_devs = {
4,
{
DVB_DEVICE_VIDEO_0, DVB_DEVICE_AUDIO_0,
DVB_DEVICE_DEMUX_0, DVB_DEVICE_DVR_0
},
{ INFU, INFU, INFU, INFU},
{ 1, 1, INFU, 1}
};
int dvb_register(struct cvdv_cards *card)
{
int i;
struct dvb_device *dvbd=&card->dvb_dev;
if (card->dvb_registered)
return -1;
card->dvb_registered=1;
card->audiostate.AVSyncState=0;
card->audiostate.muteState=0;
card->audiostate.playState=AUDIO_STOPPED;
card->audiostate.streamSource=AUDIO_SOURCE_MEMORY;
card->audiostate.channelSelect=AUDIO_STEREO;
card->audiostate.bypassMode=0;
card->videostate.videoBlank=0;
card->videostate.playState=VIDEO_STOPPED;
card->videostate.streamSource=VIDEO_SOURCE_MEMORY;
card->videostate.videoFormat=VIDEO_FORMAT_4_3;
card->videostate.displayFormat=VIDEO_CENTER_CUT_OUT;
// init and register demuxes
// init and register dvb device structure
dvbd->priv=(void *) card;
dvbd->open=dvbdev_open;
dvbd->close=dvbdev_close;
dvbd->write=dvbdev_write;
dvbd->ioctl=dvbdev_ioctl;
dvbd->poll=dvbdev_poll;
dvbd->device_type=dvbdev_device_type;
for (i=0; i<DVB_DEVS_MAX; i++)
card->users[i]=card->writers[i]=0;
card->dvb_devs=0;
card->dvb_devs=&mdvb_devs;
return dvb_register_device(dvbd);
}
void dvb_unregister(struct cvdv_cards *card)
{
dvb_unregister_device(&card->dvb_dev);
}
#endif
LinuxTV legacy CVS <linuxtv.org/cvs>