/* License: GPL
 * Author: Kevin Thayer <nufan_wfk at yahoo dot com>
 *
 * This file will hold API related functions, both internal (firmware api)
 * and external (v4l2, etc)
 *
 */

#include "ivtv.h"

/* Fix the v4l2 api breakage - need to define if still using the old api */
#ifndef VIDIOC_OVERLAY_OLD
#define VIDIOC_OVERLAY_OLD		_IOWR ('V', 14, int)
#define VIDIOC_S_PARM_OLD		_IOW  ('V', 22, struct v4l2_streamparm)
#define VIDIOC_S_CTRL_OLD		_IOW  ('V', 28, struct v4l2_control)
#define VIDIOC_G_AUDIO_OLD		_IOWR ('V', 33, struct v4l2_audio)
#define VIDIOC_G_AUDOUT_OLD		_IOWR ('V', 49, struct v4l2_audioout)
#endif

/* FIXME need to find a good value */
#define V4L2_PIX_FMT_CMP_MPG2 77777
#define IVTV_V4L2_MAX_MINOR 15

int ivtv_v4l2_init(struct video_device *v);
int ivtv_v4l2_close(struct inode *inode, struct file *filp);
int ivtv_v4l2_open(struct inode *inode, struct file *filp);
int ivtv_v4l2_read(struct file *filp, char *buf, size_t count, loff_t *pos);
ssize_t ivtv_v4l2_write(struct file *filp, const char *buf, size_t count, loff_t *pos);
int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
		                unsigned long arg);
int ivtv_v4l2_pre_init(struct ivtv *itv);

struct file_operations ivtv_v4l2_fops = {
	read:           ivtv_v4l2_read,
	write:          ivtv_v4l2_write,
	open:           ivtv_v4l2_open,
	ioctl:          ivtv_v4l2_ioctl,
	release:        ivtv_v4l2_close,
	poll:		ivtv_poll,
};

/* FIXME  Static variables for the various card types go here */

struct video_device tmk_v4l2dev = {  /*values that work with the author's card */
.name	= "Vanilla iTVC15 card",
.type	= VFL_TYPE_GRABBER,
.type2	= VID_TYPE_CAPTURE,
.fops	= &ivtv_v4l2_fops,
.initialize =  ivtv_v4l2_init,
};


/* some tuner table values can change, so allocate this dynamically when you use it*/
struct v4l2_tuner tmk_tuners[2] = {
	{
	.index		= 0,
	.name		= "ivtv TV Tuner",
	.type		= V4L2_TUNER_ANALOG_TV,
	.capability	= (V4L2_TUNER_CAP_NORM|V4L2_TUNER_CAP_STEREO),
	.rxsubchans	= (V4L2_TUNER_SUB_STEREO),
	.audmode	= V4L2_TUNER_MODE_STEREO,
	.signal		= 0,
	.afc		= 0,
	.reserved	= {0,0,0,0}
	},{
	.index		= 1,
	.name		= "ivtv Radio",
	.type		= V4L2_TUNER_RADIO,
	.capability	= (V4L2_TUNER_CAP_STEREO),
	.rxsubchans	= 0,
	.audmode	= V4L2_TUNER_MODE_STEREO,
	.signal		= 0,
	.afc		= 0,
	.reserved	= {0,0,0,0}
	}
};

struct v4l2_standard tmk_standards[3] = {
	{
	.index		= 0,
	.id		= V4L2_STD_NTSC,
	.name		= "NTSC",
	.frameperiod	= { .numerator	= 1001,
			    .denominator= 30000},
	.framelines	= 525,
	.reserved	= {0,0,0,0}
	},{
	.index		= 1,
	.id		= V4L2_STD_PAL,
	.name		= "PAL",
	.frameperiod	= { .numerator	= 1,
			    .denominator= 25},
	.framelines	= 625,
	.reserved	= {0,0,0,0}
	},{
	.index		= 2,
	.id		= V4L2_STD_SECAM,
	.name		= "SECAM",
	.frameperiod	= { .numerator	= 1,
			    .denominator= 25},
	.framelines	= 625,
	.reserved	= {0,0,0,0}
	}
};

struct v4l2_input tmk_inputs[10] = { /*values that work with the author's card */
	{
	.index		= 0,
	.name		= "Composite 0",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 1,
	.name		= "Composite 1",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 2,
	.name		= "Composite 2",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 3,
	.name		= "Composite 3",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 4,
	.name		= "Tuner 0",
	.type		= V4L2_INPUT_TYPE_TUNER,
	.audioset	= 0,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 5,
	.name		= "Composite 4",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 6,
	.name		= "S-Video 0",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 7,
	.name		= "S-Video 1",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 8,
	.name		= "S-Video 2",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	},{
	.index		= 9,
	.name		= "S-Video 3",
	.type		= V4L2_INPUT_TYPE_CAMERA,
	.audioset	= 1,
	.tuner		= 0,
	.status		= 0,
	}
};

//FIXME capability and mode might be wrong
struct v4l2_audio tmk_audio_inputs[2] = {
	{0,"Tuner Audio In",0,0,},
	{1,"Audio Line In", 0,0,},
};

int tmk_audio_mapping[] = {
	0,3, /* Input 0 is msp input 3 */
	1,1, /* input 1 is msp input 1 */
	0,0  /* you're at end of list! */
};

struct v4l2_queryctrl ivtv_ctrl_menu_freq = {
		.id		= V4L2_CID_IVTV_FREQ,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Frequency",
		.minimum	= 0,
		.maximum	= 2,
		.step		= 1,
		.default_value	= 2,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_freq[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_FREQ, 0, "32kHz",   0 },
	{V4L2_CID_IVTV_FREQ, 1, "44.1kHz", 0 },
	{V4L2_CID_IVTV_FREQ, 2, "48kHz",   0 }
};

u32 ivtv_audio_tbl_freq[] = {
	/* setting */
	0x2 /* 32kHz   binary 10 */,
	0x0 /* 44.1kHz binary 00 */,
	0x1 /* 48kHz   binary 01 */
};

u32 ivtv_audio_mask_freq = 0x3;

struct v4l2_queryctrl ivtv_ctrl_menu_enc = {
		.id		= V4L2_CID_IVTV_ENC,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Encoding",
		.minimum	= 0,
		.maximum	= 2,
		.step		= 1,
		.default_value	= 1,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_enc[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_ENC, 0, "Layer 1",    0},
	{V4L2_CID_IVTV_ENC, 1, "Layer 2",    0},
	{V4L2_CID_IVTV_ENC, 2, "Layer 3(?)", 0}
};

u32 ivtv_audio_tbl_enc[] = {
	/* setting */
	0x1 << 2 /* Layer 1	binary 0100 */,
	0x2 << 2 /* Layer 2	binary 1000 */,
	0x3 << 2 /* Layer 3(?)	binary 1100 */
};

u32 ivtv_audio_mask_enc = 0xC;

struct v4l2_queryctrl ivtv_ctrl_menu_bitrate = {
		.id		= V4L2_CID_IVTV_BITRATE,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Audio Bitrate",
		.minimum	= 0,
		.maximum	= 14,
		.step		= 1,
		.default_value	= 14,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_bitrate[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_BITRATE,  0, "[L1/L2] Free fmt",  0},
	{V4L2_CID_IVTV_BITRATE,  1, "[L1/L2] 32k/32k",   0},
	{V4L2_CID_IVTV_BITRATE,  2, "[L1/L2] 64k/48k",   0},
	{V4L2_CID_IVTV_BITRATE,  3, "[L1/L2] 96k/56k",   0},
	{V4L2_CID_IVTV_BITRATE,  4, "[L1/L2] 128k/64k",  0},
	{V4L2_CID_IVTV_BITRATE,  5, "[L1/L2] 160k/80k",  0},
	{V4L2_CID_IVTV_BITRATE,  6, "[L1/L2] 192k/96k",  0},
	{V4L2_CID_IVTV_BITRATE,  7, "[L1/L2] 224k/112k", 0},
	{V4L2_CID_IVTV_BITRATE,  8, "[L1/L2] 256k/128k", 0},
	{V4L2_CID_IVTV_BITRATE,  9, "[L1/L2] 288k/160k", 0},
	{V4L2_CID_IVTV_BITRATE, 10, "[L1/L2] 320k/192k", 0},
	{V4L2_CID_IVTV_BITRATE, 11, "[L1/L2] 352k/224k", 0},
	{V4L2_CID_IVTV_BITRATE, 12, "[L1/L2] 384k/256k", 0},
	{V4L2_CID_IVTV_BITRATE, 13, "[L1/L2] 416k/320k", 0},
	{V4L2_CID_IVTV_BITRATE, 14, "[L1/L2] 448k/384k", 0},
};

u32 ivtv_audio_tbl_bitrate[] = {
	/* setting */
	0x0 << 4 /* [L1/L2] Free fmt	binary 0000 */,
	0x1 << 4 /* [L1/L2] 32k/32k,   binary 0001 */,
	0x2 << 4 /* [L1/L2] 64k/48k,   binary 0010 */,
	0x3 << 4 /* [L1/L2] 96k/56k,   binary 0011 */,
	0x4 << 4 /* [L1/L2] 128k/64k,  binary 0100 */,
	0x5 << 4 /* [L1/L2] 160k/80k,  binary 0101 */,
	0x6 << 4 /* [L1/L2] 192k/96k,  binary 0110 */,
	0x7 << 4 /* [L1/L2] 224k/112k, binary 0111 */,
	0x8 << 4 /* [L1/L2] 256k/128k, binary 1000 */,
	0x9 << 4 /* [L1/L2] 288k/160k, binary 1001 */,
	0xA << 4 /* [L1/L2] 320k/192k, binary 1010 */,
	0xB << 4 /* [L1/L2] 352k/224k, binary 1011 */,
	0xC << 4 /* [L1/L2] 384k/256k, binary 1100 */,
	0xD << 4 /* [L1/L2] 416k/320k, binary 1101 */,
	0xE << 4 /* [L1/L2] 448k/384k, binary 1110 */
};

u32 ivtv_audio_mask_bitrate = 0xF0;


struct v4l2_queryctrl ivtv_ctrl_menu_mono = {
		.id		= V4L2_CID_IVTV_MONO,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Mono/Stereo",
		.minimum	= 0,
		.maximum	= 3,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_mono[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_MONO, 0, "Stereo",      0},
	{V4L2_CID_IVTV_MONO, 1, "JointStereo", 0},
	{V4L2_CID_IVTV_MONO, 2, "Dual",        0},
	{V4L2_CID_IVTV_MONO, 3, "Mono",        0}
};

u32 ivtv_audio_tbl_mono[] = {
	/* setting */
	0x0 << 8 /* Stereo,      binary 00 */,
	0x1 << 8 /* JointStereo, binary 01 */,
	0x2 << 8 /* Dual,        binary 10 */,
	0x3 << 8 /* Mono,        binary 11 */
};

u32 ivtv_audio_mask_mono = 0x300;

struct v4l2_queryctrl ivtv_ctrl_menu_joint = {
		.id		= V4L2_CID_IVTV_JOINT,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Joint extension",
		.minimum	= 0,
		.maximum	= 3,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_joint[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_JOINT, 0, "Subbands 4-31/bound=4",   0},
	{V4L2_CID_IVTV_JOINT, 1, "Subbands 8-31/bound=8",   0},
	{V4L2_CID_IVTV_JOINT, 2, "Subbands 12-31/bound=12", 0},
	{V4L2_CID_IVTV_JOINT, 3, "Subbands 16-31/bound=16", 0}
};

u32 ivtv_audio_tbl_joint[] = {
	/* setting */
	0x0 << 10 /* Subbands 4-31/bound=4,   binary 00 */,
	0x1 << 10 /* Subbands 8-31/bound=8,   binary 01 */,
	0x2 << 10 /* Subbands 12-31/bound=12, binary 10 */,
	0x3 << 10 /* Subbands 16-31/bound=16, binary 11 */
};

u32 ivtv_audio_mask_joint = 0xc00;

struct v4l2_queryctrl ivtv_ctrl_menu_emphasis = {
		.id		= V4L2_CID_IVTV_EMPHASIS,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Emphasis",
		.minimum	= 0,
		.maximum	= 2,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_emphasis[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_EMPHASIS, 0, "None",       0},
	{V4L2_CID_IVTV_EMPHASIS, 1, "50/15uS",    0},
	{V4L2_CID_IVTV_EMPHASIS, 2, "CCITT J.17", 0}
};

u32 ivtv_audio_tbl_emphasis[] = {
	/* setting */
	0x0 << 12 /* None,       binary 00 */,
	0x1 << 12 /* 50/15uS,    binary 01 */,
	0x3 << 12 /* CCITT J.17, binary 11 */
};

u32 ivtv_audio_mask_emphasis = 0x3000;

struct v4l2_queryctrl ivtv_ctrl_menu_crc = {
		.id		= V4L2_CID_IVTV_CRC,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Audio CRC",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_crc[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_CRC, 0, "off", 0},
	{V4L2_CID_IVTV_CRC, 1, "on",  0}
};

u32 ivtv_audio_tbl_crc[] = {
	/* setting */
	0x0 << 14 /* off, binary 0 */,
	0x1 << 14 /* on,  binary 1 */
};

u32 ivtv_audio_mask_crc = 0x4000;

struct v4l2_queryctrl ivtv_ctrl_menu_copyright = {
		.id		= V4L2_CID_IVTV_COPYRIGHT,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Copyright",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_copyright[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_COPYRIGHT, 0, "off", 0},
	{V4L2_CID_IVTV_COPYRIGHT, 1, "on",  0}
};

u32 ivtv_audio_tbl_copyright[] = {
	/* setting */
	0x0 << 15 /* off, binary 0 */,
	0x1 << 15 /* on,  binary 1 */
};

u32 ivtv_audio_mask_copyright = 0x8000;

struct v4l2_queryctrl ivtv_ctrl_menu_generation = {
		.id		= V4L2_CID_IVTV_GEN,
		.type		= V4L2_CTRL_TYPE_MENU, 
		.name		= "Generation",
		.minimum	= 0,
		.maximum	= 1,
		.step		= 1,
		.default_value	= 0,
		.flags		= 0,
		.reserved	= {0,0}
};

struct v4l2_querymenu	ivtv_ctrl_query_generation[] = {
	/* ID, Index, Name, Reserved */
	{V4L2_CID_IVTV_GEN, 0, "copy",     0},
	{V4L2_CID_IVTV_GEN, 1, "original", 0}
};

u32 ivtv_audio_tbl_generation[] = {
	/* setting */
	0x0 << 16 /* copy,     binary 0 */,
	0x1 << 16 /* original, binary 1 */
};

u32 ivtv_audio_mask_generation = 0x10000;


/* 3 stream types: mpeg, yuv, passthru */
struct ivtv_v4l2_stream tmk_mpg_stream = {
/*MPEG*/
	.s_flags	= 0,
	.id		= -1,
	.v4l_reg_type	= VFL_TYPE_GRABBER,
	.format		= {
			  .type	= V4L2_BUF_TYPE_VIDEO_CAPTURE,
			  .fmt	= {
				  .pix	= {
					  .width 	= 720,
					  .height 	= 480,
					  .field	= V4L2_FIELD_INTERLACED,
					  .sizeimage	= (128*1024),
				  }
			},
	},
	.controlcount	=  0,
	.controls	=  NULL
};

struct ivtv_v4l2_stream tmk_yuv_stream = {
/*YUV*/
	.s_flags	= 0,
	.id		= -1,
	.v4l_reg_type	= VFL_TYPE_GRABBER,
	.format		= {
			  .type	= V4L2_BUF_TYPE_VIDEO_CAPTURE,
			  .fmt	= {
				  .pix	= {
					  .width 	= 720,
					  .height 	= 480,
					  .field	= V4L2_FIELD_INTERLACED,
					  .sizeimage	= (720*720),
				  }
			  },
	},
	.controlcount	=  0,
	.controls	=  NULL
};

//FIXME these settings are way wrong
struct ivtv_v4l2_stream tmk_vbi_stream = {
	.s_flags	= 0,
	.id		= -1,
	.v4l_reg_type	= VFL_TYPE_VBI,
	.format		= {
			  .type	= V4L2_BUF_TYPE_VIDEO_CAPTURE,
			  .fmt	= {
				  .pix	= {
					  .width 	= 720,
					  .height 	= 480,
					  .field	= V4L2_FIELD_INTERLACED,
					  .sizeimage	= (128*1024),
				  }
			  },
	},
	.controlcount	=  0,
	.controls	=  NULL
};

struct ivtv_v4l2_stream dec_mpg_stream = {
/*Decoder MPG*/
	.s_flags	= 0,
	.id		= -1,
	.v4l_reg_type	= VFL_TYPE_GRABBER,
	.format		= {
			  .type	= V4L2_BUF_TYPE_VIDEO_OUTPUT,
			  .fmt	= {
				  .pix	= {
					  .width 	= 720,
					  .height 	= 480,
					  .field	= V4L2_FIELD_INTERLACED,
					  .sizeimage	= (128*1024),
				  }
			  },
	},
	.controlcount	=  0,
	.controls	=  NULL
};

struct ivtv_v4l2_stream dec_yuv_stream = {
/*Decoder YUV*/
	.s_flags	= 0,
	.id		= -1,
	.v4l_reg_type	= VFL_TYPE_GRABBER,
	.format		= {
			  .type	= V4L2_BUF_TYPE_VIDEO_OUTPUT,
			  .fmt	= {
				  .pix	= {
					  .width 	= 720,
					  .height 	= 480,
					  .field	= V4L2_FIELD_INTERLACED,
					  .sizeimage	= (720*720),
				  }
			  },
	},
	.controlcount	=  0,
	.controls	=  NULL
};

/* Initialize v4l2 variables and register v4l2 device */
int ivtv_v4l2_setup(struct ivtv *itv) {
	int x, cont, retval;
	
	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 setup\n");

	/* Set owner for auto usage-incrementing and such */
	SET_MODULE_OWNER(&ivtv_v4l2_fops);

	//switch based on card type
	//  and fill in appropriate v4l2 device
	switch (itv->card_type) {
	case IVTV_350_V1:
		IVTV_DEBUG(IVTV_DEBUG_INFO,"Configuring 350rev1 card\n");
		itv->v4l2.streamcount = IVTV_350_V1_STREAMS;
		/* Disable dec yuv buffers if requested */
		if (itv->options.dec_yuv_buffers == 0) itv->v4l2.streamcount--;
		/* FIXME wrong values */
		itv->v4l2.capabilities = (V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_TUNER|
					  V4L2_CAP_AUDIO|V4L2_CAP_READWRITE|
					  V4L2_CAP_VIDEO_OUTPUT);
		break;
	case IVTV_250_V2:
		IVTV_DEBUG(IVTV_DEBUG_INFO,"Configuring 250rev2 card\n");
		itv->v4l2.streamcount = IVTV_250_V2_STREAMS;
		/* FIXME wrong values */
		itv->v4l2.capabilities = (V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_TUNER|
					  V4L2_CAP_AUDIO|V4L2_CAP_READWRITE);
		break;
	case IVTV_250_V1:
		IVTV_DEBUG(IVTV_DEBUG_INFO,"Configuring 250rev1 card\n");
	default: /* shouldn't happen, treat like V1 */
		itv->v4l2.streamcount = IVTV_250_V1_STREAMS;
		itv->v4l2.capabilities = (V4L2_CAP_VIDEO_CAPTURE|V4L2_CAP_TUNER|
					  V4L2_CAP_AUDIO|V4L2_CAP_READWRITE);
		
		break;
	}

        /* Initial settings */
        itv->v4l2.codec.bitrate_mode	= 0;
        itv->v4l2.codec.bitrate		= 8000000;
        itv->v4l2.codec.bitrate_peak	= 16000000;
        itv->v4l2.codec.stream_type	= IVTV_STREAM_PS;
        itv->v4l2.codec.bframes		= 3;
        itv->v4l2.codec.gop_closure	= 0;
        itv->v4l2.codec.dnr_mode	= 0;
        itv->v4l2.codec.dnr_type	= 0;
        itv->v4l2.codec.dnr_spatial 	= 0;
        itv->v4l2.codec.dnr_temporal	= 0;
	itv->v4l2.codec.aspect		= 2;

	itv->dec_options.hide_last_frame= 1;
	itv->dec_options.pts_low	= 0;
	itv->dec_options.pts_hi		= 0;
	itv->dec_options.gop_offset	= 0;
	itv->dec_options.mute_frames	= 0;

	/* Ctrls */
	itv->dec_options.sf_mute	= 1;
	itv->dec_options.aud_mute	= 0;
	itv->dec_options.smooth		= 1;
	itv->dec_options.fr_mask	= 2;
	itv->dec_options.fr_field	= 1;
	itv->dec_options.decbuffers	= 1;
	itv->dec_options.prebuffer	= 1;
		
	/* Allocate streams */
        itv->v4l2.streams = (struct ivtv_v4l2_stream *)
                        kmalloc((itv->v4l2.streamcount *
                                sizeof(struct ivtv_v4l2_stream)),
                                GFP_KERNEL);
        if (NULL == itv->v4l2.streams) {
                IVTV_DEBUG(IVTV_DEBUG_ERR, "Couldn't allocate v4l2 streams\n");
                retval = -ENOMEM;
                goto ivtv_stream_fail;
        }

	/* pre-init */
	retval = ivtv_v4l2_pre_init(itv);
	if (retval < 0) {
		IVTV_DEBUG(IVTV_DEBUG_ERR, "Error in pre-init\n");
		goto ivtv_pre_init_fail;
	}

        /* Fill in streams with some defaults */
       	memcpy(&itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG], &tmk_mpg_stream,
               sizeof(struct ivtv_v4l2_stream));

       	memcpy(&itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV], &tmk_yuv_stream,
               sizeof(struct ivtv_v4l2_stream));

       	memcpy(&itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI], &tmk_vbi_stream,
               sizeof(struct ivtv_v4l2_stream));

	/* Set some card-specific per-stream stuff here */	
	switch (itv->card_type) { 
        case IVTV_350_V1:
	       	memcpy(&itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG], &dec_mpg_stream,
        	       sizeof(struct ivtv_v4l2_stream));

		if (itv->options.dec_yuv_buffers !=0) {
		       	memcpy(&itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV], &dec_yuv_stream,
        		       sizeof(struct ivtv_v4l2_stream));
		}
		break;
        case IVTV_250_V2:
		break;
        case IVTV_250_V1:
        default: /* shouldn't happen, treat like V1 */
		break;
	}

	for (x=0; x < itv->v4l2.streamcount ; x++) {
		init_waitqueue_head(&itv->v4l2.streams[x].waitq);
		memcpy(&itv->v4l2.streams[x].v4l2dev,
		       &tmk_v4l2dev, sizeof(struct video_device));
		itv->v4l2.streams[x].v4l2dev.priv = itv;
		itv->v4l2.streams[x].ubytes = 0;
		itv->v4l2.streams[x].free_q.vdev = &itv->v4l2.streams[x].v4l2dev;
		itv->v4l2.streams[x].full_q.vdev = &itv->v4l2.streams[x].v4l2dev;
		itv->v4l2.streams[x].dma_q.vdev  = &itv->v4l2.streams[x].v4l2dev;
		INIT_LIST_HEAD(&itv->v4l2.streams[x].free_q.list);
		INIT_LIST_HEAD(&itv->v4l2.streams[x].full_q.list);
		INIT_LIST_HEAD(&itv->v4l2.streams[x].dma_q.list);

		retval = ivtv_init_queue(itv, &itv->v4l2.streams[x].full_q, 0,
				    itv->v4l2.streams[x].format.type);
		if (retval < 0) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,"Error on init_queue 1\n");
			goto ivtv_initq_fail;
		}
		
		retval = ivtv_init_queue(itv, &itv->v4l2.streams[x].dma_q, 0,
				    itv->v4l2.streams[x].format.type);
		if (retval < 0) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,"Error on init_queue 2\n");
			goto ivtv_initq_fail;
		}
	}

	/* Some streams have specific values */
	x = ivtv_init_queue(itv,
			    &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].free_q,
		       	    mpg_buffers,
			    itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].format.type);
	x = ivtv_init_queue(itv,
			    &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV].free_q,
			    yuv_buffers,
			    itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV].format.type);
	x = ivtv_init_queue(itv,
			    &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI].free_q,
			    vbi_buffers,
			    itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI].format.type);

	/* set default minors */
	itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].v4l2dev.minor = itv->num;
	itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_YUV].v4l2dev.minor = 
		itv->num + IVTV_V4L2_YUV_OFFSET;
	//vbi will get offset by v4l, so no offset needed by us
	itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_VBI].v4l2dev.minor = itv->num;

	/* Set any card-specific per-stream stuff here */	
	switch (itv->card_type) { 
        case IVTV_350_V1:
		/* allocate buffers for decoder */
	        x = ivtv_init_queue(itv,
			&itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG]
			.free_q, dec_mpg_buffers,
			itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG].format.type);
		
		itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG].v4l2dev.minor = 
			itv->num + IVTV_V4L2_DEC_OFFSET;

		if (itv->options.dec_yuv_buffers != 0) {
		        x = ivtv_init_queue(itv,
				&itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV]
				.free_q, dec_yuv_buffers,
				itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV].format.type);

			itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV].v4l2dev.minor = 
				itv->num + IVTV_V4L2_YUV_OFFSET + IVTV_V4L2_DEC_OFFSET;
		}

		/* Set poll for decoder parts */
		itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG].v4l2dev.fops->poll = ivtv_dec_poll;
		if (itv->options.dec_yuv_buffers)
			itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_YUV].v4l2dev.fops->poll = ivtv_dec_poll;

		break;
        case IVTV_250_V2:
		break;
        case IVTV_250_V1:
        default: /* shouldn't happen, treat like V1 */
		break;
	}

	/* allocate minor, register, loop until works or out of range */
	for (x=0;x < itv->v4l2.streamcount; x++) {
		cont = 0;
		do {
	 	    if(video_register_device(&itv->v4l2.streams[x].v4l2dev,
					 itv->v4l2.streams[x].v4l_reg_type,
					 itv->v4l2.streams[x].v4l2dev.minor)) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,
					"Device or minor %d not accepted\n",
					itv->v4l2.streams[x].v4l2dev.minor);
		    	itv->v4l2.streams[x].v4l2dev.minor++;
	 	    } else {
			IVTV_DEBUG(IVTV_DEBUG_ERR,
				   "Registered v4l2 device, minor %d\n",
				   itv->v4l2.streams[x].v4l2dev.minor);
			cont = 1;
		    }
		} while ((0 == cont) && 
			 (itv->v4l2.streams[x].v4l2dev.minor<=IVTV_V4L2_MAX_MINOR));
		if (0 == cont) {
		       IVTV_DEBUG(IVTV_DEBUG_ERR,"Couldn't register v4l2 device!\n");
		       /* invalidate so we don't try to unload the device */
		       itv->v4l2.streams[x].v4l2dev.minor = -1;
		       return -ENODEV;
		}
	}

	return 0;

ivtv_pre_init_fail:
	/* needs lots of queue cleanup here -axboe */
ivtv_initq_fail:
	kfree(itv->v4l2.streams);
ivtv_stream_fail:
	return retval;
}

/* After setting the audio.active param, call this to
 *  get the right input.. think of it as a resolver */
int ivtv_set_audio(struct ivtv *itv, int *map) {
	int input,msp_input;
	struct msp_matrix mspm;

	do {
		input = *(map++);
		msp_input = *(map++);
		if (input == itv->v4l2.audio.active) {
			IVTV_DEBUG(IVTV_DEBUG_INFO,
				   "Setting audio to input %d\n", msp_input);
			mspm.input = msp_input;
			mspm.output = itv->v4l2.audio_output;

			ivtv_call_i2c_client(itv,
					     IVTV_MSP3400_I2C_ADDR,
					     MSP_SET_MATRIX,
					     &mspm);
			return 0;
		}
	} while ((msp_input !=0) || (input != 0));

	IVTV_DEBUG(IVTV_DEBUG_ERR,"Invalid audio input, shouldn't happen!\n");

	return -EINVAL;
}

u32 ivtv_pause_encoder(struct ivtv *itv, int cmd) {
	u32 data[16], result=0;
	int x;

        data[0] = 0; /* 0 = pause, 1 = unpause */
	if (cmd) data[0] = 1;

        x = ivtv_api(itv->enc_mbox, &itv->sem_lock, IVTV_API_PAUSE_ENCODER,
		     &result,1, &data[0]);
	return result;
}

/* Called if v4l2 registration is successful. Set video mode here, at least
 * that is required on PAL cards */
int ivtv_v4l2_init(struct video_device *v) {
	struct ivtv *ivtv = v->priv;
	u32 data[IVTV_MBOX_MAX_DATA], result;
	int x;

	/*
	 * only set it on minor 0
	 */
	if (v->minor != 0)
		return 0;

	memset(data, 0, sizeof(data));
	/* set display standard */
	if (ivtv_pal)
		data[0] = 1;
	else
		data[0] = 0;

	x = ivtv_api(ivtv->dec_mbox, &ivtv->sem_lock,IVTV_API_DEC_DISP_STANDARD,
		     &result, 1, &data[0]);

	return 0;
}

/* Called before v4l2 registration */
int ivtv_v4l2_pre_init(struct ivtv *itv) {
	int x, temp, retval = -1;

	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 init\n");
	//Allocate based on card type
	//  allocate capabilities and such based on device type

	/* FIXME too much hardcoding? */
	//inputs
	itv->v4l2.input.active	= 4; 
	itv->v4l2.input.count	= 10;
	itv->v4l2.input.table.input	= tmk_inputs;

	itv->v4l2.audio_output	= 1;

	//audio inputs
	itv->v4l2.audio.active	= 0;
	itv->v4l2.audio.count	= 2;
	itv->v4l2.audio.table.audio	= tmk_audio_inputs;

	//outputs .. none yet (no real 350 support anyways)
	itv->v4l2.output.active = 0;
	itv->v4l2.output.count	= 0;
	itv->v4l2.output.table.output	= NULL;
	
	//standards (NTSC, PAL, SECAM)
	if (ivtv_pal)
		itv->v4l2.standard.active	= 1;
	else
		itv->v4l2.standard.active	= 0;
	itv->v4l2.standard.count	= 3;
	itv->v4l2.standard.table.std	= tmk_standards;
	
	if (itv->v4l2.standard.active == 0) {
		itv->v4l2.codec.framespergop = 15;	// NTSC
		itv->v4l2.codec.framerate	= 0;	// NTSC 30fps
	} else {
		itv->v4l2.codec.framespergop = 12;   // PAL
		itv->v4l2.codec.framerate	= 1;	// PAL  25fps

		/* set pal height in stream defaults */
		tmk_mpg_stream.format.fmt.pix.height = 576;
		tmk_yuv_stream.format.fmt.pix.height = 576;
		tmk_vbi_stream.format.fmt.pix.height = 576;
		dec_mpg_stream.format.fmt.pix.height = 576;
		dec_yuv_stream.format.fmt.pix.height = 576;
	}

	//tuner
	itv->v4l2.tuner.active	= 0;
	if (itv->card_type == IVTV_350_V1) {
		itv->v4l2.tuner.count	= 2;
	} else {
		itv->v4l2.tuner.count	= 1;
	}
	
	itv->v4l2.tuner.table.tuner = (struct v4l2_tuner *)
					kmalloc((itv->v4l2.tuner.count *
					sizeof(struct v4l2_tuner)),
					GFP_KERNEL);

	if (itv->v4l2.tuner.table.tuner == NULL) {
		IVTV_DEBUG(IVTV_DEBUG_ERR, "Couldn't allocate v4l2 tuner\n");
		retval = -ENOMEM;
		goto ivtv_tuner_fail;
	}

	memcpy(itv->v4l2.tuner.table.tuner, &tmk_tuners[0],
		(itv->v4l2.tuner.count * sizeof(struct v4l2_tuner)));

	/* Setup audio */
	/* V4L2_CID_IVTV_FREQ */
	itv->v4l2.audio_meta[0].ctrl = &ivtv_ctrl_menu_freq;
	itv->v4l2.audio_meta[0].menu = ivtv_ctrl_query_freq;
	itv->v4l2.audio_meta[0].mask = ivtv_audio_mask_freq;
	itv->v4l2.audio_meta[0].setting = ivtv_ctrl_menu_freq.default_value;
	itv->v4l2.audio_meta[0].table = &ivtv_audio_tbl_freq[0];

	/* V4L2_CID_IVTV_ENC*/
	itv->v4l2.audio_meta[1].ctrl = &ivtv_ctrl_menu_enc;
	itv->v4l2.audio_meta[1].menu = ivtv_ctrl_query_enc;
	itv->v4l2.audio_meta[1].mask = ivtv_audio_mask_enc;
	itv->v4l2.audio_meta[1].setting = ivtv_ctrl_menu_enc.default_value;
	itv->v4l2.audio_meta[1].table = &ivtv_audio_tbl_enc[0];

	/* V4L2_CID_IVTV_BITRATE*/
	itv->v4l2.audio_meta[2].ctrl = &ivtv_ctrl_menu_bitrate;
	itv->v4l2.audio_meta[2].menu = ivtv_ctrl_query_bitrate;
	itv->v4l2.audio_meta[2].mask = ivtv_audio_mask_bitrate;
	itv->v4l2.audio_meta[2].setting = ivtv_ctrl_menu_bitrate.default_value;
	itv->v4l2.audio_meta[2].table = &ivtv_audio_tbl_bitrate[0];

	/* V4L2_CID_IVTV_MONO*/
	itv->v4l2.audio_meta[3].ctrl = &ivtv_ctrl_menu_mono;
	itv->v4l2.audio_meta[3].menu = ivtv_ctrl_query_mono;
	itv->v4l2.audio_meta[3].mask = ivtv_audio_mask_mono;
	itv->v4l2.audio_meta[3].setting = ivtv_ctrl_menu_mono.default_value;
	itv->v4l2.audio_meta[3].table = &ivtv_audio_tbl_mono[0];

	/* V4L2_CID_IVTV_JOINT*/
	itv->v4l2.audio_meta[4].ctrl = &ivtv_ctrl_menu_joint;
	itv->v4l2.audio_meta[4].menu = ivtv_ctrl_query_joint;
	itv->v4l2.audio_meta[4].mask = ivtv_audio_mask_joint;
	itv->v4l2.audio_meta[4].setting = ivtv_ctrl_menu_joint.default_value;
	itv->v4l2.audio_meta[4].table = &ivtv_audio_tbl_joint[0];

	/* V4L2_CID_IVTV_EMPHASIS*/
	itv->v4l2.audio_meta[5].ctrl = &ivtv_ctrl_menu_emphasis;
	itv->v4l2.audio_meta[5].menu = ivtv_ctrl_query_emphasis;
	itv->v4l2.audio_meta[5].mask = ivtv_audio_mask_emphasis;
	itv->v4l2.audio_meta[5].setting = ivtv_ctrl_menu_emphasis.default_value;
	itv->v4l2.audio_meta[5].table = &ivtv_audio_tbl_emphasis[0];

	/* V4L2_CID_IVTV_CRC*/
	itv->v4l2.audio_meta[6].ctrl = &ivtv_ctrl_menu_crc;
	itv->v4l2.audio_meta[6].menu = ivtv_ctrl_query_crc;
	itv->v4l2.audio_meta[6].mask = ivtv_audio_mask_crc;
	itv->v4l2.audio_meta[6].setting = ivtv_ctrl_menu_crc.default_value;
	itv->v4l2.audio_meta[6].table = &ivtv_audio_tbl_crc[0];

	/* V4L2_CID_IVTV_COPYRIGHT*/
	itv->v4l2.audio_meta[7].ctrl = &ivtv_ctrl_menu_copyright;
	itv->v4l2.audio_meta[7].menu = ivtv_ctrl_query_copyright;
	itv->v4l2.audio_meta[7].mask = ivtv_audio_mask_copyright;
	itv->v4l2.audio_meta[7].setting = ivtv_ctrl_menu_copyright.default_value;
	itv->v4l2.audio_meta[7].table = &ivtv_audio_tbl_copyright[0];

	/* V4L2_CID_IVTV_GEN*/
	itv->v4l2.audio_meta[8].ctrl = &ivtv_ctrl_menu_generation;
	itv->v4l2.audio_meta[8].menu = ivtv_ctrl_query_generation;
	itv->v4l2.audio_meta[8].mask = ivtv_audio_mask_generation;
	itv->v4l2.audio_meta[8].setting = ivtv_ctrl_menu_generation.default_value;
	itv->v4l2.audio_meta[8].table = &ivtv_audio_tbl_generation[0];

	itv->v4l2.codec.audio_bitmap = 0; 
	for (x = 0; x < IVTV_V4L2_AUDIO_MENUCOUNT; x++) {
		temp = itv->v4l2.audio_meta[x].setting;
		itv->v4l2.codec.audio_bitmap |= 
			itv->v4l2.audio_meta[x].table[temp];
	}

	retval = ivtv_set_audio(itv,tmk_audio_mapping);
	if (retval < 0) goto ivtv_a_init_fail;

	//FIXME Setup components here? tuner channel etc

	return 0;

ivtv_a_init_fail:
	kfree(itv->v4l2.tuner.table.tuner);
ivtv_tuner_fail:

	return retval;
}

int ivtv_start_v4l2_stream (struct ivtv_open_id *id) {
	struct ivtv *itv = id->itv;
	u32 data[IVTV_MBOX_MAX_DATA], result;
	int x,vsize,vsync,hsize;
	int type,subtype;
	unsigned int dig;

	/* sem_lock must be held */
	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);

	IVTV_DEBUG(IVTV_DEBUG_INFO,"ivtv start v4l2 stream\n");
	
	/* NTSC/PAL switching */
	vsize = itv->v4l2.streams[0].format.fmt.pix.height;
	vsync = (int) itv->v4l2.streams[0].format.fmt.pix.height/2;
	hsize =  itv->v4l2.streams[0].format.fmt.pix.width;

	type = id->type;

	switch (type) {
	case 2: /* VBI, may be the wrong value */
		subtype = 4;
	case 4: /* Radio, probably not applicable */
		subtype = 2;
		break;
	default:
		subtype = 3;
		break;
	}

	/* clear queues */
	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].full_q,
			&itv->v4l2.streams[id->type].free_q);
	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].dma_q,
			&itv->v4l2.streams[id->type].free_q);
 
	IVTV_DEBUG(IVTV_DEBUG_INFO, "fullq size %d\n",
		   itv->v4l2.streams[id->type].full_q.elements);
	IVTV_DEBUG(IVTV_DEBUG_INFO, "freeq size %d\n",
		   itv->v4l2.streams[id->type].free_q.elements);
	IVTV_DEBUG(IVTV_DEBUG_INFO, "dmaq size %d\n",
		   itv->v4l2.streams[id->type].dma_q.elements);

	/*assign dma block len*/
	/* FIXME this needs a flag */
	data[0] = 1; /* num bytes in block*/
	data[1] = 1; /* use info from sg instead */
        x = __ivtv_api(itv->enc_mbox,IVTV_API_ASSIGN_DMA_BLOCKLEN, &result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 1. Code %d\n",x);
		
	/*assign program index info */
	/* FIXME need more info on this call */
	data[0] = 0; /*Mask    0:Disable */
	data[1] = 0; /*Num_req 0:??/ */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_PGM_INDEX_INFO,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 2. Code %d\n",x);
		
	/*assign stream type */
	data[0] = itv->v4l2.codec.stream_type;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_STREAM_TYPE,
		&result,1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 3. Code %d\n",x);
		
	/*assign output port */
	data[0] = 0; /*0:Memory */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_OUTPUT_PORT,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 4. Code %d\n",x);
		
	/*assign framerate */
	data[0] = itv->v4l2.codec.framerate;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_FRAMERATE,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 5. Code %d\n",x);
		
	/*assign frame size */
	data[0] = vsize; /* height*/
	data[1] = hsize; /* width */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_FRAME_SIZE,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 6. Code %d\n",x);
		
	/*assign aspect ratio */
	data[0] = itv->v4l2.codec.aspect; /*mpeg spec sez 2 */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_ASPECT_RATIO,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 7. Code %d\n",x);
		
	/*assign bitrates */
	/*FIXME i think these settings are valid for compressed only */
	data[0] = itv->v4l2.codec.bitrate_mode;		/*mode */
	data[1] = itv->v4l2.codec.bitrate;		/* bps */
	data[2] = itv->v4l2.codec.bitrate_peak / 400;	/* peak/400 */
	data[3] = 0; 		/*??? */
	data[4] = 0x70;		/*??? */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_BITRATES,
		&result, 5, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 8. Code %d\n",x);

	/*assign gop properties */
	data[0] = itv->v4l2.codec.framespergop;
	data[1] = itv->v4l2.codec.bframes;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_GOP_PROPERTIES,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 9. Code %d\n",x);
		
	/*assign 3 2 pulldown */
	data[0] = itv->v4l2.codec.pulldown;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_3_2_PULLDOWN,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 10. Code %d\n",x);
		
	/*assign gop closure */
	data[0] = itv->v4l2.codec.gop_closure;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_GOP_CLOSURE,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 11. Code %d\n",x);
		
	/*assign audio properties */
	data[0] = itv->v4l2.codec.audio_bitmap;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_AUDIO_PROPERTIES,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 12. Code %d\n",x);

	/*assign dnr filter mode */
	data[0] = itv->v4l2.codec.dnr_mode;
	data[1] = itv->v4l2.codec.dnr_type;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_DNR_FILTER_MODE,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 13. Code %d\n",x);
		
	/*assign dnr filter props*/
	data[0] = itv->v4l2.codec.dnr_spatial;
	data[1] = itv->v4l2.codec.dnr_temporal;
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_DNR_FILTER_PROPS,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 14. Code %d\n",x);
		
	/*assign coring levels */
	data[0] = 0; /*luma_h */
	data[1] = 255; /*luma_l */
	data[2] = 0; /*chroma_h */
	data[3] = 255; /*chroma_l */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_CORING_LEVELS,
		&result, 4, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 15. Code %d\n",x);
		
	/*assign spatial filter type */
	data[0] = 1; /*luma_t: 1 = horiz_only */
	data[1] = 1; /*chroma_t: 1 = horiz_only */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 16. Code %d\n",x);
		
	/*assign frame drop rate */
	data[0] = 0; 
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_FRAME_DROP_RATE,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 17. Code %d\n",x);
		
	/*assign placeholder */
	data[0] = 0; /* type: 0 = Extension/UserData */
	data[1] = 0; /*period */
	data[2] = 0; /*size_t*/
	data[3] = 0; /*arg0 */
	data[4] = 0; /*arg1 */
	data[5] = 0; /*arg2 */
	data[6] = 0; /*arg3 */
	data[7] = 0; /*arg4 */
	data[8] = 0; /*arg5 */
	data[9] = 0; /*arg6 */
	data[10] = 0; /*arg7 */
	data[11] = 0; /*arg8 */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_PLACEHOLDER,
		&result, 12, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 18. Code %d\n\n",x);
		
	/* assign num vsync lines */
	data[0] = vsync; /*??? */
	data[1] = vsync; /* ??? */
        x = __ivtv_api(itv->enc_mbox, IVTV_API_ASSIGN_NUM_VSYNC_LINES,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 20. Code %d\n",x);
		
	if (atomic_read(&itv->capturing) == 0) {
	
		itv->trans_id = 0;
		itv->first_read = 1;

		/* Clear pending interrupts */
		IVTV_DEBUG(IVTV_DEBUG_INFO, "Clearing Interrupts\n");
		writel((readl(itv->reg_mem + IVTV_REG_IRQSTATUS)&0xC8000000),
			(IVTV_REG_IRQSTATUS+itv->reg_mem));

		/* event notification (on) */
		data[0] = 0; /*type: 0 = refresh */
		data[1] = 1; /*on/off: 1 = on */
		data[2] = 0x10000000; /*intr_bit: 0x10000000 = digitizer */
		data[3] = -1; /*mbox_id: -1: none */
	        x = __ivtv_api(itv->enc_mbox,
			     IVTV_API_EVENT_NOTIFICATION, &result, 4, &data[0]);
		if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "startcap error 2. Code %d\n",x);

		/* Disable digitizer (saa7115) */
		IVTV_DEBUG(IVTV_DEBUG_INFO, "Disabling digitizer\n");
		dig=0;
		ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,
				     DECODER_ENABLE_OUTPUT,&dig);
	
		/*initialize input (no args) */
	        x = __ivtv_api(itv->enc_mbox,
			     IVTV_API_INITIALIZE_INPUT,	&result, 0, &data[0]);
		if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "init error 19. Code %d\n\n",x);
		
		/* enable digitizer (saa7115) */
		IVTV_DEBUG(IVTV_DEBUG_INFO, "Enabling digitizer\n");
		dig=1;
		ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,
				     DECODER_ENABLE_OUTPUT,&dig);
	
		IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 100ms\n");
		ivtv_sleep_timeout(HZ/10);
	}
	
	clear_bit(IVTV_F_I_EOS, &itv->i_flags);

	/* begin_capture */
	data[0] = type; /*type: 0 = mpeg */
	data[1] = subtype; /*subtype: 3 = video+audio */
	x = __ivtv_api(itv->enc_mbox, IVTV_API_BEGIN_CAPTURE,
	        &result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "startcap error 1. Code %d\n",x);

	if (atomic_read(&itv->capturing) == 0) {
	        /*Clear the following Interrupt mask bits: 0xd8000000 */
		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE);
	        IVTV_DEBUG(IVTV_DEBUG_IRQ, "IRQ Mask is now: 0x%08x\n",itv->irqmask);
	}

	/*you're live! sit back and await interrupts :)*/
	atomic_inc(&itv->capturing);
	return 0;
}

int ivtv_api_dec_playback_speed(struct ivtv* itv, int fastspeed, int factor, int forward, int mpeg_frame_type_mask, int bframes_per_gop, int mute_audio, int display_fields) {
  u32 data[IVTV_MBOX_MAX_DATA], result;
  data[0] = (fastspeed << 31) | (factor & 0xff);
  data[1] = forward;
  data[2] = mpeg_frame_type_mask;
  data[3] = bframes_per_gop;
  data[4] = mute_audio;
  data[5] = display_fields;

  ivtv_api(itv->dec_mbox, &itv->sem_lock, IVTV_API_DEC_PLAYBACK_SPEED, &result, 6, &data[0]);
  return result;
}

int ivtv_start_v4l2_decode (struct ivtv_open_id *id)
{
	struct ivtv *itv = id->itv;
	u32 data[IVTV_MBOX_MAX_DATA], result;
	int x;
	int type;
	int standard=0;

	/* sem_lock must be held */
	IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0);
	
	type = id->type;

	if (itv->v4l2.standard.active != 0) { /* if not NTSC */
		standard  = 1; /* PAL */
	}

/* this isn't needed until we use buffers for decoding */
	/* clear queues */
	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].full_q,
			&itv->v4l2.streams[id->type].free_q);
	ivtv_move_queue(itv, &itv->v4l2.streams[id->type].dma_q,
			&itv->v4l2.streams[id->type].free_q);

	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder fullq size %d\n",
	itv->v4l2.streams[id->type].full_q.elements);
	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder freeq size %d\n",
	itv->v4l2.streams[id->type].free_q.elements);
	IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder dmaq size %d\n",
	itv->v4l2.streams[id->type].dma_q.elements);
	
	if (atomic_read(&itv->decoding) == 0)
	{
	  /* Clear pending interrupts */
	  IVTV_DEBUG(IVTV_DEBUG_INFO, "Clearing Interrupts\n");
	  writel((readl(itv->reg_mem + IVTV_REG_IRQSTATUS)&0xC8000000),
	  (IVTV_REG_IRQSTATUS+itv->reg_mem));
	}

	/* set display standard */
	data[0] = standard; /* 0 = NTSC, 1 = PAL */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_DISP_STANDARD,
		&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T SET DISPLAY STD %d\n",x);
 
	/* set audio mode */
	data[0] = 0; /* Dual mono-mode action: ??? */
	data[1] = 0; /* stereo mode action: 0=stereo, 1=left, 2=right, 3=mono */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_SELECT_AUDIO,
		&result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T SET AUDIO MODE %d\n",x);
 
	/* set decoder source settings */
	data[0] = id->type; /* Data type: 0 = mpeg from host, 1 = yuv from encoder, 2 = yuv_from_host */
	data[1] = 720; /* YUV source width*/
	if (itv->v4l2.standard.active == 1)
		data[2] = 576; /* YUV source height*/
	else
		data[2] = 480; /* YUV source height*/
	data[3] = itv->v4l2.codec.audio_bitmap; /* Audio settings to use, bitmap. see docs.*/
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_DECODE_SOURCE,
	             &result, 4, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN't INITIALIZE DECODER SOURCE %d\n",x);

#if 0
	/* select event notification */
	data[0] = 0; /* Event: 0 = audio change between stereo and mono */
	data[1] = 1; /* Enable/Disable: 0 = disabled, 1 = enabled */
	data[2] = 0x00010000; /* Bit: interrupt bit to fire */
	data[3] = -1; /* Mailbox to use: -1 = no mailbox needed */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_EVENT_NOTIFICATION,
	             &result, 4, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T INITIALIZE EVENT NOTIFICATION %d\n",x);
#endif
	/* set number of internal decoder buffers */
	data[0] = itv->dec_options.decbuffers; /* 0 = 6 buffers, 1 = 9 buffers */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_DISPLAY_BUFFERS,
	&result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T INITIALIZE # OF DISPLAY BUFFERS %d\n",x);

	/* prebufferring*/
	data[0] = itv->dec_options.prebuffer; /* 0 = no prebuffering, 1 = enabled, see docs */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_BUFFER, &result, 1,&data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN't INITIALIZE BUFFER %d\n",x);
#if 0
	/* set stream input port */
	data[0] = 0; /* 0 = memory, 1 = streaming */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_STREAM_INPUT, &result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T INITIALIZE STREAM INPUT %d\n",x);
	
	/* A/V sync delay */
	data[0] = 0; /* Delay in 90khz ticks. 0 = synced, negative = audio lags, positive = video lags */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_SET_AV_DELAY, &result, 1, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN't INITIALIZE Audio/Vid sync delay %d\n",x);
#endif

	/* start playback */
	data[0] = itv->dec_options.gop_offset;  /* frame to start from (in GOP) */
	data[1] = itv->dec_options.mute_frames; /* # of audio frames to mute */
	x = __ivtv_api(itv->dec_mbox, IVTV_API_DEC_START_PLAYBACK, &result, 2, &data[0]);
	if (x) IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T START PLAYBACK %d\n",x);

	if (atomic_read(&itv->decoding) == 0) {
		/*Clear the following Interrupt mask bits: 0xd8000000 */
		ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE);
	 	IVTV_DEBUG(IVTV_DEBUG_IRQ, "IRQ Mask is now: 0x%08x\n",itv->irqmask);
	}

	/*you're live! sit back and await interrupts :)*/
	atomic_inc(&itv->decoding);
	return 0;
}

void ivtv_v4l2_cleanup(struct ivtv *itv) {
	int x;

	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 unregister\n");

	if (atomic_read(&itv->capturing) >= 0) ivtv_stop_all_captures(itv);
	if (itv->v4l2.tuner.table.tuner) kfree(itv->v4l2.tuner.table.tuner);
	for (x=0; x < itv->v4l2.streamcount; x++) {
		/* Catch a possible kernel panic */
		if (itv->v4l2.streams[x].v4l2dev.minor != -1) {
			video_unregister_device(&itv->v4l2.streams[x].v4l2dev);
		} else {
			IVTV_DEBUG(IVTV_DEBUG_ERR,
				   "invalid v4l2 registration on unload\n");
		}
	}
	if (itv->v4l2.streams) kfree(itv->v4l2.streams);
}

int ivtv_v4l2_open(struct inode *inode, struct file *filp) {
	int x,y=0,minor;
	struct ivtv_open_id *item;
	struct ivtv *itv=NULL;
	
	
	minor = MINOR(inode->i_rdev);

	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 open on minor %d\n", minor);

	/* Find which card this open was on */
	spin_lock_irq(&ivtv_lock);
	for (x=0;x<ivtv_cards_active;x++) {

		/* find out which stream this open was on */
		for (y=0; y < ivtv_cards[x].v4l2.streamcount;y++) {
			IVTV_DEBUG(IVTV_DEBUG_INFO,"current minor %d\n",
				       ivtv_cards[x].v4l2.streams[y].v4l2dev.minor);
			if (ivtv_cards[x].v4l2.streams[y].v4l2dev.minor == minor) {
				itv = ivtv_cards[x].v4l2.streams[y].v4l2dev.priv;
				break;
			}
		}
		/* FIXME ugly :( */
		if (itv !=NULL) break;
	}
	spin_unlock_irq(&ivtv_lock);

	/* FIXME temporary 
	if (y == 2) {
		IVTV_DEBUG(IVTV_DEBUG_ERR, "VBI not supported yet \n");
		return -EINVAL;
	}
	*/
	if (itv != NULL) {
		IVTV_DEBUG(IVTV_DEBUG_INFO,"opened card # %d, stream %d\n",x,y);
		//allocate memory
		item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL);
		if (NULL == item) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,"nomem on v4l2 open\n");
			return -ENOMEM;
		}
		item->itv = itv;
		item->type = y;
		
		INIT_LIST_HEAD(&item->list);

		down(&itv->sem_lock);

		item->open_id = item->itv->open_id++;
		
		list_add_tail (&item->list, &item->itv->client_list);

		up(&itv->sem_lock);
		
		filp->private_data = item;

		return 0;
	}

	/* Couldnt find a device registered on that minor, shouldn't happen! */
	IVTV_DEBUG(IVTV_DEBUG_ERR,"Device on minor %d not found!\n",minor);

	return -ENXIO;
}


int ivtv_v4l2_read(struct file *filp, char *buf, size_t count, loff_t *pos) {
        struct ivtv_open_id *id = filp->private_data;
        struct ivtv *itv = id->itv;
	struct ivtv_v4l2_stream *stream;
        int ret=0;

	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 read\n");

	if (down_interruptible(&itv->sem_lock))
		return -ERESTARTSYS;

	stream = &itv->v4l2.streams[id->type];

	if (!test_and_set_bit(IVTV_F_S_CAP, &stream->s_flags)
	    && stream->id == -1) {
		stream->id = id->open_id;

		ret = ivtv_start_v4l2_stream(id);
		if (ret) {
			IVTV_DEBUG(IVTV_DEBUG_INFO,
				   "Error in v4l2 stream init\n");
			/* bit of a hack... but try to reload firmware
			 * if capture init fails */
			ret = ivtv_firmware_copy(itv);
			if (!ret)
				ret = ivtv_start_v4l2_stream(id);
		}
		stream->seq = 0;
		stream->ubytes = 0;
	} else {
		if (id->open_id != stream->id)
			ret = -EBUSY;
	}

	up(&itv->sem_lock);

	if (ret)
		return ret;

        ret = ivtv_read(id, buf, count, !(filp->f_flags & O_NONBLOCK));

	if (ret > 0)
		*pos += ret;

	return ret;
}

ssize_t ivtv_v4l2_write(struct file *filp, const char *buf, size_t count, loff_t *pos)
{
	struct ivtv_open_id *id = filp->private_data;
        struct ivtv *itv = id->itv;
	struct ivtv_v4l2_stream *stream;
	int ret=0;
	
	if ( (id->type != IVTV_DEC_STREAM_TYPE_MPG) &&
	     (id->type != IVTV_DEC_STREAM_TYPE_YUV)) {
		IVTV_DEBUG(IVTV_DEBUG_ERR, "Write on read-only interface\n");
		return -EINVAL;
	}

	if (down_interruptible(&itv->sem_lock))
		return -ERESTARTSYS;

	// Initialize Decoder
	/* FIXME we'll need to make this its own stream type */
	stream = &itv->v4l2.streams[id->type];
	if (!test_and_set_bit(IVTV_F_S_CAP, &stream->s_flags)) {
		stream->id = id->open_id;
		ret = ivtv_start_v4l2_decode(id);
	} else {
		if (id->open_id != stream->id)
			ret = -EBUSY;
	}

	up(&itv->sem_lock);

	if (ret)
		return ret;

	/* do all the work */
	return ivtv_write(id, buf, count);
}

int ivtv_v4l2_close(struct inode *inode, struct file *filp) {
	   struct ivtv_open_id *id = filp->private_data;
	
	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 close\n");
	
	if (NULL == id) {
		IVTV_DEBUG(IVTV_DEBUG_ERR,"invalid id on v4l2 close\n");
		return -ENODEV;
	}

	if (down_interruptible(&id->itv->sem_lock))
		return -ERESTARTSYS;

	if (id->open_id == id->itv->v4l2.streams[id->type].id) {
		ivtv_close(id);

		clear_bit(IVTV_F_S_CAP, &id->itv->v4l2.streams[id->type].s_flags);
		id->itv->v4l2.streams[id->type].id	  = -1;
	}
	
	list_del(&id->list);

	up(&id->itv->sem_lock);

	kfree(id);

	return 0;
}

/* direct from the latest v4l2 patch */
static unsigned int video_fix_command(unsigned int cmd)
{
	switch (cmd) {
	case VIDIOC_OVERLAY_OLD:
		cmd = VIDIOC_OVERLAY;
		break;
	case VIDIOC_S_PARM_OLD:
		cmd = VIDIOC_S_PARM;
		break;
	case VIDIOC_S_CTRL_OLD:
		cmd = VIDIOC_S_CTRL;
		break;
	case VIDIOC_G_AUDIO_OLD:
		cmd = VIDIOC_G_AUDIO;
		break;
	case VIDIOC_G_AUDOUT_OLD:
		cmd = VIDIOC_G_AUDOUT;
		break;
	default: break;
	}

	return cmd;
}

int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
		                unsigned long arg) {

        struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data;
        struct ivtv *itv = id->itv;

	IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl 0x%08x\n", cmd);

        cmd = video_fix_command(cmd);

	switch (cmd) {
#ifdef SAA7115_REGTEST
	/* ioctls to allow direct access to the saa7115 registers for testing */
	case SAA7115_GET_REG: {
		struct saa7115_reg_t *saa7115_reg = (struct saa7115_reg_t *)arg;

		ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,SAA7115_GET_REG,saa7115_reg);
		break;
	}
	case SAA7115_SET_REG: {
		struct saa7115_reg_t *saa7115_reg = (struct saa7115_reg_t *)arg;

                ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,SAA7115_SET_REG,saa7115_reg);
		break;
	}
#endif
	case IVTV_IOC_ZCOUNT: {
		/* Zeroes out usage count so it can be unloaded in case of
		 * drastic error */

		IVTV_DEBUG(IVTV_DEBUG_INFO,"ivtv ioctl: ZCOUNT\n");

		while (MOD_IN_USE)
			MOD_DEC_USE_COUNT;

		MOD_INC_USE_COUNT;
		break;
	}
	case IVTV_IOC_GET_FB: {
		if (itv->fb_id < 0)
			return -EINVAL;
		if (copy_to_user((int*)arg, &itv->fb_id, sizeof(itv->fb_id)))
			return -EFAULT;

		break;
	}
	case IVTV_IOC_FWAPI: {
		struct ivtv_ioctl_fwapi fwapi;
		int x;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"ivtv ioctl: FWAPI\n");

		if (copy_from_user(&fwapi, (struct ivtv_ioctl_fwapi *) arg,
				   sizeof(struct ivtv_ioctl_fwapi)))
			return -EFAULT;

		/* Encoder
		x = ivtv_api(itv->enc_mbox, &itv->sem_lock, fwapi.cmd,
	                      &fwapi.result, fwapi.args, &fwapi.data[0]);
		*/

		/* Decoder */
		x = ivtv_api(itv->dec_mbox, &itv->sem_lock, fwapi.cmd,
	                      &fwapi.result, fwapi.args, &fwapi.data[0]);

		if (copy_to_user((struct ivtv_ioctl_fwapi *) arg, &fwapi,
				  sizeof(struct ivtv_ioctl_fwapi)))
			return -EFAULT;

		return x;
	}
	case IVTV_IOC_FRAMESYNC: {
		interruptible_sleep_on(&itv->vsync_w);

		if (signal_pending(current))
			return -ERESTARTSYS;

		if (copy_to_user((void*)arg, &itv->dec_timestamp,
				 sizeof(itv->dec_timestamp))) {
			return -EFAULT;
		}

		break;
	}
	case IVTV_IOC_PLAY: {
		u32 data[IVTV_MBOX_MAX_DATA], result;
		data[0] = 0; /* 0-based frame # to start from (in GOP) */
		data[1] = 0; /* # of audio frames to mute */
		if (ivtv_api(itv->dec_mbox, &itv->sem_lock,
			     IVTV_API_DEC_START_PLAYBACK, &result, 2, &data[0]))
			IVTV_DEBUG(IVTV_DEBUG_ERR,
				   "DEC: error starting playback\n");

		break;

	}
	case IVTV_IOC_S_START_DECODE: {
		struct ivtv_cfg_start_decode sd;

		if (copy_from_user(&sd, (struct ivtv_cfg_start_decode *) arg,
				   sizeof(sd)))
			return -EFAULT;

		if ((sd.gop_offset < 0) || (sd.gop_offset > 15))
			return -EINVAL;
		if  (sd.muted_audio_frames < 0)
			return -EINVAL;
		
		itv->dec_options.gop_offset = sd.gop_offset;

		itv->dec_options.mute_frames = sd.muted_audio_frames;
		break;
        }
	case IVTV_IOC_S_STOP_DECODE: {
		struct ivtv_cfg_stop_decode sd;

		if (copy_from_user(&sd, (struct ivtv_cfg_stop_decode *)arg,
				   sizeof(sd)))
			return -EFAULT;

		if ((sd.hide_last < 0) || (sd.hide_last > 1))
			return -EINVAL;
		itv->dec_options.hide_last_frame = sd.hide_last;

		itv->dec_options.pts_low = (u32)(sd.pts_stop & 0xFFFFFFFF);
		itv->dec_options.pts_hi  = (u32)(sd.pts_stop >> 32);
		break;
        }
	case IVTV_IOC_S_SLOW_FAST: {
		struct ivtv_slow_fast sf;
		u32 data[IVTV_MBOX_MAX_DATA], result;

		if (copy_from_user(&sf, (struct ivtv_slow_fast *)arg,
				   sizeof(sf)))
			return -EFAULT;
		
		if ((sf.scale < 0) || (sf.scale > 50))
			return -EINVAL;
		if ((sf.speed < 0) || (sf.speed > 1))
			return -EINVAL;
		
		data[0] = sf.scale;

		if (itv->dec_options.smooth) /* smooth ff */
			data[0] |= 0x40000000;

		if (sf.speed) /* fast forward */
			data[0] |= 0x80000000;

		data[1] = 1; /* Forward. Reverse not supported */
		
		switch (itv->dec_options.fr_mask) {
			case 0:
				data[2] = 1; /* I */
				break;
			case 1:
				data[2] = 3; /* I, P */
				break;
			case 2:
			default: 
				data[2] = 7; /*I, P, B */
				break;
		}
		
		data[3] = 0; /* reverse only, not supported */
		data[4] = itv->dec_options.aud_mute; /* mute while fast/slow */
		data[5] = itv->dec_options.fr_field; /* frame or field at a time */
		data[6] = itv->dec_options.sf_mute; /* # of frames to mute on
						       normal speed resume */

		if (ivtv_api(itv->dec_mbox, &itv->sem_lock,
			     IVTV_API_DEC_PLAYBACK_SPEED, &result, 7, &data[0])) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,
				   "DEC: error in slow/fast mode\n");
		}

		break;
	}
	case IVTV_IOC_PAUSE: {
		u32 data[IVTV_MBOX_MAX_DATA], result;
		data[0] = 0;
		if (ivtv_api(itv->dec_mbox, &itv->sem_lock,
			     IVTV_API_DEC_PAUSE_PLAYBACK, &result, 1, &data[0])) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,
				   "DEC: error pausing\n");
		}

		break;
	}
	case IVTV_IOC_GET_TIMING: {
		struct ivtv_ioctl_framesync timing;
		int ret;

		/* if the value pulled 'efficiently' was OK */
		if (&itv->dec_timestamp.scr != 0) {
			if (copy_to_user((void*)arg, &itv->dec_timestamp,		                                       sizeof(itv->dec_timestamp))) {
				return -EFAULT;
			}

			break;
		}

		/* firmware glitch gave us a bad value. get a good one */
		ret = ivtv_get_timing_info(itv, &timing);

		if (ret) return -EINVAL;

		if (copy_to_user((void*)arg, &timing, sizeof(timing))) {
			return -EFAULT;
		}

		break;
	}

	case VIDIOC_QUERYMENU: {
		struct v4l2_querymenu *qmenu = (struct v4l2_querymenu *)arg;
		
		if (qmenu->id >= V4L2_CID_PRIVATE_BASE) {
			int off = qmenu->id - V4L2_CID_PRIVATE_BASE;
			if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
				u32 i = qmenu->index;
				if ((i >= itv->v4l2.audio_meta[off].ctrl->minimum) &&
				    (i <= itv->v4l2.audio_meta[off].ctrl->maximum)) {
					memcpy(qmenu,
						&itv->v4l2.audio_meta[off].menu[i], 
						sizeof(struct v4l2_querymenu));
				} else {
					IVTV_DEBUG(IVTV_DEBUG_ERR,
							"qmenu: invalid index\n");
					return -EINVAL;
				}
			} else {
				IVTV_DEBUG(IVTV_DEBUG_ERR,
						"qmenu: id out of range\n");
				return -EINVAL;
			}

		}

		break;
	}
			       
	case VIDIOC_QUERYCTRL: {
		struct v4l2_queryctrl *qctrl = (struct v4l2_queryctrl *)arg;

		if (qctrl->id >= V4L2_CID_PRIVATE_BASE) {
			int off = qctrl->id - V4L2_CID_PRIVATE_BASE;
			if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
				memcpy(qctrl,itv->v4l2.audio_meta[off].ctrl, 
						sizeof(struct v4l2_queryctrl));
			} else {
				switch (qctrl->id) {
				case V4L2_CID_IVTV_DEC_SMOOTH_FF:
					qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
					strncpy(qctrl->name, "Smooth Slow/FF", 32);
					qctrl->minimum = 0;
					qctrl->maximum = 1;
					qctrl->default_value = 1;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 0;
					break;
				case V4L2_CID_IVTV_DEC_FR_MASK:
					qctrl->type = V4L2_CTRL_TYPE_INTEGER;
					strncpy(qctrl->name, "Frame Mask", 32);
					qctrl->minimum = 0;
					qctrl->maximum = 2;
					qctrl->default_value = 2;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 0;
					break;
				case V4L2_CID_IVTV_DEC_SP_MUTE:
					qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
					strncpy(qctrl->name,
						"Mute during slow/fast", 32);
					qctrl->minimum = 0;
					qctrl->maximum = 1;
					qctrl->default_value = 1;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 0;
					break;
				case V4L2_CID_IVTV_DEC_FR_FIELD:
					qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
					strncpy(qctrl->name,
						"Toggle frame/field", 32);
					qctrl->minimum = 0;
					qctrl->maximum = 1;
					qctrl->default_value = 1;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 0;
					break;
				case V4L2_CID_IVTV_DEC_AUD_SKIP:
					qctrl->type = V4L2_CTRL_TYPE_INTEGER;
					strncpy(qctrl->name, "Mute audio frames",32);
					qctrl->minimum = 0;
					qctrl->maximum = 15;
					qctrl->default_value = 0;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 0;
					break;
				case V4L2_CID_IVTV_DEC_NUM_BUFFERS:
					qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
					strncpy(qctrl->name,
						"Number of decoder buffers", 32);
					qctrl->minimum = 0;
					qctrl->maximum = 1;
					qctrl->default_value = 1;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 1;
					break;
				case V4L2_CID_IVTV_DEC_PREBUFFER:
					qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
					strncpy(qctrl->name,
						"Decoder prebuffer", 32);
					qctrl->minimum = 0;
					qctrl->maximum = 1;
					qctrl->default_value = 1;
					qctrl->flags = 0;
					qctrl->reserved[0] = 0;
					qctrl->reserved[1] = 1;
					break;
				default:
					IVTV_DEBUG(IVTV_DEBUG_ERR,
							"qctrl: invalid control\n");
					return -EINVAL;
					break;
				}
			}
		
			break;
		}
		
		switch (qctrl->id) {
		case V4L2_CID_BRIGHTNESS:
			qctrl->type = V4L2_CTRL_TYPE_INTEGER;
			strncpy(qctrl->name, "Brightness", 32);
			qctrl->minimum = 0;
			qctrl->maximum = 255;
			qctrl->step = 0;
			qctrl->default_value = 128;
			qctrl->flags = 0;
			qctrl->reserved[0] = 0;
			qctrl->reserved[1] = 0;
			break;
		case V4L2_CID_HUE:
			qctrl->type = V4L2_CTRL_TYPE_INTEGER;
			strncpy(qctrl->name, "Hue", 32);
			qctrl->minimum = -128;
			qctrl->maximum = 127;
			qctrl->step = 0;
			qctrl->default_value = 0;
			qctrl->flags = 0;
			qctrl->reserved[0] = 0;
			qctrl->reserved[1] = 0;
			break;
		case V4L2_CID_SATURATION:
			qctrl->type = V4L2_CTRL_TYPE_INTEGER;
			strncpy(qctrl->name, "Saturation", 32);
			qctrl->minimum = 0;
			qctrl->maximum = 127;
			qctrl->step = 0;
			qctrl->default_value = 64;
			qctrl->flags = 0;
			qctrl->reserved[0] = 0;
			qctrl->reserved[1] = 0;
			break;
		case V4L2_CID_CONTRAST:
			qctrl->type = V4L2_CTRL_TYPE_INTEGER;
			strncpy(qctrl->name, "Contrast", 32);
			qctrl->minimum = 0;
			qctrl->maximum = 127;
			qctrl->step = 0;
			qctrl->default_value = 64;
			qctrl->flags = 0;
			qctrl->reserved[0] = 0;
			qctrl->reserved[1] = 0;
			break;
		case V4L2_CID_AUDIO_VOLUME:
			qctrl->type = V4L2_CTRL_TYPE_INTEGER;
			strncpy(qctrl->name, "Volume", 32);
			qctrl->minimum = 0;
			qctrl->maximum = 65535;
			qctrl->step = 0;
			qctrl->default_value = 65535;
			qctrl->flags = 0;
			qctrl->reserved[0] = 0;
			qctrl->reserved[1] = 0;
			break;
		case V4L2_CID_AUDIO_MUTE:
			qctrl->type = V4L2_CTRL_TYPE_INTEGER;
			strncpy(qctrl->name, "Mute", 32);
			qctrl->minimum = 0;
			qctrl->maximum = 1;
			qctrl->step = 0;
			qctrl->default_value = 1;
			qctrl->flags = 0;
			qctrl->reserved[0] = 0;
			qctrl->reserved[1] = 0;
			break;
		default:
			IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: invalid control\n");
			return -EINVAL;
		}
		break;
	}
	case VIDIOC_S_CTRL: {
		struct v4l2_control *vctrl = (struct v4l2_control *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set control\n");

		if (vctrl->id >= V4L2_CID_PRIVATE_BASE) {
			int off = vctrl->id - V4L2_CID_PRIVATE_BASE;
			s32 v = vctrl->value;
			if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
				if ((v<=itv->v4l2.audio_meta[off].ctrl->maximum) &&
				    (v>=itv->v4l2.audio_meta[off].ctrl->minimum)) {
					itv->v4l2.audio_meta[off].setting = v;
					/* presumably value has changed.
					 * we should update the bitmap */
					itv->v4l2.codec.audio_bitmap &= 
						~itv->v4l2.audio_meta[off].mask;
					itv->v4l2.codec.audio_bitmap |= 
						itv->v4l2.audio_meta[off].table[v];

					/* Also upade the digitizer setting */
					if (0 == off) { /* audio input bitrate */
						int vrate = (int)v;
						/* FIXME not obvious how this works
						 *  (see ivtv_ctrl_query_freq[]) */
						ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_SET_AUDIO,&vrate);
					}
				} else {
					IVTV_DEBUG(IVTV_DEBUG_ERR,
						"ctrl: value out of range\n");
				return -ERANGE;
				}
			} else {
				switch (vctrl->id) {
				case V4L2_CID_IVTV_DEC_SMOOTH_FF:
					if ((v < 0) || (v > 1)) return -ERANGE;
					itv->dec_options.smooth = vctrl->value;
					break;
				case V4L2_CID_IVTV_DEC_FR_MASK:
					if ((v < 0) || (v > 2)) return -ERANGE;
					 itv->dec_options.fr_mask = vctrl->value;
					break;
				case V4L2_CID_IVTV_DEC_SP_MUTE:
					if ((v < 0) || (v > 1)) return -ERANGE;
					itv->dec_options.aud_mute = vctrl->value;
					break;
				case V4L2_CID_IVTV_DEC_FR_FIELD:
					if ((v < 0) || (v > 1)) return -ERANGE;
					itv->dec_options.fr_field = vctrl->value;
					break;
				case V4L2_CID_IVTV_DEC_AUD_SKIP:
					if ((v < 0) || (v > 15)) return -ERANGE;
					itv->dec_options.mute_frames = vctrl->value;
					break;
				case V4L2_CID_IVTV_DEC_NUM_BUFFERS:
					if ((v < 0) || (v > 1)) return -ERANGE;
					itv->dec_options.decbuffers = vctrl->value;
					break;
				case V4L2_CID_IVTV_DEC_PREBUFFER:
					if ((v < 0) || (v > 1)) return -ERANGE;
					itv->dec_options.prebuffer = vctrl->value;
					break;
				default:
					IVTV_DEBUG(IVTV_DEBUG_ERR,
							"ctrl: invalid control\n");
					return -EINVAL;
				}
			}
		
			break;
		}
		
		switch (vctrl->id) {
		case V4L2_CID_BRIGHTNESS: {
			struct saa7114 pic;

			if (vctrl->value < 0 || vctrl->value > 255) {
				IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid brightness value: %d\n", vctrl->value);
				return -EINVAL;
			}
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			pic.bright = vctrl->value;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_SET_PICTURE,&pic);
			break;
		}
		case V4L2_CID_HUE: {
			struct saa7114 pic;

			if (vctrl->value < -128 || vctrl->value > 127) {
				IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid hue value: %d\n", vctrl->value);
				return -EINVAL;
			}
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			pic.hue = vctrl->value;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_SET_PICTURE,&pic);
			break;
		}
		case V4L2_CID_SATURATION: {
			struct saa7114 pic;

			if (vctrl->value < 0 || vctrl->value > 127) {
				IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid saturation value: %d\n", vctrl->value);
				return -EINVAL;
			}
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			pic.sat = vctrl->value;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_SET_PICTURE,&pic);
			break;
		}
		case V4L2_CID_CONTRAST: {
			struct saa7114 pic;

			if (vctrl->value < 0 || vctrl->value > 127) {
				IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid contrast value: %d\n", vctrl->value);
				return -EINVAL;
			}
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			pic.contrast = vctrl->value;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_SET_PICTURE,&pic);
			break;
		}
		case V4L2_CID_AUDIO_VOLUME: {
			struct video_audio va;

			if (vctrl->value > 65535 || vctrl->value < 0) {
				IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid value for volume: %d", vctrl->value);
				return -EINVAL;
			}
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOCGAUDIO,&va);
			va.volume = vctrl->value;
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOCSAUDIO,&va);
			break;
		}
		case V4L2_CID_AUDIO_MUTE: {
			struct video_audio va;
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOCGAUDIO,&va);
			if (vctrl->value)
				va.flags |= VIDEO_AUDIO_MUTE;
			else
				va.flags = (va.flags & ~(VIDEO_AUDIO_MUTE));
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOCSAUDIO,&va);
			break;
		}
		default:
			IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid control\n");
			return -EINVAL;
		}

		break;
	}
	case VIDIOC_G_CTRL: {
		struct v4l2_control *vctrl = (struct v4l2_control *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: get control\n");

		if (vctrl->id >= V4L2_CID_PRIVATE_BASE) {
			int off = vctrl->id - V4L2_CID_PRIVATE_BASE;
			if (off < IVTV_V4L2_AUDIO_MENUCOUNT) {
				vctrl->value =itv->v4l2.audio_meta[off].setting;
			} else {
				switch (vctrl->id) {
				case V4L2_CID_IVTV_DEC_SMOOTH_FF:
					vctrl->value = itv->dec_options.smooth;
					break;
				case V4L2_CID_IVTV_DEC_FR_MASK:
					vctrl->value = itv->dec_options.fr_mask;
					break;
				case V4L2_CID_IVTV_DEC_SP_MUTE:
					vctrl->value = itv->dec_options.aud_mute;
					break;
				case V4L2_CID_IVTV_DEC_FR_FIELD:
					vctrl->value = itv->dec_options.fr_field;
					break;
				case V4L2_CID_IVTV_DEC_AUD_SKIP:
					vctrl->value = itv->dec_options.mute_frames;
					break;
				case V4L2_CID_IVTV_DEC_NUM_BUFFERS:
					vctrl->value = itv->dec_options.decbuffers;
					break;
				case V4L2_CID_IVTV_DEC_PREBUFFER:
					vctrl->value = itv->dec_options.prebuffer;
					break;
				default:
					IVTV_DEBUG(IVTV_DEBUG_ERR,
							"ctrl: invalid control\n");
					return -EINVAL;
				}
			}
		
			break;
		}
		
		switch (vctrl->id) {
		case V4L2_CID_BRIGHTNESS: {
			struct saa7114 pic;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			vctrl->value = pic.bright;
			break;
		}
		case V4L2_CID_HUE: {
			struct saa7114 pic;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			vctrl->value = pic.hue;
			break;
		}
		case V4L2_CID_SATURATION: {
			struct saa7114 pic;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			vctrl->value = pic.sat;
			break;
		}
		case V4L2_CID_CONTRAST: {
			struct saa7114 pic;
			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,DECODER_GET_PICTURE,&pic);
			vctrl->value = pic.contrast;
			break;
		}
		case V4L2_CID_AUDIO_VOLUME: {
			struct video_audio va;
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOCGAUDIO,&va);
			vctrl->value = va.volume;
			break;
		}
		case V4L2_CID_AUDIO_MUTE: {
			struct video_audio va;
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,VIDIOCGAUDIO,&va);
			vctrl->value = (va.flags & VIDEO_AUDIO_MUTE);
			break;
		}
		default:
			IVTV_DEBUG(IVTV_DEBUG_ERR, "ctrl: invalid control\n");
			return -EINVAL;
		}
		break;
	}
	case VIDIOC_QUERYCAP: {
		struct v4l2_capability *vcap = (struct v4l2_capability *)arg;
		
		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: querycap\n");

		/* driver name */
		strcpy(vcap->driver,IVTV_DRIVER_NAME);

		/* card type */
		strcpy(vcap->card,id->itv->v4l2.streams[id->type].v4l2dev.name);

		/* bus info.. card # will do */
		sprintf(vcap->bus_info, "%d", itv->num);

		/* version */
		vcap->version = IVTV_DRIVER_VERSION;

		/* capabilities */
		vcap->capabilities = itv->v4l2.capabilities;

		/* reserved.. must set to 0! */
		vcap->reserved[0] = vcap->reserved[1] = vcap->reserved[2] =
			vcap->reserved[3] = 0;
		break;
	}
	case VIDIOC_ENUMINPUT: {
		struct v4l2_input *vin = (struct v4l2_input *)arg;
		
		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: enuminput\n");

		if ((vin->index < 0) || (vin->index >= itv->v4l2.input.count))
			return -EINVAL;

		/* set it to defaults from our table */
		memcpy(vin,
		       &itv->v4l2.input.table.input[vin->index],
		       sizeof(struct v4l2_input));

		/* set the standard to whatever our overall standard is */
		vin->std = tmk_standards[itv->v4l2.standard.active].id;
		vin->status = 0; /*FIXME status isn't always ok... */

		break;
	} 
					       
	case VIDIOC_G_FMT: {
		struct v4l2_format *vfmt = (struct v4l2_format *)arg;
		
		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: get format\n");

		/* FIXME switch on stream type */
		memcpy (vfmt, &itv->v4l2.streams[0].format, sizeof(struct v4l2_format));
		break;
	}
	case VIDIOC_S_FMT: {
		struct v4l2_format *vfmt = (struct v4l2_format *)arg;
		struct video_window wind;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set format\n");

		/* FIXME only sets resolution for now */
		wind.width = vfmt->fmt.pix.width;
		wind.height = vfmt->fmt.pix.height;
		ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,
				     DECODER_SET_SIZE,&wind);

		/* FIXME switch on stream type, bounds checking */
		memcpy (&itv->v4l2.streams[0].format, vfmt, sizeof(struct v4l2_format));
		/* Adjust res in YUV also */
		itv->v4l2.streams[1].format.fmt.pix.height = vfmt->fmt.pix.height;
		itv->v4l2.streams[1].format.fmt.pix.width = vfmt->fmt.pix.width;

		break;
	}
	case VIDIOC_G_INPUT: {
		int *inp = (int *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: get input\n");

		*inp = itv->v4l2.input.active;
		break;
	}
	case VIDIOC_S_INPUT: {
		int a_in,inp = *(int *)arg;
		struct msp_matrix mspm;
		
		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set input\n");
		
                if ((inp < 0) || (inp >= itv->v4l2.input.count)) return -EINVAL;

		if (inp == itv->v4l2.input.active) {
			IVTV_DEBUG(IVTV_DEBUG_INFO, "Input unchanged\n");
		} else {
			IVTV_DEBUG(IVTV_DEBUG_INFO, "Changing input from %d to %d\n",
				   itv->v4l2.input.active, inp);

			itv->v4l2.input.active = inp;
			itv->v4l2.audio.active	=
				itv->v4l2.input.table.input[inp].audioset;

			/* Mute sound to avoid pop */
			mspm.input = 8;
			mspm.output = itv->v4l2.audio_output;
			ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR
					     ,MSP_SET_MATRIX,&mspm);

			if (0 != ivtv_pause_encoder(itv, 0)) 
				IVTV_DEBUG(IVTV_DEBUG_ERR,
					   "Input: Error pausing stream\n");

			ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,
					     DECODER_SET_INPUT,&inp);

			/* Pause to let sound calm down */
			ivtv_sleep_timeout(HZ/33);

			if (0 != ivtv_pause_encoder(itv, 1)) 
				IVTV_DEBUG(IVTV_DEBUG_ERR,
					   "Input: Error unpausing stream\n");

			/* FIXME Needs to be card-specific */
			a_in = ivtv_set_audio(itv,tmk_audio_mapping);
			if (a_in < 0) return a_in;
		}
		break;
	}
	case VIDIOC_G_FREQUENCY: {
		struct v4l2_frequency *vf=(struct v4l2_frequency *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: get frequency\n");

		if ((vf->tuner < 0) || (vf->tuner >= itv->v4l2.tuner.count))
			return -EINVAL;
		vf->frequency = itv->v4l2.freq.frequency;
		break;
	}
	case VIDIOC_S_FREQUENCY: {
		struct v4l2_frequency *vf=(struct v4l2_frequency *)arg;
		struct msp_matrix mspm;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set frequency\n");

		if ((vf->tuner < 0) || (vf->tuner >= itv->v4l2.tuner.count))
			return -EINVAL;
		itv->v4l2.freq.frequency = vf->frequency;

		/* Mute sound to avoid pop */
		mspm.input = 8;
		mspm.output = itv->v4l2.audio_output;
		ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,MSP_SET_MATRIX,&mspm);

		if (0 != ivtv_pause_encoder(itv, 0)) 
			IVTV_DEBUG(IVTV_DEBUG_ERR, "Freq: Error pausing stream\n");

		/* Set frequency */
		ivtv_call_i2c_client(itv,IVTV_TUNER_I2C_ADDR,
				     VIDIOCSFREQ,&itv->v4l2.freq.frequency);

		/* Pause to let sound calm down */
		ivtv_sleep_timeout(HZ/33);

		if (0 != ivtv_pause_encoder(itv, 1)) 
			IVTV_DEBUG(IVTV_DEBUG_ERR, "Freq: Error unpausing stream\n");

		/* Unmute */
		ivtv_set_audio(itv,tmk_audio_mapping);

		break;
	}
	case VIDIOC_ENUMSTD: {
		struct v4l2_standard *vs=(struct v4l2_standard *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: enum standard\n");

		if ((vs->index < 0) || (vs->index >= itv->v4l2.standard.count))
			return -EINVAL;
		
		memcpy(vs, &itv->v4l2.standard.table.std[vs->index],
		 	sizeof(struct v4l2_standard));

		break;
	}
	case VIDIOC_G_STD: {
		v4l2_std_id *vs=(v4l2_std_id *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: get standard\n");

		*vs = itv->v4l2.standard.table.std[itv->v4l2.standard.active].id;
		break;
	}
	case VIDIOC_S_STD: {
		v4l2_std_id *vs=(v4l2_std_id *)arg;
                struct video_channel v;
		int x;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set standard\n");

		for (x = 0; x < itv->v4l2.standard.count; x++) {
			if (itv->v4l2.standard.table.std[x].id & *vs) {
				IVTV_DEBUG(IVTV_DEBUG_INFO,
					   "Switching standard to %s.\n",
					   itv->v4l2.standard.table.std[x].name);
				itv->v4l2.standard.active = x;
				/* fixme set standard here */
				switch (itv->v4l2.standard.active) {
				case 0: /* NTSC */
					v.norm = VIDEO_MODE_NTSC;
					break;
				case 1: /* PAL */
					v.norm = VIDEO_MODE_PAL;
					break;
				case 2: /* SECAM */
					v.norm = VIDEO_MODE_SECAM;
					break;
				default:
					break;
				}

				/* Tuner */
				ivtv_call_i2c_client(itv,
						     IVTV_TUNER_I2C_ADDR,
						     VIDIOCSCHAN,&v);
				/* Tuner Audio */
                                ivtv_call_i2c_client(itv,
                                                     IVTV_MSP3400_I2C_ADDR,
                                                     VIDIOCSCHAN,&v);
				/* Digitizer */
		                ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,
			                             DECODER_SET_NORM,&v.norm);

                                if (itv->v4l2.standard.active == 0) { // NTSC
                                        itv->v4l2.codec.framespergop = 15;
                                        itv->v4l2.codec.framerate    = 0;
                                } else { // PAL
                                        itv->v4l2.codec.framespergop = 12;
                                        itv->v4l2.codec.framerate    = 1;
                                }

				return 0;
			}
		}
		return -EINVAL;
	}
	case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */
		struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: set tuner\n");

		if ((vt->index < 0) || (vt->index >= itv->v4l2.tuner.count))
			return -EINVAL;
		/* looks like tuner.c doesn't support selection 
		 * fallback to stereo... */
		vt->audmode = V4L2_TUNER_MODE_STEREO;

		break;
	}
	case VIDIOC_G_TUNER: {
		struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;
		int sig=0;

		IVTV_DEBUG(IVTV_DEBUG_INFO,"v4l2 ioctl: get tuner\n");

		if ((vt->index < 0) || (vt->index >= itv->v4l2.tuner.count))
			return -EINVAL;

		memcpy(vt, &itv->v4l2.tuner.table.tuner[vt->index],
		       sizeof(struct v4l2_tuner));
		
		
                ivtv_call_i2c_client(itv,IVTV_SAA7115_I2C_ADDR,
				     DECODER_GET_STATUS,&sig);

		if (sig & DECODER_STATUS_GOOD) {
			vt->signal = 65535; /* best possible signal */
		} else {
			vt->signal = 0;
		}
		break;
	}
	case MSP_SET_MATRIX: {
		struct msp_matrix *mspm = (struct msp_matrix *)arg;

		/* FIXME hardcoding! */
		if ((mspm->input < 1) || (mspm->input > 8)) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,"Invalid audio input!\n");
			return -EINVAL;
		}
		if ((mspm->output < 0) || (mspm->output > 3)) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,"Invalid audio output!\n");
			return -EINVAL;
		}

		itv->v4l2.audio_output = mspm->output;

		IVTV_DEBUG(IVTV_DEBUG_INFO,
			   "v4l2 ioctl: set matrix in=%d,out=%d\n",
			   mspm->input,
			   mspm->output);

		ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,MSP_SET_MATRIX, mspm);
		break;
	}
	case IVTV_IOC_G_CODEC: {
		struct ivtv_ioctl_codec *codec=(struct ivtv_ioctl_codec *)arg;

		/* FIXME: bounds check? */
		memcpy(codec, &(itv->v4l2.codec), sizeof(struct ivtv_ioctl_codec));
		break;
	}
	case IVTV_IOC_S_CODEC: {
		struct ivtv_ioctl_codec *codec=(struct ivtv_ioctl_codec *)arg;

		/* FIXME: insert abundant parameter validation here */
		if((codec->bitrate == 0) || (codec->bitrate_peak == 0) ||
		   (codec->bitrate > codec->bitrate_peak) ) {
			IVTV_DEBUG(IVTV_DEBUG_ERR,"ivtv ioctl: set "
				   "bitrate=%u < peak=%u: failed\n",
				   codec->bitrate, codec->bitrate_peak);
			return -EINVAL;
		} else {
	  		/* Passed the garbage check */
	  		memcpy(&(itv->v4l2.codec), codec,
				sizeof(struct ivtv_ioctl_codec));
		}

		/* VCD streamtype has some quirks. Handle them here */
		if ((codec->stream_type == IVTV_STREAM_VCD) ||
		    (codec->stream_type == IVTV_STREAM_MPEG1)) {
		        struct v4l2_format *vfmt = (struct v4l2_format *)arg;
			struct video_window wind;
			int tmpsize = 480;

			if (itv->v4l2.standard.active == 1) tmpsize = 576;
			
			IVTV_DEBUG(IVTV_DEBUG_INFO,"ivtv ioctl: mpeg1_stream "
					           "size %d\n", tmpsize);

			/* so far it looks like you can change width at will     *
		 	 * but the compressor is unhappy when the height changes *
			 * to anything other than 240 */
		        wind.width = 352; 
			wind.height = tmpsize;
			vfmt->fmt.pix.width = 352;
			vfmt->fmt.pix.height = tmpsize / 2;

		        ivtv_call_i2c_client(itv,
				IVTV_SAA7115_I2C_ADDR,DECODER_SET_SIZE,&wind);
		        memcpy (&itv->v4l2.streams[0].format,
				vfmt, sizeof(struct v4l2_format));
		}

		break;
	}
/* DEBUGGING ONLY */
#if 0
	case 0xFEAF: { /* tmk test msp call */
		u32 *argarray = (u32 *)arg;
		ivtv_call_i2c_client(itv,IVTV_MSP3400_I2C_ADDR,0xFEAF, argarray);
		break;
	}
#endif
	case 0x00005401: /* Handle isatty() calls */
		return -EINVAL;
	default:
		/* If it got here, it's probably not supported.. */
		IVTV_DEBUG(IVTV_DEBUG_ERR,"ivtv-api.c: unknown ioctl 0x%08x\n", cmd);
		return -ENOTTY;
	}
	return 0;
}

