diff -upN reference/drivers/media/Kconfig current/drivers/media/Kconfig --- reference/drivers/media/Kconfig Tue Jun 15 11:09:30 2004 +++ current/drivers/media/Kconfig Thu Jun 17 12:58:30 2004 @@ -34,14 +34,15 @@ source "drivers/media/common/Kconfig" config VIDEO_TUNER tristate - default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_MXB=y || VIDEO_CX88=y - default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_MXB=m || VIDEO_CX88=m + default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_MXB=y || VIDEO_CX88=y || VIDEO_IVTV=y + default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_MXB=m || VIDEO_CX88=m || VIDEO_IVTV=m + depends on VIDEO_DEV config VIDEO_BUF tristate - default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_SAA7146=y || VIDEO_CX88=y - default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_SAA7146=m || VIDEO_CX88=m + default y if VIDEO_BT848=y || VIDEO_SAA7134=y || VIDEO_SAA7146=y || VIDEO_CX88=y || VIDEO_IVTV=y + default m if VIDEO_BT848=m || VIDEO_SAA7134=m || VIDEO_SAA7146=m || VIDEO_CX88=m || VIDEO_IVTV=m depends on VIDEO_DEV config VIDEO_BTCX diff -upN reference/drivers/media/video/Kconfig current/drivers/media/video/Kconfig --- reference/drivers/media/video/Kconfig Wed Jun 16 08:19:19 2004 +++ current/drivers/media/video/Kconfig Thu Jun 17 12:58:30 2004 @@ -22,6 +22,25 @@ config VIDEO_BT848 To compile this driver as a module, choose M here: the module will be called bttv. +config VIDEO_IVTV + tristate "IVTV Video For Linux" + depends on VIDEO_DEV && PCI && I2C_ALGOBIT && SOUND + ---help--- + Support for Hauppauge WinTv PVR 250 and 350 boards. + + If you say Y or M here, you need to say Y or M to "I2C support" and + "I2C bit-banging interfaces" in the character device section. + + Saying M here will compile this driver as a module (ivtv). + +config VIDEO_IVTV_FB + tristate "IVTV Video For Linux Framebuffer" + depends on VIDEO_IVTV && FB && PCI + ---help--- + Support for Hauppauge WinTv PVR 350 boards TV Out via framebuffer. + + Saying M here will compile this driver as a module (ivtv-fb). + config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" depends on VIDEO_DEV && ISA diff -upN reference/drivers/media/video/Makefile current/drivers/media/video/Makefile --- reference/drivers/media/video/Makefile Mon Apr 5 16:26:26 2004 +++ current/drivers/media/video/Makefile Thu Jun 17 12:58:30 2004 @@ -8,12 +8,16 @@ zoran-objs := zr36120.o zr36120_i2c zr36067-objs := zoran_procfs.o zoran_device.o \ zoran_driver.o zoran_card.o +ivtv-objs := ivtv-driver.o ivtv-i2c.o ivtv-api.o + obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \ tda7432.o tda9875.o ir-kbd-i2c.o ir-kbd-gpio.o obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o +obj-$(CONFIG_VIDEO_IVTV) += msp3400.o saa7115.o tveeprom.o ivtv.o saa7127.o +obj-$(CONFIG_VIDEO_IVTV_FB) += ivtv-fb.o obj-$(CONFIG_VIDEO_ZR36120) += zoran.o obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o diff -upN /dev/null current/drivers/media/video/ivtv-api.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/ivtv-api.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,3151 @@ +/* License: GPL + * Author: Kevin Thayer + * + * 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 + +static int ivtv_v4l2_init(struct video_device *v); +static int ivtv_v4l2_close(struct inode *inode, struct file *filp); +static int ivtv_v4l2_open(struct inode *inode, struct file *filp); +static int ivtv_v4l2_read(struct file *filp, char *buf, size_t count, + loff_t * pos); +static ssize_t ivtv_v4l2_write(struct file *filp, const char *buf, size_t count, + loff_t * pos); +static int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ivtv_v4l2_pre_init(struct ivtv *itv); + +struct file_operations ivtv_v4l2_fops = { + owner:THIS_MODULE, + 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 */ + +static struct video_device tmk_v4l2dev = { /*values that work with the author's card */ + .owner = THIS_MODULE, + .name = "Vanilla iTVC15 card", + .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_OVERLAY | + VID_TYPE_CLIPPING | VID_TYPE_SCALES, + .fops = &ivtv_v4l2_fops, + .minor = -1, +}; + +/* 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"); + + //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.speed.mute = 1; + itv->dec_options.speed.aud_mute = 0; + itv->dec_options.speed.smooth = 1; + itv->dec_options.speed.fr_mask = 2; + itv->dec_options.speed.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); + + itv->v4l2.streams[x].v4l2dev = video_device_alloc(); + if (NULL == itv->v4l2.streams[x].v4l2dev) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Couldn't allocate v4l2 video_device\n"); + retval = -ENOMEM; + goto ivtv_videodev_fail; + } + + 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; + } + + itv->v4l2.streams[x].v4l2dev->dev = &itv->dev->dev; + itv->v4l2.streams[x].v4l2dev->release = video_device_release; + } + + /* Some streams have specific values */ + x = ivtv_init_queue(itv, + &itv->v4l2.streams[IVTV_ENC_STREAM_TYPE_MPG].free_q, + itv->options.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, + itv->options.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, + itv->options.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, itv->options.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, + itv->options.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; + ivtv_v4l2_init(itv->v4l2.streams[x].v4l2dev); + } + } 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_videodev_fail: + for (x = 0; x < itv->v4l2.streamcount; x++) + if (itv->v4l2.streams[x].v4l2dev != NULL) + video_device_release(itv->v4l2.streams[x].v4l2dev); + /* 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->enc_msem, 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->dec_msem, + 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"); + return -ENOMEM; + } + + 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) { + kfree(itv->v4l2.tuner.table.tuner); + return retval; + } + //FIXME Setup components here? tuner channel etc + return 0; +} + +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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, 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, &itv->enc_msem, 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, &itv->enc_msem, 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, &itv->enc_msem, 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, &itv->enc_msem, + 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, &itv->enc_msem, 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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, 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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, + 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, &itv->enc_msem, 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, &itv->enc_msem, + 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)); + +#if 0 + /* 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, &itv->enc_msem, + IVTV_API_EVENT_NOTIFICATION, &result, 4, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "startcap error 2. Code %d\n", x); +#endif + + /* 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, &itv->enc_msem, + 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); + } + + /* FIXME this is for mpg captures only i think */ + 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, &itv->enc_msem, 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->dec_msem, 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; + struct ivtv_v4l2_stream *stream; + int x; + int type; + int standard = 0; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Start v4l2_decode \n"); + /* sem_lock must be held */ + IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 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; + } + + stream = &itv->v4l2.streams[id->type]; + if ((stream->id == -1) && !test_bit(IVTV_F_S_CAP, &stream->s_flags)) { + set_bit(IVTV_F_S_CAP, &stream->s_flags); + stream->id = id->open_id; + IVTV_DEBUG(IVTV_DEBUG_INFO, "Granting ownership to id %d\n", + id->open_id); + } else { + if (id->open_id != stream->id) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder busy\n"); + return -EBUSY; + } + if (test_bit(IVTV_F_S_CAP, &stream->s_flags)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Decoder doesn't need init\n"); + return 0; + } + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Starting v4l2_decode \n"); + + 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)); + } + + if (test_bit(IVTV_F_S_UNINIT, &id->itv->v4l2.streams[id->type].s_flags)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Setting some initial decoder settings\n"); + + /* set display standard */ + data[0] = standard; /* 0 = NTSC, 1 = PAL */ + x = ivtv_api(itv->dec_mbox, + &itv->dec_msem, + 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, + &itv->dec_msem, + 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 number of internal decoder buffers */ + data[0] = itv->dec_options.decbuffers; /* 0 = 6 buffers, + 1 = 9 buffers */ + x = ivtv_api(itv->dec_mbox, + &itv->dec_msem, + 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, + &itv->dec_msem, + IVTV_API_DEC_BUFFER, &result, 1, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "COULDN't INITIALIZE PREBUFFER %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, + &itv->dec_msem, + IVTV_API_DEC_DECODE_SOURCE, &result, 4, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "COULDN'T INITIALIZE DECODER SOURCE %d\n", + x); + + clear_bit(IVTV_F_S_UNINIT, + &id->itv->v4l2.streams[id->type].s_flags); + } else { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Decoder already configured, skipping extra setup\n"); + } +#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, &itv->dec_msem, + 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 +#if 0 + /* set stream input port */ + data[0] = 0; /* 0 = memory, 1 = streaming */ + x = ivtv_api(itv->dec_mbox, &itv->dec_msem, 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, &itv->dec_msem, 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 + + if (atomic_read(&itv->decoding) == 0) { + memset(&itv->dec_timestamp, 0, + sizeof(struct ivtv_ioctl_framesync)); + writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]); + writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]); + writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]); + writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]); + writel(0, &itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data[4]); + + /*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); + } + + /* 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, &itv->dec_msem, + IVTV_API_DEC_START_PLAYBACK, &result, 2, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "COULDN'T START PLAYBACK %d\n", x); + + /*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_bit(IVTV_F_S_CAP, &stream->s_flags) && stream->id == -1) { + set_bit(IVTV_F_S_CAP, &stream->s_flags); + stream->id = id->open_id; + + ret = ivtv_start_v4l2_stream(id); + if (ret) { + stream->id = -1; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Error in v4l2 stream init\n"); + } + 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; + + if (ret == 0) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 read returning 0\n"); + } + + 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; + + int ret = 0; + + if (down_interruptible(&itv->sem_lock)) + return -ERESTARTSYS; + + ret = ivtv_start_v4l2_decode(id); + + up(&itv->sem_lock); + + if (ret) + return ret; + + /* do all the work */ + return ivtv_write(id, buf, count, !(filp->f_flags & O_NONBLOCK)); +} + +int ivtv_v4l2_streamoff(struct ivtv_open_id *id) +{ + + if (down_interruptible(&id->itv->sem_lock)) + return -ERESTARTSYS; + + if (id->open_id != id->itv->v4l2.streams[id->type].id) { + return -EINVAL; + } else { + ivtv_stop_capture(id); + } + + up(&id->itv->sem_lock); + + return 0; +} + +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->itv->v4l2.streams[id->type].id == -1) { + clear_bit(IVTV_F_S_CAP, + &id->itv->v4l2.streams[id->type].s_flags); + set_bit(IVTV_F_S_UNINIT, + &id->itv->v4l2.streams[id->type].s_flags); + ivtv_flush_queues(id); + } else 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); + set_bit(IVTV_F_S_UNINIT, + &id->itv->v4l2.streams[id->type].s_flags); + id->itv->v4l2.streams[id->type].id = -1; + ivtv_flush_queues(id); + } + + up(&id->itv->sem_lock); + + list_del(&id->list); + + 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_change_speed(struct ivtv *itv, struct ivtv_speed speed) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + + if ((speed.scale < 0) || (speed.scale > 50)) + return -EINVAL; + + if ((speed.speed < 0) || (speed.speed > 1)) + return -EINVAL; + + data[0] = speed.scale; + + if (speed.smooth) /* smooth ff */ + data[0] |= 0x40000000; + + if (speed.speed) /* fast forward */ + data[0] |= 0x80000000; + + if (speed.direction) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "DEC: Reverse not supported\n"); + return -EINVAL; + } + + data[1] = speed.direction; /* Forward. Reverse not supported */ + + switch (speed.fr_mask) { + case 2: + default: + data[2] |= 4; /* B */ + case 1: + data[2] |= 2; /* P */ + case 0: + data[2] |= 1; /* I */ + break; + } + + data[3] = itv->v4l2.codec.framespergop; + data[4] = speed.aud_mute; /* mute while fast/slow */ + data[5] = speed.fr_field; /* frame or field at a time */ + data[6] = speed.mute; /* # of frames to mute on normal speed resume */ + + if (ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_DEC_PLAYBACK_SPEED, &result, 7, &data[0])) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "DEC: error changing speed\n"); + return (int)result; + } + + /* Save speed options if call succeeded */ + memcpy(&itv->dec_options.speed, &speed, sizeof(speed)); + + return 0; +} + +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"); + + 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->enc_msem, fwapi.cmd, + &fwapi.result, fwapi.args, &fwapi.data[0]); + */ + + /* Decoder */ + x = ivtv_api(itv->dec_mbox, &itv->dec_msem, 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->dec_msem, + IVTV_API_DEC_START_PLAYBACK, &result, 2, + &data[0])) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DEC: error starting playback\n"); + + break; + + } + case IVTV_IOC_START_DECODE: + case IVTV_IOC_S_START_DECODE:{ + struct ivtv_cfg_start_decode sd; + int ret = 0; + + 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; + + if (cmd == IVTV_IOC_S_START_DECODE) + break; + + if (down_interruptible(&itv->sem_lock)) + return -ERESTARTSYS; + + ret = ivtv_start_v4l2_decode(id); + + up(&itv->sem_lock); + + return ret; + break; + } + case IVTV_IOC_STOP_DECODE: + 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); + + if (cmd == IVTV_IOC_S_STOP_DECODE) + break; + + if (down_interruptible(&id->itv->sem_lock)) + return -ERESTARTSYS; + + if (id->open_id == id->itv->v4l2.streams[id->type].id) { + ivtv_stop_decode(id); + clear_bit(IVTV_F_S_CAP, + &id->itv->v4l2.streams[id->type]. + s_flags); + id->itv->v4l2.streams[id->type].id = -1; + } + + up(&id->itv->sem_lock); + + break; + } + case IVTV_IOC_DEC_FLUSH:{ + if ((id->open_id == id->itv->v4l2.streams[id->type].id) + || (id->itv->v4l2.streams[id->type].id == -1)) { + if (down_interruptible(&id->itv->sem_lock)) + return -ERESTARTSYS; + + ivtv_flush_queues(id); + + up(&id->itv->sem_lock); + } else { + return -EBUSY; + } + } + case IVTV_IOC_DEC_STEP:{ + int howfar, *fieldsel = (int *)arg; + u32 data[IVTV_MBOX_MAX_DATA], result; + + get_user(howfar, fieldsel); + + if (howfar < 0 || howfar > 2) + return -EINVAL; + + data[0] = howfar; /* 0 = 1 frame, 1 = top field, 2 = bottom field */ + if (ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_DEC_STEP_VIDEO, &result, 1, + &data[0])) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DEC: error stepping\n"); + } + + break; + } + case IVTV_IOC_G_SPEED:{ + if (copy_to_user((void *)arg, + &itv->dec_options.speed, + sizeof(itv->dec_options.speed))) { + return -EFAULT; + } + + break; + } + case IVTV_IOC_S_SPEED:{ + struct ivtv_speed speed; + int ret = 0; + + if (copy_from_user(&speed, (struct ivtv_speed *)arg, + sizeof(speed))) + return -EFAULT; + + ret = ivtv_change_speed(itv, speed); + if (ret) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DEC: error in slow/fast mode\n"); + return ret; + } + + break; + } + case IVTV_IOC_S_SLOW_FAST:{ + struct ivtv_slow_fast sf; + struct ivtv_speed speed; + int ret; + + 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; + + memcpy(&speed, &itv->dec_options.speed, sizeof(speed)); + speed.scale = sf.scale; + speed.speed = sf.speed; + + ret = ivtv_change_speed(itv, speed); + if (ret) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DEC: error in set slow/fast mode\n"); + return ret; + } + + break; + } + case IVTV_IOC_PAUSE:{ + u32 data[IVTV_MBOX_MAX_DATA], result; + data[0] = 0; + if (ivtv_api(itv->dec_mbox, &itv->dec_msem, + 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; + + if (atomic_read(&itv->decoding) == 0) { + memset(&timing, 0, sizeof(timing)); + } else { + memcpy(&timing, &itv->dec_timestamp, + sizeof(timing)); + } + + if (copy_to_user((void *)arg, &timing, sizeof(timing))) { + return -EFAULT; + } + + break; + } + + case VIDIOC_QUERYMENU:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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.speed.smooth = + vctrl->value; + break; + case V4L2_CID_IVTV_DEC_FR_MASK: + if ((v < 0) || (v > 2)) + return -ERANGE; + itv->dec_options.speed.fr_mask = + vctrl->value; + break; + case V4L2_CID_IVTV_DEC_SP_MUTE: + if ((v < 0) || (v > 1)) + return -ERANGE; + itv->dec_options.speed. + aud_mute = vctrl->value; + break; + case V4L2_CID_IVTV_DEC_FR_FIELD: + if ((v < 0) || (v > 1)) + return -ERANGE; + itv->dec_options.speed. + 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:{ + //FIXME copy_from_user needed + 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.speed. + smooth; + break; + case V4L2_CID_IVTV_DEC_FR_MASK: + vctrl->value = + itv->dec_options.speed. + fr_mask; + break; + case V4L2_CID_IVTV_DEC_SP_MUTE: + vctrl->value = + itv->dec_options.speed. + aud_mute; + break; + case V4L2_CID_IVTV_DEC_FR_FIELD: + vctrl->value = + itv->dec_options.speed. + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + int *inp = (int *)arg; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "v4l2 ioctl: get input\n"); + + *inp = itv->v4l2.input.active; + break; + } + case VIDIOC_S_INPUT:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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 */ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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:{ + //FIXME copy_from_user needed + 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; + } + case IVTV_IOCTL_GET_DEBUG_LEVEL:{ + //FIXME copy_from_user needed + int *dbg_level = (int *)arg; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "IVTV_IOCTL_GET_DEBUG_LEVEL ivtv_debug = " + "0x%08x\n", ivtv_debug); + if (dbg_level) { + put_user(ivtv_debug, dbg_level); + } else { + printk + ("ivtv: Error: IVTV_IOCTL_GET_DEBUG_LEVEL called with " + "NULL\n"); + } + break; + } + case IVTV_IOCTL_SET_DEBUG_LEVEL:{ + //FIXME copy_from_user needed + int *dbg_level = (int *)arg; + int old_debug_level = ivtv_debug; + get_user(ivtv_debug, dbg_level); + if (!(ivtv_debug & IVTV_DEBUG_ERR)) + ivtv_debug |= IVTV_DEBUG_ERR; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "IVTV_IOCTL_SET_DEBUG_LEVEL ivtv_debug = " + "0x%08x (new) 0x%08x (old)\n", + ivtv_debug, old_debug_level); + put_user(ivtv_debug, dbg_level); + break; + } + case VIDIOC_STREAMOFF:{ + ivtv_v4l2_streamoff(id); + break; + } + + 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; +} diff -upN /dev/null current/drivers/media/video/ivtv-driver.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/ivtv-driver.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,3073 @@ +/* Main Driver file for the ivtv project: + * Driver for the iTVC15 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.sourceforge.net/projects/ivtv/ + */ +/* Hack - this file needs to be converted to use the firmware api */ +#define __KERNEL_SYSCALLS__ + +#include "ivtv.h" + +// Version info +#define IVTV_VERSION_NUMBER(name) name##_version_int +#define IVTV_VERSION_STRING(name) name##_version_string +#define IVTV_VERSION_COMMENT(name) name##_comment_string + +#define IVTV_DEFINE_VERSION_INTERNAL(name, major, minor, patchlevel, comment) \ +unsigned int IVTV_VERSION_NUMBER(name) = ((major << 16) | (minor << 8) | (patchlevel)); \ +const char * const IVTV_VERSION_STRING(name) = #major"."#minor"."#patchlevel;\ +const char * const IVTV_VERSION_COMMENT(name) = comment; + +#define IVTV_VERSION_MAJOR(name) (0xFF & (IVTV_VERSION_NUMBER(name) >> 16)) +#define IVTV_VERSION_MINOR(name) (0xFF & (IVTV_VERSION_NUMBER(name) >> 8)) +#define IVTV_VERSION_PATCHLEVEL(name) (0xFF & (IVTV_VERSION_NUMBER(name))) + +#define IVTV_DEFINE_VERSION(name, major, minor, patchlevel, comment) IVTV_DEFINE_VERSION_INTERNAL(name, major, minor, patchlevel, comment) + +IVTV_DEFINE_VERSION(ivtv_rev, + IVTV_DRIVER_VERSION_MAJOR, + IVTV_DRIVER_VERSION_MINOR, + IVTV_DRIVER_VERSION_PATCHLEVEL, "release"); + +/* mini header */ + +/* var to keep track of the number of array elements in use */ +int ivtv_cards_active = 0; + +/* Master variable for all ivtv info */ +struct ivtv ivtv_cards[IVTV_MAX_CARDS]; + +/* for the global data */ +spinlock_t ivtv_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + +/* add your revision and whatnot here */ +static struct pci_device_id ivtv_pci_tbl[] __devinitdata = { + {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV15, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {PCI_VENDOR_ID_ICOMP, PCI_DEVICE_ID_IVTV16, + PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +static void ivtv_irq_dec_vsync(struct ivtv *itv); +static irqreturn_t ivtv_irq_handler(int irq, void *dev_id, + struct pt_regs *regs); +static void ivtv_DMA_done(struct ivtv *itv); +static void ivtv_sched_DMA(struct ivtv *itv); +static void ivtv_dec_DMA_done(struct ivtv *itv); +static void ivtv_dec_sched_DMA(struct ivtv *itv); + +static u32 ivtv_firm_search_id[] = + { 0x12345678, 0x34567812, 0x56781234, 0x78123456 }; + +/* Parameter declarations */ +static int num_devices = IVTV_DEFAULT_NUM_CARDS; +static int yuv_buffers = IVTV_DEFAULT_YUV_BUFFERS; +static int mpg_buffers = IVTV_DEFAULT_MPG_BUFFERS; +static int vbi_buffers = IVTV_DEFAULT_VBI_BUFFERS; +static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS; +static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS; +static int dec_mpg_qlen = IVTV_DEFAULT_DEC_MPG_QLEN; +static int dec_yuv_qlen = IVTV_DEFAULT_DEC_YUV_QLEN; +#ifdef YUV_FIXUP +static int yuv_fixup; +#endif + +int ivtv_pal = 0; + +/* low debugging by default */ +#if 0 +int debug = (IVTV_DEBUG_ERR | IVTV_DEBUG_INFO | IVTV_DEBUG_API + | IVTV_DEBUG_DMA | IVTV_DEBUG_IOCTL | IVTV_DEBUG_I2C + | IVTV_DEBUG_IRQ); +#endif +int ivtv_debug = IVTV_DEBUG_ERR; + +/* tuner.h tuner type for ivtv card */ +int tuner = -1; + +int errno; + +#define EXPAND_TO_STRING_INTERNAL(arg) #arg +#define EXPAND_TO_STRING(arg) EXPAND_TO_STRING_INTERNAL(arg) + +#ifdef YUV_FIXUP +MODULE_PARM(yuv_fixup, "i"); +MODULE_PARM_DESC(yuv_fixup, + "\nToggles conversion of Hauppauge Macroblock NV12 to NV12\n"); +#endif + +MODULE_PARM(tuner, "i"); +MODULE_PARM_DESC(tuner, "\nTuner type selection, see tuner.h for values"); + +MODULE_PARM(yuv_buffers, "i"); +MODULE_PARM_DESC(yuv_buffers, + "\nNumber of 32K buffers for copying YUV.\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_YUV_BUFFERS) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_YUV_BUFFERS) " " + "Max: " EXPAND_TO_STRING(IVTV_MAX_YUV_BUFFERS)); + +MODULE_PARM(mpg_buffers, "i"); +MODULE_PARM_DESC(mpg_buffers, + "\nNumber of 32K buffers for copying mpg.\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_MPG_BUFFERS) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_MPG_BUFFERS) " " + "Max: " EXPAND_TO_STRING(IVTV_MAX_MPG_BUFFERS)); + +MODULE_PARM(vbi_buffers, "i"); +MODULE_PARM_DESC(vbi_buffers, + "\nNumber of 32K buffers for copying VBI.\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_VBI_BUFFERS) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_VBI_BUFFERS) " " + "Max: " EXPAND_TO_STRING(IVTV_MAX_VBI_BUFFERS)); + +MODULE_PARM(num_devices, "i"); +MODULE_PARM_DESC(num_devices, "\nNumber of supported devices (1-9).\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_NUM_CARDS)); + +MODULE_PARM(dec_mpg_buffers, "i"); +MODULE_PARM_DESC(dec_mpg_buffers, + "\nNumber of 32K buffers for decoding MPG.\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_MPG_BUFFERS) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_MPG_BUFFERS) " " + "Max: " EXPAND_TO_STRING(IVTV_MAX_DEC_MPG_BUFFERS)); + +MODULE_PARM(dec_yuv_buffers, "i"); +MODULE_PARM_DESC(dec_yuv_buffers, + "\nNumber of 32K buffers for decoding YUV.\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_YUV_BUFFERS) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_YUV_BUFFERS) " " + "Max: " EXPAND_TO_STRING(IVTV_MAX_DEC_YUV_BUFFERS) ", " + "0 to disable"); + +MODULE_PARM(dec_mpg_qlen, "i"); +MODULE_PARM_DESC(dec_mpg_qlen, + "\nNumber of 32K buffers to queue before dispatching to decoder\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_MPG_QLEN) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_MPG_QLEN) " " + "Max: "); + +MODULE_PARM(dec_yuv_qlen, "i"); +MODULE_PARM_DESC(dec_yuv_qlen, + "\nNumber of 32K buffers to queue before dispatching to decoder\n" + "Default: " EXPAND_TO_STRING(IVTV_DEFAULT_DEC_YUV_QLEN) ", " + "Min: " EXPAND_TO_STRING(IVTV_MIN_DEC_YUV_QLEN) " " + "Max: "); + +MODULE_PARM(ivtv_debug, "i"); +MODULE_PARM_DESC(ivtv_debug, "\nDebug level (bitmask), default, errors only\n" + "(debug=127 gives full debuging)"); + +MODULE_PARM(ivtv_pal, "i"); +MODULE_PARM_DESC(ivtv_pal, "\nUse PAL as default video mode instead of NTSC"); + +MODULE_AUTHOR("Kevin Thayer"); +MODULE_DESCRIPTION("Alpha iTVC15 driver"); +MODULE_SUPPORTED_DEVICE("iTVC15/16 mpg2 encoder (aka WinTV PVR 250/350)"); +MODULE_LICENSE("GPL"); + +static int SGarray_size; +static int DSGarray_size; + +void ivtv_sleep_timeout(int timeout) +{ + int sleep = timeout; + + do { + set_current_state(TASK_INTERRUPTIBLE); + sleep = schedule_timeout(sleep); + + } while (sleep && !signal_pending(current)); +} + +/* ceiling function for ints.. */ +int ivtv_ceil(int x, int y) +{ + int floor = (int)(x / y); + + if ((floor * y) < x) + return floor + 1; + return floor; +} + +/* Release ioremapped memory */ +static void ivtv_iounmap(struct ivtv *itv) +{ + if (itv == NULL) + return; + + /* Release io memory */ + if (itv->io_mem != NULL) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing iomem\n"); + iounmap(itv->io_mem); + itv->io_mem = NULL; + } + + /* Release registers memory */ + if (itv->reg_mem != NULL) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing regmem\n"); + iounmap(itv->reg_mem); + itv->reg_mem = NULL; + } + + /* Release encoder mailboxes */ + if (itv->enc_mbox != NULL) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing encmbox\n"); + iounmap(itv->enc_mbox); + itv->enc_mbox = NULL; + } + + /* Release decoder mailboxes */ + if (itv->dec_mbox != NULL) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing decmbox\n"); + iounmap(itv->dec_mbox); + itv->dec_mbox = NULL; + } +} + +/* must only be used as hints, not as a definitive answer. the answer could + * be wrong as soon as we return */ +int ivtv_get_free_elements(struct ivtv *itv, struct ivtv_buffer_list *queue) +{ + unsigned long flags; + int elements; + + spin_lock_irqsave(&itv->lock, flags); + elements = queue->elements; + spin_unlock_irqrestore(&itv->lock, flags); + + return elements; +} + +inline void __ivtv_enq_buf(struct ivtv_buffer_list *queue, + struct ivtv_buffer *buf) +{ + WARN_ON(!list_empty(&buf->list)); + list_add_tail(&buf->list, &queue->list); + queue->elements++; +} + +/* Adds buffers to the tail, effectively making a queue */ +int ivtv_enq_buf(struct ivtv *itv, struct ivtv_buffer_list *queue, + struct ivtv_buffer *buf) +{ + unsigned long flags; + + spin_lock_irqsave(&itv->lock, flags); + __ivtv_enq_buf(queue, buf); + spin_unlock_irqrestore(&itv->lock, flags); + + return 0; +} + +inline void __ivtv_del_buf(struct ivtv_buffer_list *queue, + struct ivtv_buffer *buffer) +{ + WARN_ON(list_empty(&buffer->list)); + list_del_init(&buffer->list); + queue->elements--; +} + +/* called to remove the buffer returned by _peek_ functions */ +void ivtv_del_buf(struct ivtv *itv, struct ivtv_buffer_list *queue, + struct ivtv_buffer *buffer) +{ + unsigned long flags; + + spin_lock_irqsave(&itv->lock, flags); + __ivtv_del_buf(queue, buffer); + spin_unlock_irqrestore(&itv->lock, flags); +} + +void ivtv_move_buf(struct ivtv *itv, struct ivtv_buffer_list *from, + struct ivtv_buffer_list *to, struct ivtv_buffer *buffer) +{ + unsigned long flags; + + WARN_ON(list_empty(&buffer->list)); + + spin_lock_irqsave(&itv->lock, flags); + list_move_tail(&buffer->list, &to->list); + from->elements--; + to->elements++; + spin_unlock_irqrestore(&itv->lock, flags); +} + +/* returns first item in queue, doesn't dequeue */ +struct ivtv_buffer *__ivtv_deq_peek_head(struct ivtv_buffer_list *queue) +{ + + /* make sure list has something to DeQ */ + if (!list_empty(&queue->list)) + return list_entry(queue->list.next, struct ivtv_buffer, list); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DeQ from empty list\n"); + queue->elements = 0; + return NULL; +} + +struct ivtv_buffer *ivtv_deq_peek_head(struct ivtv *itv, + struct ivtv_buffer_list *queue) +{ + unsigned long flags; + struct ivtv_buffer *buffer; + + spin_lock_irqsave(&itv->lock, flags); + buffer = __ivtv_deq_peek_head(queue); + spin_unlock_irqrestore(&itv->lock, flags); + + return buffer; +} + +/* removes buffer from the head */ +struct ivtv_buffer *__ivtv_deq_buf(struct ivtv_buffer_list *queue) +{ + struct ivtv_buffer *buf; + + /* make sure list has something to DeQ */ + if (!list_empty(&queue->list)) { + buf = list_entry(queue->list.next, struct ivtv_buffer, list); + list_del_init(queue->list.next); + queue->elements--; + return buf; + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DeQ from empty list!\n"); + queue->elements = 0; + return NULL; +} + +struct ivtv_buffer *ivtv_deq_buf(struct ivtv *itv, + struct ivtv_buffer_list *queue) +{ + struct ivtv_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&itv->lock, flags); + buf = __ivtv_deq_buf(queue); + spin_unlock_irqrestore(&itv->lock, flags); + + return buf; +} + +struct ivtv_buffer *ivtv_init_buffer(int gfp_mask) +{ + struct ivtv_buffer *ibuf; + + ibuf = kmalloc(sizeof(struct ivtv_buffer), gfp_mask); + if (ibuf == NULL) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "No mem on ibuf alloc!\n"); + return NULL; + } + + (void *)ibuf->buffer.m.userptr = kmalloc(IVTV_DMA_BUF_SIZE, gfp_mask); + if ((void *)ibuf->buffer.m.userptr == NULL) { + kfree(ibuf); + IVTV_DEBUG(IVTV_DEBUG_ERR, "No mem on buf alloc!\n"); + return NULL; + } + + INIT_LIST_HEAD(&ibuf->list); + ibuf->buffer.length = IVTV_DMA_BUF_SIZE; + ibuf->buffer.bytesused = 0; + ibuf->readpos = 0; + + return ibuf; +} + +#define IVTV_DMA_UNMAPPED ((u32) -1) + +void ivtv_free_buffer(struct ivtv *itv, struct ivtv_buffer *item) +{ + if (item->dma_handle != IVTV_DMA_UNMAPPED) + pci_unmap_single(itv->dev, item->dma_handle, IVTV_DMA_BUF_SIZE, + PCI_DMA_TODEVICE); + if (item->buffer.m.userptr) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing buf %d!\n", + item->buffer.index); + kfree((void *)item->buffer.m.userptr); + } + kfree(item); +} + +int ivtv_free_queue(struct ivtv_buffer_list *queue) +{ + struct ivtv_buffer *item; + unsigned long flags; + struct ivtv *itv; + int x; + + if (queue == NULL) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Free on NULL list!\n"); + return -EINVAL; + } + + spin_lock_irqsave(&ivtv_lock, flags); + + /* FIXME ugly */ + /* verify ivtv before continuing */ + itv = NULL; + for (x = 0; x < ivtv_cards_active; x++) { + if (queue->vdev->priv == &ivtv_cards[x]) { + itv = queue->vdev->priv; + break; + } + } + + spin_unlock_irqrestore(&ivtv_lock, flags); + + if (itv == NULL) + return -ENODEV; + + while ((item = ivtv_deq_buf(itv, queue))) + ivtv_free_buffer(itv, item); + + return 0; +} + +/* NOTE: This returns the # of buffers allocated */ +int ivtv_init_queue(struct ivtv *itv, struct ivtv_buffer_list *queue, + int length, enum v4l2_buf_type type) +{ + int x; + struct ivtv_buffer *item; + + /* Just in case */ + INIT_LIST_HEAD(&queue->list); + + for (x = 0; x < length; x++) { + /* allocate buffer */ + item = ivtv_init_buffer(GFP_KERNEL); + if (item == NULL) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Buffer alloc failed!\n"); + return x; + } + + /* setup buffer */ + item->buffer.index = x; + item->buffer.type = type; + item->buffer.field = V4L2_FIELD_INTERLACED; + item->buffer.memory = V4L2_MEMORY_MMAP; + + /* enqueue buffer */ + ivtv_enq_buf(itv, queue, item); + } + + return x; +} + +int ivtv_move_queue(struct ivtv *itv, struct ivtv_buffer_list *src, + struct ivtv_buffer_list *dst) +{ + struct ivtv_buffer *buf; + unsigned long flags; + + spin_lock_irqsave(&itv->lock, flags); + + while ((buf = __ivtv_deq_buf(src))) + __ivtv_enq_buf(dst, buf); + + spin_unlock_irqrestore(&itv->lock, flags); + return 0; +} + +static int load_fw_direct(const char *fn, char *mem) +{ + int fd; + long l; + mm_segment_t fs = get_fs(); + + set_fs(get_ds()); + + if ((fd = open(fn, 0, 0)) == -1) { + printk(KERN_INFO "Unable to open '%s'.\n", fn); + l = -EINVAL; + goto out; + } + /* the 2 means SEEK_END */ + l = lseek(fd, 0L, 2); + + if (l <= 0 || l > IVTV_FIRM_IMAGE_SIZE) { + printk(KERN_INFO "Firmware image too large '%s'\n", fn); + l = -ENOMEM; + goto out; + } + + /* the 2 means SEEK_SET */ + lseek(fd, 0L, 0); + + if (read(fd, mem, l) != l) { + printk(KERN_INFO "Failed to read '%s'.\n", fn); + l = -ENOMEM; + } + + out: + close(fd); + set_fs(fs); + + return (int)l; +} + +int ivtv_firmware_copy(struct ivtv *itv) +{ + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Loading encoder image\n"); + + if (load_fw_direct(IVTV_FIRM_ENC_FILENAME, + (char *)(itv->io_mem + IVTV_ENC_MEM_START)) != + IVTV_FIRM_IMAGE_SIZE) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "failed loading encoder firmware\n"); + return -3; + } + + if (itv->card_type != IVTV_250_V2) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Loading decoder firmware\n"); + if (load_fw_direct(IVTV_FIRM_DEC_FILENAME, + (char *)(itv->io_mem + + IVTV_DEC_MEM_START)) != + IVTV_FIRM_IMAGE_SIZE) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "failed loading decoder firmware\n"); + return -1; + } + } + + return 0; +} + +int ivtv_stop_firmware(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + int x = 0; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping firmware\n"); + + if (atomic_read(&itv->capturing)) { + x = ivtv_stop_all_captures(itv); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 1. Code %d\n", + x); + } + + /*Stop decoder_playback */ + data[0] = 1; /* 0: render last frame, 1: stop NOW! :) */ + data[1] = 0; /* "low 4 bytes of stop index" */ + data[2] = 0; /* 0: stop immedeately */ + x = ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_STOP_PLAYBACK, + &result, 3, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 2. Code %d\n", x); + + /*halt enc firmware */ + x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ENC_HALT_FW, + &result, 0, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 3. Code %d\n", x); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n"); + ivtv_sleep_timeout(HZ / 100); + + /*halt dec firmware */ + if (IVTV_250_V2 != itv->card_type) { + x = ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_DEC_HALT_FW, &result, 0, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stop_fw error 4. Code %d\n", + x); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n"); + ivtv_sleep_timeout(HZ / 100); + } + + return 0; +} + +int ivtv_firmware_init(struct ivtv *itv) +{ + int x; + + /* check that we're not RE-loading firmware */ + /* a sucessful load will have detected HW */ + /* mailboxes. */ + + /* FIXME i dont think this will ever get called */ + if (NULL != itv->enc_mbox) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "readying card for firmware upload\n"); + x = ivtv_stop_firmware(itv); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error %d, stopping firmware\n", x); + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping VDM\n"); + writel(IVTV_CMD_VDM_STOP, (IVTV_REG_VDM + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping AO\n"); + writel(IVTV_CMD_AO_STOP, (IVTV_REG_AO + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "pinging (?) APU\n"); + writel(IVTV_CMD_APU_PING, (IVTV_REG_APU + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping VPU\n"); + if (IVTV_250_V2 == itv->card_type) { + writel(IVTV_CMD_VPU_STOP16, (IVTV_REG_VPU + itv->reg_mem)); + } else { + writel(IVTV_CMD_VPU_STOP15, (IVTV_REG_VPU + itv->reg_mem)); + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Resetting Hw Blocks\n"); + writel(IVTV_CMD_HW_BLOCKS_RST, (IVTV_REG_HW_BLOCKS + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping SPU\n"); + writel(IVTV_CMD_SPU_STOP, (IVTV_REG_SPU + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n"); + ivtv_sleep_timeout(HZ / 100); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "init Encoder SDRAM pre-charge\n"); + writel(IVTV_CMD_SDRAM_PRECHARGE_INIT, + (IVTV_REG_ENC_SDRAM_PRECHARGE + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "init Encoder SDRAM refresh to 1us\n"); + writel(IVTV_CMD_SDRAM_REFRESH_INIT, + (IVTV_REG_ENC_SDRAM_REFRESH + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "init Decoder SDRAM pre-charge\n"); + writel(IVTV_CMD_SDRAM_PRECHARGE_INIT, + (IVTV_REG_DEC_SDRAM_PRECHARGE + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "init Decoder SDRAM refresh to 1us\n"); + writel(IVTV_CMD_SDRAM_REFRESH_INIT, + (IVTV_REG_DEC_SDRAM_REFRESH + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for %dms (600 recommended)\n", + (int)IVTV_SDRAM_SLEEPTIME); + ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Card ready for firmware!\n"); + x = ivtv_firmware_copy(itv); + if (x) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error loading firmware!\n"); + return x; + } + + /*I guess this is read-modify-write :) */ + writel((readl(itv->reg_mem + IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE), + (IVTV_REG_SPU + itv->reg_mem)); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 1 sec\n"); + ivtv_sleep_timeout(HZ); + + /*I guess this is read-modify-write :) */ + if (IVTV_250_V2 == itv->card_type) { + writel((readl(itv->reg_mem + IVTV_REG_VPU) & + IVTV_MASK_VPU_ENABLE16), (IVTV_REG_VPU + itv->reg_mem)); + } else { + writel((readl(itv->reg_mem + IVTV_REG_VPU) & + IVTV_MASK_VPU_ENABLE15), (IVTV_REG_VPU + itv->reg_mem)); + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 1 sec\n"); + ivtv_sleep_timeout(HZ); + + /* FIXME Send Status API commands to encoder and decoder to verify! */ + + return 0; +} + +int ivtv_find_firmware_mailbox(struct ivtv *itv) +{ + u32 *searchptr, *result; + int match = 0; + + searchptr = NULL; + result = NULL; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Searching for encoder mailbox\n"); + searchptr = (u32 *) (IVTV_FIRM_SEARCH_ENCODER_START + itv->io_mem); + + while (searchptr < (u32 *) (IVTV_FIRM_SEARCH_ENCODER_END + itv->io_mem)) { + if (ivtv_firm_search_id[match] == readl(searchptr)) { + (u32) result = (u32) searchptr + 4; /* avoid pointer aritmetic */ + match++; + while ((match > 0) && (match < 4)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "match: 0x%08x at " + "0x%08x. match: %d\n", *result, + (u32) result, match); + if (ivtv_firm_search_id[match] == readl(result)) { + match++; + /* FIXME change to just "result++;" ? */ + (u32) result = (u32) result + 4; + } else + match = 0; + } + } else { + IVTV_DEBUG(IVTV_DEBUG_INFO, "."); + } + if (4 == match) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "found encoder mailbox!\n"); + itv->enc_mbox = (struct ivtv_mailbox *)result; + break; + } + (u32) searchptr += IVTV_FIRM_SEARCH_STEP; + } + if (itv->enc_mbox == NULL) + IVTV_DEBUG(IVTV_DEBUG_ERR, "Encoder mailbox not found\n"); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Searching for decoder mailbox\n"); + match = 0; + searchptr = (u32 *) (IVTV_FIRM_SEARCH_DECODER_START + itv->io_mem); + + while (searchptr < (u32 *) (IVTV_FIRM_SEARCH_DECODER_END + itv->io_mem)) { + if (ivtv_firm_search_id[match] == readl(searchptr)) { + (u32) result = (u32) searchptr + 4; /* avoid pointer aritmetic */ + match++; + while ((match > 0) && (match < 4)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "match: 0x%08x at 0x%08x. match: %d\n", + *result, (u32) result, match); + if (ivtv_firm_search_id[match] == readl(result)) { + match++; + /* FIXME change to just "result++;" ? */ + (u32) result = (u32) result + 4; + } else + match = 0; + } + } else { + IVTV_DEBUG(IVTV_DEBUG_INFO, "."); + } + if (4 == match) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "found decoder mailbox!\n"); + itv->dec_mbox = (struct ivtv_mailbox *)result; + break; + } + (u32) searchptr += IVTV_FIRM_SEARCH_STEP; + } + if (itv->dec_mbox == 0) + IVTV_DEBUG(IVTV_DEBUG_ERR, "Decoder mailbox not found\n"); + + return 0; +} + +int ivtv_get_free_mailbox(struct ivtv_mailbox *mbox) +{ + int i = 0; + if (NULL == mbox) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Can't get mailbox from NULL\n"); + return -ENODEV; + } + + /* FIXME hardcoded cause i'm not sure what changing API_BOXES will do */ + //for (i = 0; i < IVTV_MBOX_API_BOXES; i++) { + for (i = 0; i < 2; i++) { + if (mbox[i].flags & IVTV_MBOX_FIRMWARE_DONE) { + switch (mbox[i].cmd) { + case IVTV_API_SCHED_DMA_TO_HOST: + case IVTV_API_DEC_DMA_FROM_HOST: + IVTV_DEBUG(IVTV_DEBUG_API, + "recycled mailbox: %d\n", i); + writel(IVTV_MBOX_IN_USE, &mbox[i].flags); + return i; + break; + default: + IVTV_DEBUG(IVTV_DEBUG_API, + "Mailbox %d in use, skipping\n", i); + break; + } + /* FIXME using 'else' may leak mailboxes in some situations */ + } else if (!test_and_set_bit(0, &mbox[i].flags)) { + IVTV_DEBUG(IVTV_DEBUG_API, "got free mailbox: %d\n", i); + return i; + } + } + + IVTV_DEBUG(IVTV_DEBUG_ERR, "no free mailboxes!\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "mbox 0: 0x%08x, mbox 1 0x%08x!\n", + mbox[0].cmd, mbox[1].cmd); + return -ENODEV; +} + +void ivtv_clear_irq_mask(struct ivtv *itv, unsigned long mask) +{ + itv->irqmask &= ~mask; + writel(itv->irqmask, (itv->reg_mem + IVTV_REG_IRQMASK)); + /* pci posting */ + readl(itv->reg_mem + IVTV_REG_IRQMASK); +} + +void ivtv_set_irq_mask(struct ivtv *itv, unsigned long mask) +{ + itv->irqmask |= mask; + writel(itv->irqmask, (itv->reg_mem + IVTV_REG_IRQMASK)); + /* pci posting */ + readl(itv->reg_mem + IVTV_REG_IRQMASK); +} + +/** + * Call ivtv api function using given mailbox, without locking sem. + */ +int __ivtv_api_call(struct ivtv_mailbox *mbox, u32 cmd, int elements, + const u32 * data) +{ + int x; + if (NULL == mbox) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "invalid api mailbox\n"); + return -ENODEV; + } + + /* "if mailbox is available" */ + if ((mbox->flags & IVTV_MBOX_FIRMWARE_DONE) || + (!test_and_set_bit(0, &mbox->flags))) { + /* I'm too lazy to invert the condition ;) */ + } else { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Mailbox busy (unexpected)\n"); + IVTV_DEBUG(IVTV_DEBUG_INFO, + "cmd 0x%08x, m.cmd 0x%08x\n", cmd, mbox->cmd); + IVTV_DEBUG(IVTV_DEBUG_INFO, "d0 0x%08x, d1 0x%08x, d2 0x%08x\n", + mbox->data[0], mbox->data[1], mbox->data[2]); + return -EBUSY; + } + + readl(&mbox->flags); + writel(cmd, &mbox->cmd); + writel(IVTV_API_STD_TIMEOUT, &mbox->timeout); + + for (x = 0; x < IVTV_MBOX_MAX_DATA; x++) { + if (x < elements) { + writel(data[x], &mbox->data[x]); + } else { + writel(0, &mbox->data[x]); + } + } + + writel((IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_IN_USE), &mbox->flags); + readl(&mbox->flags); + + return 0; +} + +/* This one is for stuff that can't sleep.. irq handlers, etc.. */ +int ivtv_api_getresult_nosleep(struct ivtv_mailbox *mbox, u32 * result, + u32 data[]) +{ + u32 readdata; + int count = 0; + + if (NULL == mbox) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "invalid api mailbox\n"); + return -ENODEV; + } + + readdata = readl(&mbox->flags); + + *result = readl(&mbox->retval); + for (count = 0; count < IVTV_MBOX_MAX_DATA; count++) + data[count] = readl(&mbox->data[count]); + + return 0; +} + +int __ivtv_api_getresult(struct ivtv_mailbox *mbox, u32 * result, u32 data[], + int api_timeout) +{ + u32 readdata; + int count = 0; + + readdata = readl(&mbox->flags); + + while (!(readdata & IVTV_MBOX_FIRMWARE_DONE)) { + IVTV_DEBUG(IVTV_DEBUG_API, + "[%d]result not ready, waiting 10 ms\n", count); + ivtv_sleep_timeout(HZ / 100); + readdata = readl(&mbox->flags); + + if (count++ > api_timeout) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "%d ms time out waiting for firmware\n", + api_timeout); + return -EBUSY; + } + } + + *result = readl(&mbox->retval); + for (count = 0; count < IVTV_MBOX_MAX_DATA; count++) + data[count] = readl(&mbox->data[count]); + + return 0; +} + +int ivtv_api(struct ivtv_mailbox *mbox, struct semaphore *sem, int cmd, + u32 * result, int args, u32 data[]) +{ + int x = 0, gotsem = 0, needsresult = 1; + int die = 0, api_timeout = 100; + struct ivtv_mailbox *local_box; + + IVTV_DEBUG(IVTV_DEBUG_API, "API Call: 0x%08x\n", cmd); + + local_box = mbox; + + /* check args */ + if (args > IVTV_MBOX_MAX_DATA) + return -EINVAL; + + switch (cmd) { + case IVTV_API_SCHED_DMA_TO_HOST: + case IVTV_API_DEC_DMA_FROM_HOST: + needsresult = 0; + if (down_trylock(sem)) { /* box 0 was busy */ + gotsem = 0; + local_box = &mbox[1]; + } else { + gotsem = 1; + } + if ((x = __ivtv_api_call(local_box, cmd, args, data))) { + if (local_box == mbox) { + IVTV_DEBUG(IVTV_DEBUG_API, + "Trying alternate mailbox\n"); + x = __ivtv_api_call(&mbox[1], cmd, args, data); + } + } + goto ivtv_api_done; + break; + /* adjust api timeout for these 2 calls */ + case IVTV_API_END_CAPTURE: + case IVTV_API_EVENT_NOTIFICATION: + api_timeout = 1000; + default: + if (down_interruptible(sem)) + return -ERESTARTSYS; + gotsem = 1; + break; + } + + /* wait 200ms for mailbox to become free */ + x = __ivtv_api_call(local_box, cmd, args, data); + while ((x == -EBUSY) && (die < 20)) { + die++; + ivtv_sleep_timeout(HZ / 100); + x = __ivtv_api_call(local_box, cmd, args, data); + IVTV_DEBUG(IVTV_DEBUG_API, "die: %d\n", die); + } + + if (x == -EBUSY) { + /* dilemma here: + if the command that currently has the mailbox + is lost, then it'll never free up the box, and + we'll lose our only 'general purpose' box forever. + But if it comes back, we run the risk of squishing + something important!. */ + switch (local_box->cmd) { + case IVTV_API_DEC_DMA_FROM_HOST: + /* if we got here, it's because the call to xfer the dma + was finished, and the command that wants to run has + already slept for 20ms. It's probably safe to take over + */ + IVTV_DEBUG(IVTV_DEBUG_API, + "Forcibly freeing mailbox\n"); + writel(0x00000000, &mbox->flags); + x = __ivtv_api_call(local_box, cmd, args, data); + break; + default: + /* do nothing */ + break; + } + } + + if (x) { + IVTV_DEBUG(IVTV_DEBUG_API, "Error running command 0x%08x\n", + cmd); + goto ivtv_api_done; + } + + if (needsresult) { + x = __ivtv_api_getresult(local_box, result, &data[0], + api_timeout); + IVTV_DEBUG(IVTV_DEBUG_API, "retval: 0x%08x\n", *result); + if (x == -EBUSY) + IVTV_DEBUG(IVTV_DEBUG_ERR, "api call 0x%08x\n", cmd); + } + + IVTV_DEBUG(IVTV_DEBUG_API, "Releasing mailbox (before 0x%08x, ", + readl(&mbox->flags)); + writel(0x00000000, &mbox->flags); + IVTV_DEBUG(IVTV_DEBUG_API, "after 0x%08x )\n", readl(&mbox->flags)); + + ivtv_api_done: + if (gotsem) { + up(sem); + } + + return x; +} + +int ivtv_firmware_versions(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + int x; + + /* Encoder */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "Getting encoder firmware rev.\n"); + x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_ENC_GETVER, + &result, 0, &data[0]); + if (x) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "error getting Encoder firmware version\n"); + } else { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Encoder revision: 0x%08x\n", data[0]); + } + + if (itv->card_type != IVTV_250_V2) { + /* Decoder */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "Getting decoder firmware rev.\n"); + x = ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_GETVER, + &result, 0, &data[0]); + if (x) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "error getting Decoder firmware version\n"); + } else { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Decoder revision: 0x%08x\n", data[0]); + } + } + + return 0; +} + +void ivtv_flush_queues(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + struct ivtv_v4l2_stream *st = &itv->v4l2.streams[id->type]; + struct ivtv_buffer *buf; + + /* move free_q to full_q to clean up partially-filled buffers */ + while ((buf = ivtv_deq_buf(itv, &st->free_q))) + ivtv_enq_buf(itv, &st->full_q, buf); + + while ((buf = ivtv_deq_buf(itv, &itv->v4l2.streams[id->type].full_q))) { + buf->buffer.bytesused = 0; + buf->readpos = 0; + ivtv_enq_buf(itv, &st->free_q, buf); + } + + while ((buf = ivtv_deq_buf(itv, &itv->v4l2.streams[id->type].dma_q))) { + buf->buffer.bytesused = 0; + buf->readpos = 0; + ivtv_enq_buf(itv, &st->free_q, buf); + } + + return; +} + +int ivtv_stop_all_captures(struct ivtv *itv) +{ + struct ivtv_open_id id; + int x; + id.itv = itv; + + down(&itv->sem_lock); + + for (x = 0; x < itv->v4l2.streamcount; x++) { + if (test_bit(IVTV_F_S_CAP, &itv->v4l2.streams[x].s_flags)) { + id.type = x; + ivtv_stop_capture(&id); + } + } + + up(&itv->sem_lock); + return 0; +} + +int ivtv_stop_capture(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + u32 data[IVTV_MBOX_MAX_DATA], result; + DECLARE_WAITQUEUE(wait, current); + int type, subtype, then; + int x; + + /* This function assumes that you are allowed to stop the capture + and that we are actually capturing */ + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stop Capture\n"); + + /* sem_lock must be held */ + IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0); + + type = id->type; + if (type == 1) { + subtype = 3; //FIXME temp + } else { + subtype = 3; + } + +#if 0 + /* only run these if we're shutting down the last cap */ + if (atomic_read(&itv->capturing) - 1 == 0) { + /* event notification (off) */ + data[0] = 0; /*type: 0 = refresh */ + data[1] = 0; /*on/off: 0 = off */ + data[2] = 0x10000000; /*intr_bit: 0x10000000 = digitizer */ + data[3] = -1; /*mbox_id: -1: none */ + x = ivtv_api(itv->enc_mbox, &itv->enc_msem, + IVTV_API_EVENT_NOTIFICATION, &result, 4, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stopcap error 1. Code %d\n", + x); + + } +#endif + + /* end_capture */ + data[0] = 1; /*when: 0 = end of GOP 1 = NOW! */ + data[1] = type; /*type: 0 = mpeg */ + data[2] = subtype; /*subtype: 3 = video+audio */ + x = ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_END_CAPTURE, + &result, 3, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stopcap error 2. Code %d\n", x); + + then = jiffies; + + add_wait_queue(&itv->v4l2.streams[type].waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + do { + /* check if DMA is pending */ + if (!test_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags)) { + break; + } + IVTV_DEBUG(IVTV_DEBUG_INFO, "dma still pending!\n"); + schedule_timeout(HZ / 100); + } while (((then + HZ) < jiffies) && !signal_pending(current)); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&itv->v4l2.streams[type].waitq, &wait); + + if (test_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "giving up waiting for DMA pending clear\n"); + } +/* only needed if we're searching for an EOS.. currently disabled */ +#if 0 + /* only run these if we're shutting down the last cap */ + if (atomic_read(&itv->capturing) - 1 == 0) { + add_wait_queue(&itv->cap_w, &wait); + + set_current_state(TASK_INTERRUPTIBLE); + + /* wait 2s for EOS interrupt */ + while ((!test_bit(IVTV_F_I_EOS, &itv->i_flags)) && + (jiffies < then + 2 * HZ)) { + schedule_timeout(HZ); + } + then = jiffies - then; + + if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "EOS interrupt not received! stopping anyway.\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, + "waitied %d ms: %d\n", (1000 / HZ) * then); + } else { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "EOS took %d ms to occur.\n", + (1000 / HZ) * then); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&itv->cap_w, &wait); + } +#endif + clear_bit(IVTV_F_S_OVERFLOW, &itv->v4l2.streams[type].s_flags); + clear_bit(IVTV_F_S_CAP, &itv->v4l2.streams[type].s_flags); + + atomic_dec(&itv->capturing); + if (atomic_read(&itv->capturing)) + return 0; + + /*Set the following Interrupt mask bits: 0xd8000000 */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + IVTV_DEBUG(IVTV_DEBUG_IRQ, "IRQ Mask is now: 0x%08x\n", itv->irqmask); + + return 0; +} + +int ivtv_stop_decode(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + u32 data[IVTV_MBOX_MAX_DATA], result; + int x; + + /* sem_lock must be held */ + IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0); + + /* FIXME set 'die' ?? */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder stop.\n"); + + /* only run these if we're shutting down the last cap */ + if (atomic_read(&itv->decoding) - 1 == 0) { +#if 0 + /* event notification (off) */ + data[0] = 0; /* Event: 0 = audio change between stereo and mono */ + data[1] = 0; /* 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, &itv->dec_msem, + IVTV_API_DEC_EVENT_NOTIFICATION, &result, 4, + &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stopDEC error 1. Code %d\n", + x); +#endif + + /* end_capture */ + data[0] = itv->dec_options.hide_last_frame; /* 0 = last frame, + 1 = black */ + data[1] = itv->dec_options.pts_low; /* when: pts low */ + data[2] = itv->dec_options.pts_hi; /* when: pts hi */ + x = ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_DEC_STOP_PLAYBACK, &result, 3, &data[0]); + if (x) + IVTV_DEBUG(IVTV_DEBUG_ERR, "stopDEC error 2. Code %d\n", + x); + } + + /* FIXME turn off relevant irqmask here */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + + /* stop decoder interrupt timeout */ + del_timer_sync(&itv->dec_timeout); + + if (test_and_clear_bit + (IVTV_F_S_DMAP, &itv->v4l2.streams[id->type].s_flags)) + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: clearing dma_pending\n"); + + /* Clean up some possibly loose-ends */ + clear_bit(IVTV_F_I_BUSY, &itv->i_flags); + atomic_dec(&itv->decoding); + wake_up(&itv->dec_master_w); + + return 0; +} + +void ivtv_dec_timeout(unsigned long arg) +{ + struct ivtv *itv = (struct ivtv *)arg; + + /* FIXME mpg only :/ */ + struct ivtv_v4l2_stream *stream = + &itv->v4l2.streams[IVTV_DEC_STREAM_TYPE_MPG]; + unsigned long flags; + + if (!test_bit(IVTV_F_S_DMAP, &stream->s_flags)) + return; + + IVTV_DEBUG(IVTV_DEBUG_ERR, + "ivtv_dec_timeout: lost IRQ; resetting...\n"); + spin_lock_irqsave(&itv->lock, flags); + ivtv_dec_DMA_done(itv); + /* kick it off again! */ + set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags); + ivtv_dec_sched_DMA(itv); + spin_unlock_irqrestore(&itv->lock, flags); +} + +#if 0 +static void ivtv_show_irq_status(struct ivtv *itv, u32 irqstat, u32 irqmask, + u32 dmastat) +{ + struct ivtv_mailbox *mbox8 = &itv->dec_mbox[8]; + struct ivtv_mailbox *mbox9 = &itv->dec_mbox[9]; + +#if 0 + // Make it less verbose... + if ((irqstat & ~4) == IVTV_IRQ_DEC_VSYNC) + return; +#endif + + printk("ivtv: irqstat [ " + "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s], " + "frame %d, pts %d, scr %d, type %d, offset %08x, max %d, full %d\n", + (irqstat & IVTV_IRQ_ENC_START_CAP) ? "StartCap " : "", + (irqstat & IVTV_IRQ_ENC_EOS) ? "EndOfStream " : "", + (irqstat & IVTV_IRQ_ENC_VBI_CAP) ? "VBICap " : "", + (irqstat & IVTV_IRQ_ENC_VIM_RST) ? "VIMReset " : "", + (irqstat & IVTV_IRQ_ENC_DMA_COMPLETE) ? "EncDMAComplete " : "", + (irqstat & (1 << 26)) ? "26 " : "", + (irqstat & IVTV_IRQ_DEC_COPY_PROTECT) ? "CopyProt " : "", + (irqstat & IVTV_IRQ_DEC_AUD_MODE_CHG) ? "AudioMode " : "", + (irqstat & (1 << 23)) ? "23 " : "", + (irqstat & IVTV_IRQ_DEC_DATA_REQ) ? "DecDataReq " : "", + (irqstat & IVTV_IRQ_DEC_IFRAME_DONE) ? "IFrameDone " : "", + (irqstat & IVTV_IRQ_DEC_DMA_COMPLETE) ? "DecDMAComplete " : "", + (irqstat & IVTV_IRQ_DEC_VBI_RE_INSERT) ? "VBIReInsert " : "", + (irqstat & IVTV_IRQ_DEC_DMA_ERR) ? "DecDMAError " : "", + (irqstat & (1 << 17)) ? "17 " : "", + (irqstat & (1 << 16)) ? "16 " : "", + (irqstat & (1 << 15)) ? "15 " : "", + (irqstat & (1 << 14)) ? "14 " : "", + (irqstat & (1 << 13)) ? "13 " : "", + (irqstat & (1 << 12)) ? "12 " : "", + (irqstat & (1 << 11)) ? "11 " : "", + (irqstat & IVTV_IRQ_DEC_VSYNC) ? "DecVSync " : "", + (irqstat & (1 << 9)) ? "9 " : "", + (irqstat & (1 << 8)) ? "8 " : "", + (irqstat & (1 << 7)) ? "7 " : "", + (irqstat & (1 << 6)) ? "6 " : "", + (irqstat & (1 << 5)) ? "5 " : "", + (irqstat & (1 << 4)) ? "4 " : "", + (irqstat & (1 << 3)) ? "3 " : "", + (irqstat & (1 << 2)) ? "2 " : "", + (irqstat & (1 << 1)) ? "1 " : "", + (irqstat & (1 << 0)) ? "0 " : "", + readl(&mbox8->data[0]), + readl(&mbox8->data[1]), + readl(&mbox8->data[3]), + readl(&mbox9->data[0]), + readl(&mbox9->data[1]), + readl(&mbox9->data[2]), readl(&mbox9->data[3])); +} +#endif + +static irqreturn_t ivtv_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + + u32 stat = 0; + u32 combo = 0; + struct ivtv *itv = (struct ivtv *)dev_id; + + spin_lock(&itv->lock); + + /* get contents of irq status register */ + stat = readl(itv->reg_mem + IVTV_REG_IRQSTATUS); + + combo = ~itv->irqmask & stat; + + if (0 == combo) { + /* wasn't for us */ + spin_unlock(&itv->lock); + return IRQ_NONE; + } + +/* + ivtv_show_irq_status(itv, stat, itv->irqmask, + readl(itv->reg_mem + IVTV_REG_DMASTATUS)); +*/ + + IVTV_DEBUG(IVTV_DEBUG_IRQ, "======= valid IRQ bits: 0x%08x ======\n", + combo); + + writel(combo, (itv->reg_mem + IVTV_REG_IRQSTATUS)); + + if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) { + ivtv_DMA_done(itv); + IVTV_DEBUG(IVTV_DEBUG_IRQ, "Processed DMA-complete\n"); + } + if (combo & IVTV_IRQ_ENC_START_CAP) { + ivtv_sched_DMA(itv); + IVTV_DEBUG(IVTV_DEBUG_IRQ, "Processed enc-startcap\n"); + } + if (combo & IVTV_IRQ_ENC_EOS) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "Encoder End Of Stream\n"); + set_bit(IVTV_F_I_EOS, &itv->i_flags); + wake_up(&itv->cap_w); + } + if (combo & IVTV_IRQ_ENC_VBI_CAP) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb3\n"); + } + if (combo & IVTV_IRQ_ENC_VIM_RST) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "VIM Restart\n"); + } + if (combo & IVTV_IRQ_DEC_COPY_PROTECT) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb6\n"); + } + if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb7\n"); + } + if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "Decoder DMA Done\n"); + ivtv_dec_DMA_done(itv); + } + if (combo & IVTV_IRQ_DEC_DATA_REQ) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "Decoder Data Request\n"); + set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags); + ivtv_dec_sched_DMA(itv); + } + if (combo & IVTV_IRQ_DEC_IFRAME_DONE) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb9\n"); + } + if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb11\n"); + } + if (combo & IVTV_IRQ_DEC_DMA_ERR) { + IVTV_DEBUG(IVTV_DEBUG_IRQ, "deb12\n"); + } + if (combo & IVTV_IRQ_DEC_VSYNC) { + ivtv_irq_dec_vsync(itv); + } + /* + stat = readl(itv->reg_mem + IVTV_REG_IRQSTATUS); + IVTV_DEBUG(IVTV_DEBUG_IRQ, "IVTV IRQ STATUS REG AFTER INTERRUPT 0x%08x", stat); + if (combo & ~IVTV_IRQ_DEBUG_KLUGE) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "unknown irq 0x%08x, mask:0x%08x, combo:0x%08x\n", + stat, itv->irqmask, combo); + } + */ + spin_unlock(&itv->lock); + return IRQ_HANDLED; +} + +static void ivtv_irq_dec_vsync(struct ivtv *itv) +{ + u32 *data = itv->dec_mbox[IVTV_MBOX_FIELD_DISPLAYED].data; + + u32 newframe = readl(&data[0]); + u64 newpts = ((u64) readl(&data[2]) << 32) | (u64) (readl(&data[1])); + u64 newscr = ((u64) readl(&data[4]) << 32) | (u64) (readl(&data[3])); + + itv->dec_timestamp.pts = newpts; + itv->dec_timestamp.scr = newscr; + if (newframe != itv->dec_timestamp.frame) { + itv->dec_timestamp.frame = newframe; + wake_up(&itv->vsync_w); + } + + IVTV_DEBUG(IVTV_DEBUG_IRQ, + "ivtv_irq_dec_vsync: frames %d, pts %ld, scr %ld\n", + itv->dec_timestamp.frame, (long int)itv->dec_timestamp.pts, + (long int)itv->dec_timestamp.scr); +} + +static void ivtv_show_debug_flags(struct ivtv *itv) +{ + int y; + + printk(KERN_DEBUG "ivtv: i_flags=%lx", itv->i_flags); + for (y = IVTV_DEC_STREAM_TYPE_MPG; y < itv->v4l2.streamcount; y++) + printk(", %d s_sflags=%lx", y, itv->v4l2.streams[y].s_flags); + printk("\n"); +} + +static void ivtv_DMA_done(struct ivtv *itv) +{ + u32 result; + int y, stmtype = -1; + struct ivtv_v4l2_stream *stream = NULL; + struct ivtv_buffer *buf; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet\n"); + + for (y = 0; y < itv->v4l2.streamcount; y++) { + if (test_and_clear_bit + (IVTV_F_S_DMAP, &itv->v4l2.streams[y].s_flags)) { + stmtype = y; + break; + } + } + + if (stmtype < 0) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Got DMA-done, but not expecting one\n"); + ivtv_show_debug_flags(itv); + return; + } + + stream = &itv->v4l2.streams[stmtype]; + + /* check DMA status register */ + result = readl(itv->reg_mem + IVTV_REG_DMASTATUS); + + if (!(result & IVTV_DMA_SUCCESS)) { + if (result & IVTV_DMA_WRITE_ERR) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DMA write error. Result=0x%08x\n", result); + if (result & IVTV_DMA_READ_ERR) + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DMA read error. Result=0x%08x\n", result); + return; + } + + /* DMA was fine if we made it this far */ + + /* remove from dma_pending queue */ + while ((buf = __ivtv_deq_buf(&stream->dma_q))) { + IVTV_ASSERT(buf->dma_handle != IVTV_DMA_UNMAPPED); + pci_unmap_single(itv->dev, buf->dma_handle, + IVTV_DMA_BUF_SIZE, PCI_DMA_TODEVICE); + buf->dma_handle = IVTV_DMA_UNMAPPED; + /* byteswap ABCD -> DCBA for MPG data */ + if (stmtype == 0) { + for (y = 0; y < buf->buffer.bytesused; y += 4) { + swab32s((u32 *) ((u32) buf->buffer.m.userptr + + y)); + } + } + /* put in the 'done' queue */ + __ivtv_enq_buf(&stream->full_q, buf); + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet5\n"); + /*wake up client */ + wake_up(&stream->waitq); + IVTV_DEBUG(IVTV_DEBUG_INFO, "DMA Done tasklet6\n"); +} + +/* must hold itv->lock */ +static int ivtv_ignore_DMA_req(struct ivtv *itv, u32 type) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + int ret = 0; + + data[0] = 0; + data[1] = 0; /* ACK the DMA and continue */ + data[2] = type; // AEW - API docs say type goes here + if (ivtv_api(itv->enc_mbox, &itv->enc_msem, + IVTV_API_SCHED_DMA_TO_HOST, &result, 3, &data[0])) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "error sending DMA info\n"); + ret = -EIO; + } + + if (!ret) + set_bit(IVTV_F_S_DMAP, &itv->v4l2.streams[type].s_flags); + + return ret; +} + +/* FIXME this function is getting too long. split it up? */ +static void ivtv_sched_DMA(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + u32 type, size, offset; + u32 UVsize = 0, UVoffset = 0, pts_stamp = 0; + struct ivtv_v4l2_stream *st; + int x, bufs_needed; + int uvflag = 0; + struct ivtv_buffer *buf; + LIST_HEAD(free_list); + long sequence; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sched DMA tasklet\n"); + + /* Get DMA destination and size arguments from card */ + x = ivtv_api_getresult_nosleep(&itv->enc_mbox[9], &result, &data[0]); + if (x) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "error:%d getting DMA info\n", x); + return; + } + + type = data[0]; + + /* FIXME should check for null on the stream element */ + if (itv->v4l2.streamcount <= type) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "No stream handler for type %d\n", + type); + ivtv_ignore_DMA_req(itv, type); + return; + } + + switch (type) { + case 0: /* MPEG */ + offset = data[1]; + size = data[2]; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DMA/MPG type 0x%08x,size 0x%08x,offset 0x%08x\n", + type, size, offset); + bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE); + break; + case 1: /* YUV */ + offset = data[1]; + size = data[2]; + UVoffset = data[3]; + UVsize = data[4]; + pts_stamp = data[6]; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DMA/YUV type 0x%08x,Ysize 0x%08x,Yoffset 0x%08x," + "UVsize 0x%08x,UVoffset 0x%08x,PTS 0x%08x\n", + type, size, offset, UVsize, UVoffset, pts_stamp); + bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE); + bufs_needed += ivtv_ceil(UVsize, IVTV_DMA_BUF_SIZE); + break; + + case 2: /* PCM (audio) */ + offset = data[1]; + size = data[2]; + pts_stamp = data[6]; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DMA/PCM type 0x%08x,size 0x%08x,offset 0x%08x " + "PTS 0x%08x\n", type, size, offset, pts_stamp); + bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE); + ivtv_ignore_DMA_req(itv, type); + return; + case 3: /* VBI */ + offset = data[1]; + size = data[2]; + bufs_needed = ivtv_ceil(size, IVTV_DMA_BUF_SIZE); + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DMA/VBI type 0x%08x, size 0x%08x, offset 0x%08x" + "EXPERIMENTAL\n", type, size, offset); + break; + default: + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DMA/UNKNOWN type 0x%08x, NOT SUPPORTED\n", type); + ivtv_ignore_DMA_req(itv, type); + return; + } + + if (bufs_needed > SGarray_size) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "INTERNAL ERROR: ivtv_sched_DMA_tasklet: " + "bufs_needed = %d but SGarray_size = %d\n", + bufs_needed, SGarray_size); + return; + } + + st = &itv->v4l2.streams[type]; + + /* gather the needed buffers first, so we don't have to bail + * in mid-air. put them on a list on the stack */ + for (x = 0; x < bufs_needed; x++) { + buf = __ivtv_deq_buf(&st->free_q); + if (!buf) + break; + + list_add_tail(&buf->list, &free_list); + } + + /* damn, failed */ + if (x < bufs_needed) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DMA buffer DeQueue failed! got %d, want %d\n", + x + 1, bufs_needed + 1); + IVTV_DEBUG(IVTV_DEBUG_INFO, "SCHED: free_q: %d elements\n", + st->free_q.elements); + IVTV_DEBUG(IVTV_DEBUG_INFO, "SCHED: dma_q: %d elements\n", + st->dma_q.elements); + IVTV_DEBUG(IVTV_DEBUG_INFO, "SCHED: full_q: %d elements\n", + st->full_q.elements); + while (!list_empty(&free_list)) { + buf = + list_entry(free_list.next, struct ivtv_buffer, + list); + list_del_init(&buf->list); + __ivtv_enq_buf(&st->free_q, buf); + } + /* mark overflow condition, next free will restart dma req */ + set_bit(IVTV_F_S_OVERFLOW, &st->s_flags); + return; + } + + /* increment the sequence # */ + sequence = ++st->seq; + + for (x = 0; x < bufs_needed; x++) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "size: %d 0x%08x\n", size, size); + + if ((size == 0) && (type == 1) && (uvflag == 0)) { /* YUV */ + /* process the UV section */ + offset = UVoffset; + size = UVsize; + uvflag = 1; + } + + /* extract the buffers we procured earlier */ + buf = list_entry(free_list.next, struct ivtv_buffer, list); + list_del_init(&buf->list); + + buf->readpos = 0; + buf->buffer.index = x; + buf->buffer.sequence = sequence; + buf->ts = jiffies; + + if (size < (IVTV_DMA_BUF_SIZE & 0xffffff00)) { + buf->buffer.bytesused = size; + /* bytecount must be multiple of 0x100 (256) */ + itv->SGarray[x].size = + (0xffffff00 & (buf->buffer.bytesused + 0xFF)); + size = 0; + } else { + buf->buffer.bytesused = IVTV_DMA_BUF_SIZE; + itv->SGarray[x].size = IVTV_DMA_BUF_SIZE; + size -= IVTV_DMA_BUF_SIZE; + } + + itv->SGarray[x].src = offset; + offset += buf->buffer.bytesused; + + /* unfortunately the pci dma api wasn't properly defined + * for handling mapping errors (running out of iommu space, + * for instance). 0 can be a valid bus address. */ + buf->dma_handle = pci_map_single(itv->dev, + (void *)buf->buffer.m.userptr, + buf->buffer.bytesused, + PCI_DMA_FROMDEVICE); + + itv->SGarray[x].dst = buf->dma_handle; + + /* FIXME need to add pts stuff, index, etc. */ + __ivtv_enq_buf(&st->dma_q, buf); + } + + /* This should wrap gracefully */ + /* FIXME obselete? */ + itv->trans_id++; + + itv->SGarray[bufs_needed - 1].size |= 0x80000000; + + /*FIXME unlock */ + + data[0] = itv->SG_handle; + /* 3 elements * 4 bytes per element * num_elements */ + data[1] = (3 * 4 * bufs_needed); + data[2] = type; + data[3] = 0x0; + + for (x = 0; x < bufs_needed; x++) + IVTV_DEBUG(IVTV_DEBUG_INFO, + "SGarray[%d]: 0x%08x, 0x%08x 0x%08x\n", x, + itv->SGarray[x].src, itv->SGarray[x].dst, + itv->SGarray[x].size); + + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Sched dma: addr: 0x%08x, array_size 0x%08x," + " type 0x%08x\n", data[0], data[1], data[2]); + + set_bit(IVTV_F_S_DMAP, &st->s_flags); + ivtv_api(itv->enc_mbox, &itv->enc_msem, IVTV_API_SCHED_DMA_TO_HOST, + &result, 4, &data[0]); +} + +static void ivtv_sched_DMA_tasklet(unsigned long arg) +{ + struct ivtv *itv = (struct ivtv *)arg; + unsigned long flags; + + spin_lock_irqsave(&itv->lock, flags); + ivtv_sched_DMA(itv); + spin_unlock_irqrestore(&itv->lock, flags); +} + +/* FIXME this function does way more than it should */ +static int __devinit ivtv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + int retval = 0; + unsigned char pci_latency; + struct ivtv *itv; + struct video_channel v; + unsigned long freq; + u16 cmd; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Found card #%d\n", ivtv_cards_active); + + spin_lock_irq(&ivtv_lock); + + /* Make sure we've got a place for this card */ + if (ivtv_cards_active == IVTV_MAX_CARDS) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + ":Maximum # of cards already detected (%d).\n", + ivtv_cards_active); + spin_unlock_irq(&ivtv_lock); + return -ENOMEM; + } + + itv = &ivtv_cards[ivtv_cards_active]; + itv->dev = dev; + itv->num = ivtv_cards_active; + + ivtv_cards_active++; + + spin_unlock_irq(&ivtv_lock); + + /* always remember what you think the irq mask should be */ + itv->irqmask = 0; + +#ifdef YUV_FIXUP + itv->options.yuv_fixup = yuv_fixup; +#endif + itv->options.dec_yuv_buffers = dec_yuv_buffers; + itv->options.dec_mpg_buffers = mpg_buffers; + itv->options.yuv_buffers = yuv_buffers; + itv->options.mpg_buffers = mpg_buffers; + itv->options.vbi_buffers = vbi_buffers; + itv->options.num_devices = num_devices; + itv->options.dec_mpg_qlen = dec_mpg_qlen; + itv->options.dec_yuv_qlen = dec_yuv_qlen; + + /* Set FrameBuffer-ID to invalid */ + itv->fb_id = -1; + + switch (dev->subsystem_device) { + case IVTV_PCI_ID_250_V2: + case IVTV_PCI_ID_250_V4: + IVTV_DEBUG(IVTV_DEBUG_ERR, "Found an iTVC16 based chip\n"); + itv->card_type = IVTV_250_V2; + break; + case IVTV_PCI_ID_350_V1: + case IVTV_PCI_ID_350_V2: + IVTV_DEBUG(IVTV_DEBUG_ERR, "Found an iTVC15 based chip\n"); + itv->card_type = IVTV_350_V1; + break; + case IVTV_PCI_ID_250_V1: + case IVTV_PCI_ID_250_V3: + IVTV_DEBUG(IVTV_DEBUG_ERR, "Found an iTVC15 based chip\n"); + itv->card_type = IVTV_250_V1; + break; + default: /* Default to 250 v1 style */ + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Found an unknown chip, treating it like an iTVC15\n"); + itv->card_type = IVTV_250_V1; + break; + } + + init_MUTEX(&itv->enc_msem); + init_MUTEX(&itv->dec_msem); + init_MUTEX(&itv->sem_lock); + spin_lock_init(&itv->lock); + itv->base_addr = pci_resource_start(dev, 0); + itv->enc_mbox = NULL; + itv->dec_mbox = NULL; + itv->io_mem = NULL; + itv->reg_mem = NULL; + itv->i_flags = 0; + atomic_set(&itv->capturing, 0); + atomic_set(&itv->decoding, 0); + itv->user_dma_to_device_state = NULL; + + /* Prepare list for action! */ + INIT_LIST_HEAD(&itv->client_list); + + init_waitqueue_head(&itv->cap_w); + init_waitqueue_head(&itv->vsync_w); + init_waitqueue_head(&itv->dec_master_w); + init_timer(&itv->dec_timeout); + itv->dec_timeout.function = ivtv_dec_timeout; + itv->dec_timeout.data = (unsigned long)itv; + + tasklet_init(&itv->dma_sched_tq, ivtv_sched_DMA_tasklet, + (unsigned long)itv); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "base addr: 0x%08x\n", itv->base_addr); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Enabling pci device\n"); + if (pci_enable_device(dev)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Can't enable device %d!\n", + itv->num); + retval = -EIO; + goto err; + } + if (pci_set_dma_mask(dev, 0xffffffff)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + KERN_WARNING + "No suitable DMA available on card %d.\n", itv->num); + retval = -EIO; + goto err; + } + if (!request_mem_region + (pci_resource_start(dev, 0), IVTV_IOREMAP_SIZE, IVTV_DEVNAME)) { + retval = -EIO; + goto err; + } + + /* Check for bus mastering */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Bus Mastering is not enabled\n"); + retval = -ENXIO; + goto free_mem; + } else { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Bus Mastering Enabled."); + } + + pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "%d (rev %d) at %02x:%02x.%x, ", + itv->dev->device, itv->card_rev, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + IVTV_DEBUG(IVTV_DEBUG_INFO, + " irq: %d, latency: %d, memory: 0x%lx\n", itv->dev->irq, + pci_latency, (unsigned long)itv->base_addr); + + /*map io memory */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr, IVTV_ENCDEC_SIZE); + itv->io_mem = ioremap_nocache(itv->base_addr, IVTV_ENCDEC_SIZE); + if (!itv->io_mem) { + IVTV_DEBUG(IVTV_DEBUG_ERR, IVTV_IOREMAP_ERROR); + retval = -ENOMEM; + goto free_mem; + } + + /*map registers memory */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + itv->reg_mem = + ioremap_nocache(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (!itv->reg_mem) { + IVTV_DEBUG(IVTV_DEBUG_ERR, IVTV_IOREMAP_ERROR); + retval = -ENOMEM; + goto free_io; + } + + IVTV_DEBUG(IVTV_DEBUG_IRQ, "Masking interrupts\n"); + /* clear interrupt mask, effectively disabling interrupts */ + ivtv_set_irq_mask(itv, 0xffffffff); + + retval = request_irq(itv->dev->irq, ivtv_irq_handler, + SA_SHIRQ | SA_INTERRUPT, IVTV_DEVNAME, + (void *)itv); + if (retval) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "failed to register irq %d\n", + retval); + goto free_io; + } + + /* save itv in the pci struct for later use */ + pci_set_drvdata(dev, itv); + + /* active i2c */ + itv->i2c_command = (I2C_TIMING); + IVTV_DEBUG(IVTV_DEBUG_INFO, "activating i2c..\n"); + if (init_ivtv_i2c(itv)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "i2c died! unloading\n"); + goto free_irq; + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Active card count: %d.\n", + ivtv_cards_active); + + /*write firmware */ + retval = ivtv_firmware_init(itv); + if (retval) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error initializing.\n"); + retval = -ENOMEM; + goto free_i2c; + } + + /*search for encoder/decoder mailboxes */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "About to search for mailboxes\n"); + ivtv_find_firmware_mailbox(itv); + + if ((itv->enc_mbox == NULL) && (itv->dec_mbox == NULL)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error locating firmware.\n"); + retval = -ENOMEM; + goto free_i2c; + } + + /*releasing unneeded iomapped memory (encoder+decoder) */ + //iounmap(itv->io_mem); + + /*remapping only needed io memory (mailboxes) */ + itv->enc_mbox = + ioremap(itv->base_addr + + ((u8 *) itv->enc_mbox - (u8 *) itv->io_mem), + IVTV_MBOX_MAX_BOXES * IVTV_MBOX_SIZE); + itv->dec_mbox = + ioremap(itv->base_addr + + ((u8 *) itv->dec_mbox - (u8 *) itv->io_mem), + IVTV_MBOX_MAX_BOXES * IVTV_MBOX_SIZE); + + /* clearing pointers */ + //itv->io_mem = NULL ; + + /*Try and get firmware versions */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "Getting firmware version..\n"); + retval = ivtv_firmware_versions(itv); + if (retval) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "error %d getting version #!\n", + retval); + goto free_i2c; + } + + /* Allocate scatter-gather arrays */ + + //++MTY NASTY little bug!!! If user changes dec_mpg_buffers, + // memory corruption results with the old way! + + /* encoder */ + itv->SGarray = (struct ivtv_SG_element *) + kmalloc(sizeof(struct ivtv_SG_element) * SGarray_size, GFP_KERNEL); + if (!(itv->SGarray)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error allocating SGarray[].\n"); + retval = -ENOMEM; + goto free_i2c; + } + + itv->SG_handle = pci_map_single(itv->dev, + (void *)&itv->SGarray[0], + (sizeof(struct ivtv_SG_element) * + SGarray_size), PCI_DMA_TODEVICE); + + if (itv->card_type == IVTV_350_V1) { + /* decoder */ + itv->DSGarray = (struct ivtv_SG_element *) + kmalloc(sizeof(struct ivtv_SG_element) * + DSGarray_size, GFP_KERNEL); + if (!(itv->DSGarray)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error allocating DSGarray[].\n"); + retval = -ENOMEM; + goto free_sg; + } + + itv->DSG_handle = pci_map_single(itv->dev, + (void *)&itv->DSGarray[0], + (sizeof(struct ivtv_SG_element) + * DSGarray_size), + PCI_DMA_TODEVICE); + } + + /* FIXME -temporary- setup tuner */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "Setting Tuner\n"); + + if (tuner > -1) { + ivtv_call_i2c_client(itv, + IVTV_TUNER_I2C_ADDR, + TUNER_SET_TYPE, &tuner); + } + + /* set the standard */ + if (!ivtv_pal) + v.norm = VIDEO_MODE_NTSC; + else + v.norm = VIDEO_MODE_PAL; + + ivtv_call_i2c_client(itv, IVTV_TUNER_I2C_ADDR, VIDIOCSCHAN, &v); + + if (!ivtv_pal) { + /* set the channel */ + freq = 1076; /* ch. 4 67250*16/1000 */ + ivtv_call_i2c_client(itv, IVTV_TUNER_I2C_ADDR, VIDIOCSFREQ, + &freq); + } + + retval = ivtv_v4l2_setup(itv); + if (retval) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Problem starting v4l2\n"); + goto ivtv_v4l2_fail; + } + + return 0; + + ivtv_v4l2_fail: + pci_unmap_single(itv->dev, + itv->DSG_handle, + (sizeof(struct ivtv_SG_element) * + DSGarray_size), PCI_DMA_TODEVICE); + kfree(itv->DSGarray); + free_sg: + pci_unmap_single(itv->dev, + itv->SG_handle, + (sizeof(struct ivtv_SG_element) * + SGarray_size), PCI_DMA_TODEVICE); + kfree(itv->SGarray); + free_i2c: + exit_ivtv_i2c(itv); + free_irq: + free_irq(itv->dev->irq, (void *)itv); + free_io: + ivtv_iounmap(itv); + free_mem: + release_mem_region(pci_resource_start(itv->dev, 0), IVTV_IOREMAP_SIZE); + err: + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error %d on init\n", retval); + + spin_lock_irq(&ivtv_lock); + ivtv_cards_active--; + spin_unlock_irq(&ivtv_lock); + return retval; +} + +static void ivtv_remove(struct pci_dev *pci_dev) +{ + struct ivtv *itv = pci_get_drvdata(pci_dev); + int x = 0; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Disabling interrupts!\n"); + ivtv_set_irq_mask(itv, 0xffffffff); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping thread\n"); + atomic_set(&itv->decoding, 0); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Stopping card parts\n"); + x = ivtv_stop_firmware(itv); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing buffers\n"); + + for (x = 0; x < itv->v4l2.streamcount; x++) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing stream %d!\n", x); + ivtv_free_queue(&itv->v4l2.streams[x].free_q); + ivtv_free_queue(&itv->v4l2.streams[x].full_q); + ivtv_free_queue(&itv->v4l2.streams[x].dma_q); + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Unregistering v4l devices!\n"); + ivtv_v4l2_cleanup(itv); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Freeing dma resources\n"); + + pci_unmap_single(itv->dev, itv->SG_handle, + (sizeof(struct ivtv_SG_element) * SGarray_size), + PCI_DMA_TODEVICE); + kfree(itv->SGarray); + + pci_unmap_single(itv->dev, itv->DSG_handle, + (sizeof(struct ivtv_SG_element) * DSGarray_size), + PCI_DMA_TODEVICE); + kfree(itv->DSGarray); + + exit_ivtv_i2c(itv); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing irq\n"); + free_irq(itv->dev->irq, (void *)itv); + + if (itv->dev) { + ivtv_iounmap(itv); + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "releasing mem\n"); + if (itv) + release_mem_region(pci_resource_start(itv->dev, 0), + IVTV_IOREMAP_SIZE); + + /* FIXME free v4l2 stuff! */ + /* FIXME am i leaking kernel mem? */ + +} + +/* define a pci_driver for card detection */ +static struct pci_driver ivtv_pci_driver = { + name:"ivtv: iTVC15/16 mpg2 encoder card", + id_table:ivtv_pci_tbl, + probe:ivtv_probe, + remove:ivtv_remove, +}; + +#ifdef YUV_FIXUP +static int ivtv_YUV_fixup(struct ivtv_v4l2_stream *st, int count, + char *ubuf, struct ivtv_buffer *buf) +{ +/* + * int count = # of bytes to transfer to client + * st->ubytes = # of bytes written this frame + * ubuf = buffer to write to (user's buffer) + * buf = read buffer + * + */ + int src_width = 720; /* all known formats have src width of 720 */ + int Hoff, Voff; /* collectors for offsets to read position */ + int width, height; /* resolution of the capture stream */ + int curline; /* vertical line currently being processed */ + int maxline; /* height of combined frame */ + int cur_m_block; /* current horizontal offset of working mblock in this row */ + int maxblock; /* # of macroblocks in a row */ + int Hbytes; /* # of bytes to write to user this time around */ + int retval = 0; /* accumulator for total bytes written */ + int start; /* position in buf to read from */ + int buf_start; /* byte offset of first byte in this *buf */ + + height = st->format.fmt.pix.height; + width = st->format.fmt.pix.width; + maxblock = (width + 0xf) >> 4; + maxline = (int)(1.5 * height); /* 1 for Y, .5 for UV */ + /* Offset is always bufsize * buffer index + buf_start = (st->ubytes - buf->readpos); tested/works */ + + buf_start = IVTV_DMA_BUF_SIZE * buf->buffer.index; + + /* FIXME it may not be possible to get YUV width > 720 */ + // if (width > src_width) src_width=width; + + curline = (int)(st->ubytes / width); + + while (curline < maxline) { +// printk(" cl: %d, ml: %d\n", curline, maxline); + Voff = 16 * (curline & 0xf) + /* Voffset within MBlock */ + ((curline & 0xfff0) * src_width); /* Voffset of Mblock */ + + cur_m_block = (st->ubytes - (curline * width)) >> 4; + +/* printk("voff %d, macroVoff %d, Voff %d, cmb %d\n", (16 * (curline & 0xf)), + ((curline & 0xff0) * src_width), Voff, cur_m_block); +*/ + + while ((cur_m_block < maxblock) && (count > 0)) { + Hoff = (cur_m_block * 256) + /* mblock offset within line */ + /* Hoffset within mblock, usually 0 */ + ((st->ubytes - (curline * width)) & 0xf); + Hbytes = 16 - ((st->ubytes - (curline * width)) & 0xf); + + if (Hbytes > count) + Hbytes = count; + + start = Hoff + Voff; + + if (copy_to_user((char *)((u32) ubuf + retval), + (u32 *) ((u32) buf->buffer.m.userptr + + (start - buf_start)), + Hbytes)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "copy to user failed\n"); + return -EFAULT; + } + + count -= Hbytes; + retval += Hbytes; + st->ubytes += Hbytes; + + cur_m_block++; + } + + /* if user can't handle anymore data or buffer empty */ + curline++; + if ((count == 0)) /*|| ((curline * src_width) % IVTV_DMA_BUF_SIZE) == 0) */ + return retval; + } + + /* FIXME i don't think you should ever get here */ + IVTV_DEBUG(IVTV_DEBUG_ERR, + "You've just sailed off the edge of this function\n"); + return retval; +} +#endif + +long ivtv_read(struct ivtv_open_id *id, char *ubuf, size_t count, int block) +{ + int x, sleepctr, datalen, retval = 0, freed = 0; + struct ivtv *itv = id->itv; + size_t newcount; + unsigned long tid; + struct ivtv_buffer *buf; + struct ivtv_v4l2_stream *st = &itv->v4l2.streams[id->type]; + DECLARE_WAITQUEUE(wait, current); + + IVTV_DEBUG(IVTV_DEBUG_INFO, " Read stream.. \n"); + + if (atomic_read(&itv->capturing) == 0 && (st->id == -1)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Stream not initialized before read(shouldn't happen)\n"); + return -EIO; + } + + /* FIXME find a way to gracefully exit capture */ + + sleepctr = retval = 0; + buf = NULL; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&st->waitq, &wait); + do { + if ((itv->trans_id == 0) && (sleepctr >= IVTV_MAX_DATA_SLEEP)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Timeout waiting for data!\n"); + retval = -EIO; + break; + } + + buf = ivtv_deq_peek_head(itv, &st->full_q); + if (buf) { + break; + } else { + + /* Done capturing? */ + if (!test_bit(IVTV_F_S_CAP, &st->s_flags)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "No more data to send, returning 0\n"); + set_current_state(TASK_RUNNING); + remove_wait_queue(&st->waitq, &wait); + return 0; + } + } + + if (!block) { + retval = -EAGAIN; + break; + } + + ivtv_sleep_timeout(IVTV_SLEEP_WAIT); + + if (signal_pending(current)) + retval = -ERESTARTSYS; + + sleepctr++; + } while (!retval); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&st->waitq, &wait); + + /* an error (or signal) occured */ + if (retval) { + return retval; + } + + /* Skip the first 4 bytes of mpg streams to help out + * finicky decoders.. but not for iTVC16 */ + if ((id->type == 0) && (itv->first_read == 1) && + (itv->card_type != IVTV_250_V2)) { + for (x = 0; x < buf->buffer.bytesused - 4; x++) { + unsigned char *p; + itv->first_read = 0; + p = (unsigned char *)buf->buffer.m.userptr + + buf->readpos + x; + if (!p[0] && !p[1] && p[2] == 1) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Stripping first 4 bytes\n"); + buf->buffer.bytesused -= x; + buf->readpos += x; + break; + } + } + } + + /* data ready */ + /* copy it to the client */ + while ((count > 0) && (buf->buffer.bytesused > 0)) { + newcount = count; + datalen = buf->buffer.bytesused; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "datalen 0x%08x\n", datalen); + + if (newcount > datalen) + newcount = datalen; + +#ifdef YUV_FIXUP + if ((id->type == 1) && (itv->options.yuv_fixup)) { + newcount = + ivtv_YUV_fixup(st, newcount, ubuf + retval, buf); + if (newcount < 0) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error fixing up YUV!\n"); + return newcount; + } + } else { +#endif + if (copy_to_user((char *)((u32) ubuf + retval), + (u32 *) ((u32) buf->buffer.m.userptr + + buf->readpos), newcount)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "copy to user failed\n"); + return -EFAULT; + } +#ifdef YUV_FIXUP + } +#endif + + buf->readpos += newcount; + retval += newcount; + count -= newcount; + buf->buffer.bytesused -= newcount; + IVTV_DEBUG(IVTV_DEBUG_INFO, + "new datalen 0x%08x\n", buf->buffer.bytesused); + + /* if buffer is empty or we've read the whole frame */ + if ((buf->buffer.bytesused == 0)) { + ivtv_move_buf(itv, &st->full_q, &st->free_q, buf); + freed++; + + buf = ivtv_deq_peek_head(itv, &st->full_q); + if (buf) { + tid = buf->buffer.sequence; + if (buf->buffer.sequence != tid) { + /* end of frame! */ + st->ubytes = 0; + break; + } + } else { + /* user wanted more than we had. Since + * queues are filled in irq time, + * that means end of frame */ + st->ubytes = 0; + break; + } + } + } /* end of while */ + + /* if we put some buffers back in the free queue, kick off dma + * scheduling if card was stopped due to overflow before */ + if (freed && test_and_clear_bit(IVTV_F_S_OVERFLOW, &st->s_flags)) { + spin_lock_irq(&itv->lock); + ivtv_sched_DMA(itv); + spin_unlock_irq(&itv->lock); + } + + /*FIXME unlock */ + if (retval != 0) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Returning %d\n", retval); + return retval; + } + + /* Shouldn't ever get here */ + return -EIO; +} + +static void ivtv_swap_copy(const char *buf, const char *ubuf, size_t count) +{ + u32 *src, *dst; + + src = (u32 *) ubuf; + dst = (u32 *) buf; + +#ifdef CONFIG_X86 + while ((u32) src <= (u32) ubuf + count) { /* byteswap while copying */ + __asm__ __volatile__("bswap %0":"=r"(*dst):"0"(*src)); + src++; + dst++; + } +#else + { + int y; + /* Old slow memcpy then swab */ + memcpy((void *)buf, (void *)ubuf, count); + for (y = 0; y < count; y += 4) { + swab32s((u32 *) ((u32) buf + y)); + } + } +#endif +} + +static int ivtv_fill_dec_buffers(struct ivtv_open_id *id, const char *ubuf, + size_t count, int block) +{ + struct ivtv *itv = id->itv; + struct ivtv_v4l2_stream *stream = &itv->v4l2.streams[id->type]; + struct ivtv_buffer *buf; + int copybytes = 0, bytesread = 0, retval = 0; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_fill_dec_buffers, %d bytes\n", count); + + /* Read what the user gives us. queue it for DMA after each buffer + * also enqueue partly-full buffers. */ + + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DEC: free_q: %d elements\n", stream->free_q.elements); + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DEC: dma_q: %d elements\n", stream->dma_q.elements); + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DEC: full_q: %d elements\n", stream->full_q.elements); + + /* FIXME will only do one write. Has underlying code to handle more + * than one, just need loop control logic for it, if it's + * deemed necessary. */ + while (bytesread == 0) { + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + + buf = NULL; + add_wait_queue(&stream->waitq, &wait); + do { + set_current_state(TASK_INTERRUPTIBLE); + buf = ivtv_deq_peek_head(itv, &stream->free_q); + if (buf) + break; + + if (!block) { + retval = -EAGAIN; + break; + } + + schedule(); + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + spin_lock_irqsave(&itv->lock, flags); + ivtv_dec_sched_DMA(id->itv); + spin_unlock_irqrestore(&itv->lock, flags); + } while (!buf); + set_current_state(TASK_RUNNING); + remove_wait_queue(&stream->waitq, &wait); + + if (retval) + return retval; + + /* bytes left to send > free bytes in current buffer */ + if ((count - bytesread) > + (IVTV_DMA_DEC_BUF_SIZE - buf->buffer.bytesused)) { + copybytes = + IVTV_DMA_DEC_BUF_SIZE - buf->buffer.bytesused; + } else { + copybytes = count - bytesread; + } + + /* copy data */ + /* FIXME */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "copying %d bytes to 0x%08x" + " (buffer free = %d, used = %d)\n", + copybytes, + (int)(buf->buffer.m.userptr + + buf->buffer.bytesused), + (int)(IVTV_DMA_DEC_BUF_SIZE - + buf->buffer.bytesused), buf->buffer.bytesused); + + ivtv_swap_copy((char *)(buf->buffer.m.userptr + + buf->buffer.bytesused), + (char *)((u32) ubuf + bytesread), copybytes); + + bytesread += copybytes; + buf->buffer.bytesused += copybytes; + + /* enq buffer when full */ + if (buf->buffer.bytesused == IVTV_DMA_DEC_BUF_SIZE) + ivtv_move_buf(itv, &stream->free_q, &stream->full_q, + buf); + } + + return bytesread; +} + +/* + * Schedule host -> hardware DMA of one buffer from the stream (MPEG or YUV) + * with the most recent request for more data, but only if dec->dec_needs_data + * is set. + * + * This code can be called from both interrupt context as well as userspace; + * it does the right things in either case. If called from userspace, it may + * block only when the same call is in progress in interupt context (since + * interrupt context is not allowed to block.) + * + * @returns 0 if the buffer was queued to dma_q and the DMA was initiated. + * + * -EAGAIN if either the full_q queue is empty or the function is + * already in progress in interrupt context. + * + * -ENOSPC if there is no space remaining in the hardware's buffer. + * This should never happen if proper flow control is used. + * + * -EINVAL if the most recent "data needed" interrupt requested an + * unknown stream type (should really never happen!) + * + * -EBUSY if a DMA on the same queue is already in progress (should + * never happen) + * + */ +static void ivtv_dec_sched_DMA(struct ivtv *itv) +{ + int ret = 0, x = 0, bytes_written = 0, type = 0, max = 2; + struct ivtv_buffer *buf; + struct ivtv_v4l2_stream *stream = NULL; + u32 data[IVTV_MBOX_MAX_DATA], result; + u32 mem_offset, mem_size, hw_stream_type, buffer_bytes; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_dec_sched_DMA\n"); + + /* fancy way of saying "if (ivtv->dec_needs_data == 0)" */ + if (!test_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: no data needed\n"); + return; + } + + /* Get Card Mem Dst address from mailbox 10 */ + ret = ivtv_api_getresult_nosleep(&itv->dec_mbox[9], &result, &data[0]); + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DEC: Mailbox 10: 0x%08x 0x%08x 0x%08x 0x%08x\n", data[0], + data[1], data[2], data[3]); + + hw_stream_type = data[0]; + + switch (hw_stream_type) { + case 0: /* MPG */ + type = IVTV_DEC_STREAM_TYPE_MPG; + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: mpg data\n"); + break; + case 2: /* YUV */ + type = IVTV_DEC_STREAM_TYPE_YUV; + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: yuv data\n"); + break; + default: + IVTV_DEBUG(IVTV_DEBUG_ERR, "DEC: unknown stream type %d\n", + data[0]); + max = 0; + return; + } + + stream = &itv->v4l2.streams[type]; + + if (test_bit(IVTV_F_I_BUSY, &itv->i_flags)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: decoder busy, delaying\n"); + set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags); + return; + } + + /* If we got this far, we have data to send and it wants it */ + clear_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags); + + /* Get card mem addr and size from data array */ + mem_offset = data[1]; + mem_size = data[2]; + buffer_bytes = data[3]; /* # bytes in card's buffer */ + + while ((max > x) && (mem_size > bytes_written)) { /* send a maximum of + 'max' buffers */ + buf = __ivtv_deq_peek_head(&stream->full_q); + if (buf == NULL) { + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DEC: No more buffers to send\n"); + break; + } +#if 1 + if (mem_size < buf->buffer.bytesused) { + itv->DSGarray[x].size = mem_size; + } else { + itv->DSGarray[x].size = buf->buffer.bytesused; + } +#else + /* just send the whole buffer */ + itv->DSGarray[x].size = buf->buffer.bytesused; +#endif + buf->dma_handle = pci_map_single(itv->dev, + (void *)(buf->buffer.m. + userptr + + buf->readpos), + itv->DSGarray[x].size, + PCI_DMA_TODEVICE); + + itv->DSGarray[x].src = buf->dma_handle; + itv->DSGarray[x].dst = (mem_offset + bytes_written + + IVTV_FIRM_SEARCH_DECODER_START); + + buf->readpos += itv->DSGarray[x].size; + bytes_written += itv->DSGarray[x].size; + buf->buffer.bytesused -= itv->DSGarray[x].size; + + IVTV_DEBUG(IVTV_DEBUG_INFO, + "1st 32bits of buffer %d are 0x%08x\n", + buf->buffer.index, *(u32 *) buf->buffer.m.userptr); + IVTV_DEBUG(IVTV_DEBUG_INFO, + "DSGarray[%d]: 0x%08x, 0x%08x 0x%08x\n", x, + itv->DSGarray[x].src, itv->DSGarray[x].dst, + itv->DSGarray[x].size); + + /* buffer is empty? */ + if (buf->buffer.bytesused == 0) { + __ivtv_del_buf(&stream->full_q, buf); + __ivtv_enq_buf(&stream->dma_q, buf); + } + x++; + } + + if (x == 0) { /* no full buffers */ + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: Nothing to send\n"); + set_bit(IVTV_F_I_NEEDS_DATA, &itv->i_flags); + return; + } + //Set Last Element Bit + itv->DSGarray[x - 1].size |= 0x80000000; + + //Schedule DMA XFER + data[0] = itv->DSG_handle; + data[1] = bytes_written; + data[2] = hw_stream_type; + + /* note that we're DMA'ing */ + mod_timer(&itv->dec_timeout, jiffies + DEC_DMA_TIMEOUT); + set_bit(IVTV_F_S_DMAP, &stream->s_flags); + set_bit(IVTV_F_I_BUSY, &itv->i_flags); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_DEC_DMA_FROM_HOST, + &result, 3, &data[0]); + + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Sched DEC dma: addr: 0x%08x, array_size 0x%08x, type 0x%08x\n", + data[0], data[1], data[2]); +} + +static void ivtv_dec_DMA_done(struct ivtv *itv) +{ + struct ivtv_v4l2_stream *stream = NULL; + struct ivtv_buffer *buf; + int y, stmtype = -1, freed = 0; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: DMA Done tasklet\n"); + + if (!test_and_clear_bit(IVTV_F_I_BUSY, &itv->i_flags)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "DMAP not set\n"); + ivtv_show_debug_flags(itv); + return; + } +#if 0 + del_timer(&itv->dec_timeout); +#else + mod_timer(&itv->dec_timeout, jiffies + DEC_DMA_TIMEOUT); +#endif + + for (y = IVTV_DEC_STREAM_TYPE_MPG; y < itv->v4l2.streamcount; y++) { + if (test_and_clear_bit + (IVTV_F_S_DMAP, &itv->v4l2.streams[y].s_flags)) { + stmtype = y; + break; + } + } + + /* Handle OSD DMA */ + if (test_and_clear_bit(IVTV_F_I_OSD_DMA, &itv->i_flags)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "OSD: DMA Done\n"); + + /* wake all the normal streams, in case they fell asleep */ + for (y = IVTV_DEC_STREAM_TYPE_MPG; y < itv->v4l2.streamcount; + y++) { + wake_up(&itv->v4l2.streams[y].waitq); + } + + wake_up(&itv->dec_master_w); + return; + } + + if (stmtype < 0) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DEC: Got DMA-done, not expecting one\n"); + ivtv_show_debug_flags(itv); + return; + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: Stream %d dma-done\n", y); + stream = &itv->v4l2.streams[y]; + + while ((buf = __ivtv_deq_buf(&stream->dma_q)) != NULL) { + IVTV_ASSERT(buf->dma_handle != IVTV_DMA_UNMAPPED); + pci_unmap_single(itv->dev, buf->dma_handle, + IVTV_DMA_DEC_BUF_SIZE, PCI_DMA_TODEVICE); + buf->dma_handle = IVTV_DMA_UNMAPPED; + buf->buffer.bytesused = 0; + buf->readpos = 0; + + /* put in the 'done' queue */ + __ivtv_enq_buf(&stream->free_q, buf); + freed++; + } + + /* if we put some buffers back in the free queue, kick off dma + * scheduling if card was stopped due to overflow before */ + if (freed && test_and_clear_bit(IVTV_F_S_OVERFLOW, &stream->s_flags)) + ivtv_sched_DMA(itv); + + /* wake up queue filler */ + wake_up(&stream->waitq); + wake_up(&itv->dec_master_w); +} + +int ivtv_get_timing_info(struct ivtv *itv, struct ivtv_ioctl_framesync *info) +{ + u32 ret, result, data[IVTV_MBOX_MAX_DATA]; + + int suicidecounter = 0; + + memset(info, 0x00, sizeof(struct ivtv_ioctl_framesync)); + + /* Occasionally, we'll get a wierd, invalid number for + * frames played. fortunately, it sets the SCR timestamp to 0 + * in that case, which it never is otherwise. cool, huh */ + while (info->scr == 0) { /* eliminate bogus values, FIXME ugly */ + ret = ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_DEC_TIMING_INFO, &result, 0, &data[0]); + if (ret) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "DEC: err sending timing info\n"); + return ret; + } + + info->frame = data[0]; + info->pts = ((u64) data[2] << 32) | (u64) data[1]; + info->scr = ((u64) data[4] << 32) | (u64) data[3]; + + if (suicidecounter++ > 10) { /* endless loops are bad! */ + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Timeout getting frames played\n"); + return -1; + } + if (info->scr == 0) + ivtv_sleep_timeout(HZ / 50); + } + + return 0; +} + +ssize_t ivtv_write(struct ivtv_open_id * id, const char *ubuf, size_t count, + int block) +{ + int bytes_written = 0, ret = 0; + unsigned long flags; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "ivtv_write\n"); + + while (bytes_written < count) { /* completely use up user data + * before returning */ + /* buffer the data - this may block waiting on free buffers */ + ret = ivtv_fill_dec_buffers(id, ubuf + bytes_written, + (count - bytes_written), block); + + /* FIXME temporary hack to make sure non-blocking works */ + /* send it! it'll return right away if no data needed */ + spin_lock_irqsave(&id->itv->lock, flags); + ivtv_dec_sched_DMA(id->itv); + spin_unlock_irqrestore(&id->itv->lock, flags); + + if (ret < 0) { + break; + } else { + bytes_written += ret; + } + } + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: returning %d\n", bytes_written); + return bytes_written ? bytes_written : ret; +} + +unsigned int ivtv_dec_poll(struct file *filp, poll_table * wait) +{ + struct ivtv_open_id *id = filp->private_data; + unsigned int mask = 0; + unsigned long flags; + + /* add stream's waitq to the poll list */ + poll_wait(filp, &id->itv->v4l2.streams[id->type].waitq, wait); + + /* FIXME temporary hack to restart DMA in case the decoder's + been busy for a while and the application is using poll to + see if it can write */ + spin_lock_irqsave(&id->itv->lock, flags); + ivtv_dec_sched_DMA(id->itv); + spin_unlock_irqrestore(&id->itv->lock, flags); + + if (ivtv_deq_peek_head + (id->itv, &id->itv->v4l2.streams[id->type].free_q)) + mask |= POLLOUT | POLLWRNORM; /* Writable */ + + IVTV_DEBUG(IVTV_DEBUG_INFO, "DEC: dec_poll returning 0x%x\n", mask); + if (!mask) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder fullq size %d\n", + id->itv->v4l2.streams[id->type].full_q.elements); + IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder freeq size %d\n", + id->itv->v4l2.streams[id->type].free_q.elements); + IVTV_DEBUG(IVTV_DEBUG_INFO, "Decoder dmaq size %d\n", + id->itv->v4l2.streams[id->type].dma_q.elements); + } + return mask; +} + +unsigned int ivtv_poll(struct file *filp, poll_table * wait) +{ + struct ivtv_open_id *id = filp->private_data; + unsigned int mask = 0; + + /* add stream's waitq to the poll list */ + poll_wait(filp, &id->itv->v4l2.streams[id->type].waitq, wait); + +#if 0 + if (down_interruptible(&id->itv->sem_lock)) + return -ERESTARTSYS; + + if (ivtv_get_free_elements + (id->itv, &id->itv->v4l2.streams[id->type].full_q)) + mask |= POLLIN | POLLRDNORM; /* readable */ + + up(&id->itv->sem_lock); +#else + mask |= POLLIN | POLLRDNORM; +#endif + return mask; +} + +#if 0 +static void ivtv_print_boxes(struct ivtv_mailbox *mbox) +{ + + int x, y; + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Sleeping for 10ms\n"); + ivtv_sleep_timeout(HZ / 100); + + if (NULL == mbox) { + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "Mailboxes not initialized!\n"); + return; + } + + for (x = 0; x <= IVTV_MBOX_MAX_BOXES; x++) { + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "mbox: 0x%08x, # %d\n", (u32) mbox, + x); + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "flags: 0x%08x ", + (u32) readl(&mbox->flags)); + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "cmd: 0x%08x\n", + readl(&mbox->cmd)); + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "result: 0x%08x ", + readl(&mbox->retval)); + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "timeout: 0x%08x\n", + readl(&mbox->timeout)); + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "Data:\n"); + for (y = 0; y < IVTV_MBOX_MAX_DATA; y++) { + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "[%02d]0x%08x, ", y, + readl(&mbox->data[y])); + if (2 == y % 3) + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "\n"); + } + /*Since mbox has type ptr, this should step it up */ + /* to the start of the next mbox */ + mbox++; + IVTV_DEBUG(IVTV_DEBUG_IOCTL, "\n"); + } +} +#endif + +int ivtv_close(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + int ret = 0; + + /* sem_lock must be held */ + IVTV_ASSERT(ivtv_sem_count(&itv->sem_lock) <= 0); + + switch (id->type) { + case IVTV_DEC_STREAM_TYPE_MPG: /* decoder streams */ + case IVTV_DEC_STREAM_TYPE_YUV: + if (atomic_read(&itv->decoding)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "close stopping decode\n"); + ret = ivtv_stop_decode(id); + } + break; + default: /* encoder streams */ + if (atomic_read(&itv->capturing)) { + IVTV_DEBUG(IVTV_DEBUG_INFO, "close stopping capture\n"); + ret = ivtv_stop_capture(id); + } + break; + } + + return ret; +} + +static int module_start(void) +{ + int loop_a; + + printk("ivtv: version %s (%s) loading\n", IVTV_VERSION_STRING(ivtv_rev), + IVTV_VERSION_COMMENT(ivtv_rev)); + + memset(&ivtv_cards[0], 0, IVTV_MAX_CARDS * sizeof(struct ivtv)); + + /* Validate parameters */ + if (((yuv_buffers > IVTV_MAX_YUV_BUFFERS) + || (yuv_buffers < IVTV_MIN_YUV_BUFFERS)) + && (yuv_buffers != 0)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! yuv_buffers must be between 40 and 500\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } +#ifdef YUV_FIXUP + if ((yuv_fixup != 0) && (yuv_fixup != 1)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error! yuv_fixup must be 0 or 1\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } +#endif + if ((dec_mpg_buffers > IVTV_MAX_DEC_MPG_BUFFERS) + || (dec_mpg_buffers < IVTV_MIN_DEC_MPG_BUFFERS)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! dec_mpg_buffers must be between 5 and 100\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if (((dec_yuv_buffers > IVTV_MAX_DEC_YUV_BUFFERS) + || (dec_yuv_buffers < IVTV_MIN_DEC_YUV_BUFFERS)) + && (dec_yuv_buffers != 0)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! dec_yuv_buffers must be between 17 and 500\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if ((mpg_buffers > IVTV_MAX_MPG_BUFFERS) + || (mpg_buffers < IVTV_MIN_MPG_BUFFERS)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! mpg_buffers must be between 15 and 100\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if ((dec_yuv_qlen > dec_yuv_buffers) + || (dec_yuv_qlen < IVTV_MIN_DEC_YUV_QLEN)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! dec_yuv_qlen must be between %d and %d\n", + IVTV_MIN_DEC_YUV_QLEN, dec_yuv_buffers); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if ((dec_mpg_qlen > dec_mpg_buffers) + || (dec_mpg_qlen < IVTV_MIN_DEC_MPG_QLEN)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! dec_mpg_qlen must be between %d and %d\n", + IVTV_MIN_DEC_MPG_QLEN, dec_mpg_buffers); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if ((vbi_buffers > IVTV_MAX_VBI_BUFFERS) + || (vbi_buffers < IVTV_MIN_VBI_BUFFERS)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! vbi_buffers must be between 3 and 100\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if ((num_devices > IVTV_MAX_CARDS) || (num_devices < 1)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, + "Error! num_devices must be between 1 and 9 (not working yet)\n"); + IVTV_DEBUG(IVTV_DEBUG_ERR, "Exiting..\n"); + return -1; + } + + if (ivtv_debug < 0) + IVTV_DEBUG(IVTV_DEBUG_ERR, "debug value must be >= 0!\n"); + + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Loading, I'll try to detect %d devices!\n", num_devices); + IVTV_DEBUG(IVTV_DEBUG_INFO, " .. running on kernel %s\n", UTS_RELEASE); + IVTV_DEBUG(IVTV_DEBUG_INFO, + "Setting some variables to invalid for detection\n"); + + for (loop_a = 0; loop_a < IVTV_MAX_CARDS; loop_a++) { + ivtv_cards[loop_a].num = -1; + ivtv_cards[loop_a].dev = NULL; + } + + SGarray_size = (mpg_buffers + yuv_buffers + vbi_buffers) * 2; + DSGarray_size = (dec_mpg_buffers + dec_yuv_buffers) * 2; + + IVTV_DEBUG(IVTV_DEBUG_ERR, "SGarray_size = %d, DSGarray_size = %d\n", + SGarray_size, DSGarray_size); + + IVTV_DEBUG(IVTV_DEBUG_INFO, "Scanning PCI bus..\n"); + if (pci_module_init(&ivtv_pci_driver)) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "Error detecting PCI card\n"); + return -ENODEV; + } + + printk("ivtv: loaded\n"); + return 0; +} + +static void module_cleanup(void) +{ + + pci_unregister_driver(&ivtv_pci_driver); + IVTV_DEBUG(IVTV_DEBUG_ERR, "You've not seen the last of willy!\n"); +#ifdef AEW_DEBUG + DUMP_BAD_ALLOC_TABLE; +#endif // AEW_DEBUG +} + +EXPORT_SYMBOL(ivtv_pal); +EXPORT_SYMBOL(ivtv_set_irq_mask); +EXPORT_SYMBOL(ivtv_cards_active); +EXPORT_SYMBOL(ivtv_cards); +EXPORT_SYMBOL(ivtv_api); +EXPORT_SYMBOL(ivtv_clear_irq_mask); +EXPORT_SYMBOL(ivtv_debug); + +module_init(module_start); +module_exit(module_cleanup); diff -upN /dev/null current/drivers/media/video/ivtv-fb.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/ivtv-fb.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,1039 @@ +/* + * iTVC15 Framebuffer driver + * + * This module presents the iTVC15 OSD (onscreen display) framebuffer memory + * as a standard Linux /dev/fb style framebuffer device. The framebuffer has + * a 32 bpp packed pixel format with full alpha channel support. Depending + * on the TV standard configured in the ivtv module at load time, resolution + * is fixed at either 720x480 (NTSC) or 720x576 (PAL). + * + * Copyright (c) 2003 Matt T. Yourst + * + * Derived from drivers/video/vesafb.c + * Portions (c) 1998 Gerd Knorr + * + * This file is licensed under the GNU General Public License, version 2. + * + */ + +/* +# +# Instructions for making ivtv-fb work with XFree86: +# Add the following sections and parts thereof to /etc/X11/XF86Config: +# + +# +# NOTE: The monitor section is obtainable by running: +# fbset -fb /dev/fb1 -x +# (or /dev/fbX for whatever framebuffer ivtv-fb is on) +# +Section "Monitor" + Identifier "NTSC Monitor" + HorizSync 30-68 + VertRefresh 50-120 + Mode "720x480" + # D: 34.563 MHz, H: 37.244 kHz, V: 73.897 Hz + DotClock 34.564 + HTimings 720 752 840 928 + VTimings 480 484 488 504 + Flags "-HSync" "-VSync" + EndMode +EndSection + +Section "Device" + Identifier "Hauppauge PVR 350 iTVC15 Framebuffer" + Driver "fbdev" + Option "fbdev" "/dev/fb1" # <-- modify if using another device + BusID "0:10:0" +EndSection + +Section "Screen" + Identifier "TV Screen" + Device "Hauppauge PVR 350 iTVC15 Framebuffer" + Monitor "NTSC Monitor" + DefaultDepth 24 + DefaultFbbpp 32 + Subsection "Display" + Depth 24 + FbBpp 32 + Modes "720x480" + EndSubsection +EndSection + +Section "ServerLayout" + ... + + Screen 0 "Screen 1" # << (your computer monitor) + + # (add the following line) + Screen 1 "TV Screen" RightOf "Screen 1" # << (TV screen) + + ... +EndSection + +# +# Then start X as usual; both your normal (computer) monitor and the +# NTSC or PAL TV monitor should display the default X background. +# +# Note the "RightOf" clause above: if you move the mouse off the right +# side of the computer screen, the pointer should appear on your TV +# screen. Keyboard events will go to windows in either screen. +# +# To start a program (e.g., xterm) on the TV only: +# +# export DISPLAY=:0.1 (i.e., X server #0, screen #1 = TV) +# xterm& +# +# There is also a way to join both the computer monitor and TV into +# one giant virtual screen using the Xinerama extension, but I haven't +# tried it. Doing so may not be such a good idea anyway, as you obviously +# wouldn't want random X windows getting moved over the TV picture. + +A note on unloading the fb driver: + +If you want to be able to unload the framebuffer driver (and you aren't +already using fbcon), add this to your lilo config: + +video=vc:x-y + +where x is the first fb device to allocate and y is the second. If you +already have a fb driver loaded, fiddle with the numbers so all the consoles +are already allocated. For me, i just set it to 0-0, ie: + +in lilo.conf: + +image=/vmlinuz + label=linux + read-only + append="root=/dev/hda1 video=vc:0-0" + +--OR-- +on bootup, do this +LILO: linux video=vc:0-0 + +according to how i read /usr/src/linux/drivers/video/fbmem.c and +/usr/src/linux/drivers/char/console.c, that should disable the +console hijacks, and allow you to unload the driver. + +-tmk +# +# +# +# +# +# +# +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef CONFIG_MTRR +#include +#endif + +#include "ivtv.h" + +/* + * card parameters + */ + +static int ivtv_fb_card_id; + +/* Card selected as framebuffer for this module instance: */ +static struct ivtv *ivtv_fb; + +/* card */ +static unsigned long video_base; /* physical addr */ +static unsigned long video_rel_base; /* address relative to base of decoder memory */ +static int video_size; +static char *video_vbase; /* mapped */ + +/* mode */ +static int video_width; +static int video_height; +static int video_height_virtual; +static int video_linelength; +static unsigned long shadow_framebuf_offset; +static unsigned long shadow_framebuf_size; + +/* + * ivtv API calls for framebuffer related support + */ + +static inline int ivtv_api_fb_get_framebuffer(struct ivtv *itv, + void **fbbase, int *fblength) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + int rc; + + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_framebuffer\n"); + + rc = ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_FB_GET_FRAMEBUFFER, &result, 0, &data[0]); + *fbbase = (void *)data[0]; + *fblength = data[1]; + return rc; +} + +static inline int ivtv_api_fb_get_pixel_format(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_pixel_format\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_PIXEL_FORMAT, + &result, 0, &data[0]); + return data[0]; +} + +static inline int ivtv_api_fb_set_pixel_format(struct ivtv *itv, int format) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + data[0] = format; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_pixel_format\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_FB_SET_PIXEL_FORMAT, &result, 1, &data[0]); + return result; +} + +static inline int ivtv_api_fb_get_state(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_state\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_STATE, + &result, 0, &data[0]); + return data[0]; +} + +static inline int ivtv_api_fb_set_state(struct ivtv *itv, int enabled) +{ + u32 params[IVTV_MBOX_MAX_DATA], result; + params[0] = enabled; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_state\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_SET_STATE, + &result, 1, ¶ms[0]); + return result; +} + +static inline int ivtv_api_fb_set_framebuffer_window(struct ivtv *itv, + int left, int top, + int width, int height) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_framebuffer_window\n"); + data[0] = width; + data[1] = height; + data[2] = left; + data[3] = top; + + ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_FB_SET_FRAMEBUFFER_WINDOW, &result, 4, &data[0]); + return result; +} + +static inline int ivtv_api_fb_get_osd_coords(struct ivtv *itv, + struct ivtv_osd_coords *osd) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_osd_coords\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_OSD_COORDS, + &result, 0, &data[0]); + + osd->offset = data[0] - video_rel_base; + osd->max_offset = video_size; + osd->pixel_stride = data[1]; + osd->lines = data[2]; + osd->x = data[3]; + osd->y = data[4]; + + return result; +} + +static inline int ivtv_api_fb_set_osd_coords(struct ivtv *itv, const struct ivtv_osd_coords + *osd) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_osd_coords\n"); + data[0] = osd->offset; + data[1] = osd->pixel_stride; + data[2] = osd->lines; + data[3] = osd->x; + data[4] = osd->y; + + // FIXME maybe wait on vsync? + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_SET_OSD_COORDS, + &result, 5, &data[0]); + return result; +} + +static inline int ivtv_api_fb_get_screen_coords(struct ivtv *itv, + struct rectangle *r) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_screen_coords\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_SCREEN_COORDS, + &result, 0, &data[0]); + + r->x0 = data[0]; + r->y0 = data[1]; + r->x1 = data[2]; + r->y1 = data[3]; + + return result; +} + +static inline int ivtv_api_fb_set_screen_coords(struct ivtv *itv, + const struct rectangle *r) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_screen_coords\n"); + data[0] = r->x0; + data[1] = r->y0; + data[2] = r->x1; + data[3] = r->y1; + + ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_FB_SET_SCREEN_COORDS, &result, 4, &data[0]); + return result; +} + +static inline int ivtv_api_fb_get_global_alpha(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_global_alpha\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_GLOBAL_ALPHA, + &result, 0, &data[0]); + return data[1]; +} + +static inline int ivtv_api_fb_set_global_alpha(struct ivtv *itv, + int enable_global, + int alpha, int enable_local) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_global_alpha\n"); + data[0] = enable_global; + data[1] = alpha; + data[2] = !enable_local; + ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_FB_SET_GLOBAL_ALPHA, &result, 3, &data[0]); + return result; +} + +static inline int ivtv_api_fb_get_flicker_state(struct ivtv *itv) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_get_flicker_state\n"); + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_GET_FLICKER_STATE, + &result, 0, &data[0]); + return data[0]; +} + +static inline int ivtv_api_fb_set_flicker_state(struct ivtv *itv, int enabled) +{ + u32 params[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_set_flicker_state\n"); + params[0] = enabled; + + ivtv_api(itv->dec_mbox, &itv->dec_msem, + IVTV_API_FB_SET_FLICKER_STATE, &result, 1, ¶ms[0]); + return result; +} + +static inline int ivtv_api_fb_blt_fill(struct ivtv *itv, int rasterop, + int alpha_mode, int alpha_mask_mode, + int width, int height, int destmask, + u32 destaddr, int deststride, u32 value) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_api_fb_blt_fill\n"); + data[0] = rasterop; + data[1] = alpha_mode; + data[2] = alpha_mask_mode; + data[3] = width; + data[4] = height; + data[5] = destmask; + data[6] = destaddr; + data[7] = deststride; + data[8] = value; + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_BLT_FILL, &result, + 9, &data[0]); + return result; +} + +static inline int ivtv_api_fb_blt_copy(struct ivtv *itv, int rasterop, + int alpha_mode, int alpha_mask_mode, + int width, int height, int destmask, + u32 destaddr, int deststride, + int sourcestride, int sourceaddr) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, + "ivtv_api_fb_blt_copy: width = %d, height = %d, destaddr = %d, deststride = %d, sourcestride = %d, sourceaddr = %d\n", + width, height, destaddr, deststride, sourcestride, + sourceaddr); + + data[0] = rasterop; + data[1] = alpha_mode; + data[2] = alpha_mask_mode; + data[3] = width; + data[4] = height; + data[5] = destmask; + data[6] = destaddr; + data[7] = deststride; + data[8] = sourcestride; + data[9] = sourceaddr; + + ivtv_api(itv->dec_mbox, &itv->dec_msem, IVTV_API_FB_BLT_COPY, &result, + 10, &data[0]); + return result; +} + +MODULE_PARM(ivtv_fb_card_id, "i"); +MODULE_PARM_DESC(ivtv_fb_card_id, + "ID number of ivtv card to use as framebuffer device (0-2)"); + +MODULE_LICENSE("GPL"); + +/* --------------------------------------------------------------------- */ + +static struct fb_var_screeninfo ivtvfb_defined = { + 0, 0, 0, 0, /* W,H, W, H (virtual) load xres,xres_virtual */ + 0, 0, /* virtual -> visible no offset */ + 32, /* depth -> load bits_per_pixel */ + 0, /* greyscale ? */ + {0, 0, 0}, /* R */ + {0, 0, 0}, /* G */ + {0, 0, 0}, /* B */ + {0, 0, 0}, /* transparency */ + 0, /* standard pixel format */ + FB_ACTIVATE_NOW, + -1, -1, + 0, + 0L, 0L, 0L, 0L, 0L, + 0L, 0L, 0, /* No sync info */ + FB_VMODE_NONINTERLACED, + 0, + {0, 0, 0, 0, 0} +}; + +static struct fb_info fb_info; + +#ifdef CONFIG_MTRR +static int mtrr = 1; //++MTY +static unsigned long fb_start_aligned_physaddr; /* video_base rounded down as required by hardware MTRRs */ +static unsigned long fb_end_aligned_physaddr; /* video_base rounded up as required by hardware MTRRs */ +#endif + +/* --------------------------------------------------------------------- */ +static int _ivtvfb_set_var(struct fb_var_screeninfo *var) +{ + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "_ivtvfb_set_var\n"); + + if (var->xres != ivtvfb_defined.xres || + var->yres != ivtvfb_defined.yres || + var->xres_virtual != ivtvfb_defined.xres_virtual || + var->yres_virtual > video_height_virtual || + var->yres_virtual < video_height || + var->xoffset || + var->bits_per_pixel != ivtvfb_defined.bits_per_pixel || + var->nonstd) { + return -EINVAL; + } + return 0; + +} +static int _ivtvfb_get_fix(struct fb_fix_screeninfo *fix) +{ + memset(fix, 0, sizeof(struct fb_fix_screeninfo)); + strcpy(fix->id, "iTVC15 TV out"); + fix->smem_start = video_base; + fix->smem_len = video_size; + fix->type = FB_TYPE_PACKED_PIXELS; + fix->visual = FB_VISUAL_TRUECOLOR; + fix->xpanstep = 0; + fix->ypanstep = 0; + fix->ywrapstep = 0; + fix->line_length = video_linelength; + return 0; +} + +static int ivtvfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_fb_check_var\n"); + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_set_var\n"); + return (_ivtvfb_set_var(&info->var)); +} +static int ivtvfb_set_par(struct fb_info *info) +{ + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_set_var\n"); + return (_ivtvfb_set_var(&info->var)); +} +static int ivtvfb_setcolreg(unsigned regno, unsigned red, unsigned green, + unsigned blue, unsigned transp, + struct fb_info *info) +{ + return (0); +} + +static int ivtv_fb_blt_copy(struct ivtv *itv, int x, int y, int width, + int height, int source_offset, int source_stride) +{ + int rc; + unsigned long destaddr = ((y * video_width) + x) * 4; + + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_fb_blt_copy\n"); + source_offset += shadow_framebuf_offset; + + rc = ivtv_api_fb_blt_copy(ivtv_fb, 0xa, 0x1, 0x0, width, height, + 0xffffffff, destaddr, video_width, + source_stride, source_offset); + return rc; +} + +#if 0 /* looks like this is unused. */ +/* + * Returns the physical location of the PTE associated with a given virtual address. + */ +static inline pte_t *virt_to_pte(struct mm_struct *mm, void *addr) +{ + return + pte_offset(pmd_offset + (pgd_offset(mm, (unsigned long)addr), + (unsigned long)addr), (unsigned long)addr); +} +#endif + +struct ivtvfb_user_dma_to_device ivtvfb_current_fb_dma; + +// 4MB max buffer size (on IA32 at least: 1024 pages x 4KB/page = 4MB): +#define IVTV_MAX_FB_DMA_PAGES 1024 + +int ivtvfb_alloc_user_dma_to_device(struct ivtvfb_user_dma_to_device *dma) +{ + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_alloc_user_dma_to_device\n"); + dma->page_count = 0; + dma->sglist = + kmalloc(sizeof(struct ivtv_SG_element) * IVTV_MAX_FB_DMA_PAGES, + GFP_KERNEL); + if (!dma->sglist) { + printk(KERN_ERR + "ivtvfb: cannot allocate scatter/gather list for %d pages\n", + IVTV_MAX_FB_DMA_PAGES); + return -ENOMEM; + } + + dma->map = + kmalloc(sizeof(struct page *) * IVTV_MAX_FB_DMA_PAGES, GFP_KERNEL); + if (!dma->map) { + IVTV_DEBUG_FB(IVTV_DEBUG_ERR, "can't alloc dma page array\n"); + kfree(dma->sglist); + return -ENOMEM; + } + + dma->sg_dma_handle = + pci_map_single(ivtv_fb->dev, (void *)dma->sglist, + (sizeof(struct ivtv_SG_element) * + IVTV_MAX_FB_DMA_PAGES), PCI_DMA_TODEVICE); + + return 0; +} + +//++MTY This is pretty fast - fast enough to do around 30+ frames per second at NTSC 720x480x4 or 27 frames per second at PAL 720x576x4 +int ivtvfb_prep_user_dma_to_device(struct ivtvfb_user_dma_to_device *dma, + unsigned long ivtv_dest_addr, + char *userbuf, int size_in_bytes) +{ + int i, offset; + unsigned long uaddr; + int size_in_pages = (size_in_bytes + (PAGE_SIZE - 1)) >> PAGE_SHIFT; + + IVTV_DEBUG_FB(IVTV_DEBUG_DMA, + "ivtvfb_prep_user_dma_to_device, dst: 0x%08x\n", + (unsigned int)ivtv_dest_addr); + + uaddr = ((unsigned long)userbuf & PAGE_MASK); + offset = uaddr & ~PAGE_MASK; + + down_read(¤t->mm->mmap_sem); + size_in_pages = + get_user_pages(current, current->mm, uaddr, size_in_pages, 0, 0, + dma->map, NULL); + up_read(¤t->mm->mmap_sem); + + if (size_in_pages < 0) { + IVTV_DEBUG_FB(IVTV_DEBUG_ERR, "failed to map user pages\n"); + return size_in_pages; + } + + dma->page_count = size_in_pages; + for (i = 0; i < size_in_pages; i++) { + dma->sglist[i].size = PAGE_SIZE; + dma->sglist[i].src = + pci_map_page(ivtv_fb->dev, dma->map[i], 0, offset, + PCI_DMA_TODEVICE); + dma->sglist[i].dst = ivtv_dest_addr + i * PAGE_SIZE + offset; + offset = 0; + } + + // Indicate the last element to the hardware, so we get an interrupt on completion... + dma->sglist[size_in_pages - 1].size |= 0x80000000; + +#ifdef IVTVFB_DEBUG_PER_FRAME + printk(KERN_INFO + "ivtvfb: Allocated scatter/gather list of %d bytes (%d pages) at kva 0x%08x = physaddr 0x%08x:\n", + size_in_bytes, size_in_pages, dma->sglist, dma->sg_dma_handle); + for (i = 0; i < size_in_pages; i++) { + printk(KERN_INFO + "ivtvfb: [%d] src 0x%08x -> dest 0x%08x, size 0x%08x bytes\n", + i, dma->sglist[i].src, dma->sglist[i].dst, + dma->sglist[i].size); + } +#endif + + return 0; +} + +int ivtvfb_free_user_dma_to_device(struct ivtvfb_user_dma_to_device *dma) +{ + int i; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_free_user_dma_to_device\n"); + + for (i = 0; i < dma->page_count; i++) { + pci_unmap_page(ivtv_fb->dev, dma->sglist[i].src, PAGE_SIZE, + PCI_DMA_TODEVICE); + page_cache_release(dma->map[i]); + } + kfree(dma->sglist); + kfree(dma->map); + dma->page_count = 0; + return 0; +} + +int ivtvfb_execute_user_dma_to_device(struct ivtvfb_user_dma_to_device + *dma) +{ + u32 data[IVTV_MBOX_MAX_DATA], result; + int rc; + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtvfb_execute_user_dma_to_device\n"); + + data[0] = dma->sg_dma_handle; + data[1] = dma->page_count; + data[2] = 0x1; // 0x1 = OSD data + + IVTV_DEBUG_FB(IVTV_DEBUG_DMA, + "Schedule FB DMA: physical address 0x%08x, " + "arraysize 0x%08x, type 0x%08x\n", data[0], data[1], + data[2]); + + // Enable DMA complete interrupt: + ivtv_clear_irq_mask(ivtv_fb, IVTV_IRQ_DEC_DMA_COMPLETE); + + set_bit(IVTV_F_I_OSD_DMA, &ivtv_fb->i_flags); + + rc = ivtv_api(ivtv_fb->dec_mbox, &ivtv_fb->dec_msem, + IVTV_API_DEC_DMA_FROM_HOST, &result, 3, &data[0]); + + if (rc) { + IVTV_DEBUG_FB(IVTV_DEBUG_ERR, "error sending DMA info\n"); + clear_bit(IVTV_F_I_BUSY, &ivtv_fb->i_flags); + } + + IVTV_DEBUG_FB(IVTV_DEBUG_DMA, "OK, scheduled FB DMA!"); + return 0; +} + +static inline int ivtv_fb_prep_frame(struct ivtv *itv, + unsigned long destaddr, void *srcaddr, + int count) +{ + DECLARE_WAITQUEUE(wait, current); + int rc; + + //if (!srcaddr || verify_area(...)) ... + if ((destaddr + count) > video_size) + return -E2BIG; + + rc = 0; + add_wait_queue(&ivtv_fb->dec_master_w, &wait); + do { + set_current_state(TASK_INTERRUPTIBLE); + /* FIXME mini-race still .. need to port to 'stream' format */ + if (!test_and_set_bit(IVTV_F_I_BUSY, &ivtv_fb->i_flags)) + break; + + schedule(); + + if (signal_pending(current)) + rc = -ERESTARTSYS; + } while (!rc); + set_current_state(TASK_RUNNING); + remove_wait_queue(&ivtv_fb->dec_master_w, &wait); + + if (rc) + goto out_dma_lock; + + destaddr = IVTV_DEC_MEM_START + video_rel_base + destaddr; + + if (0 != + (rc = + ivtvfb_prep_user_dma_to_device(&ivtvfb_current_fb_dma, destaddr, + (char *)srcaddr, count))) { + IVTV_DEBUG_FB(IVTV_DEBUG_DMA, + "err prep user dma to device=%x\n", rc); + goto out_dma_lock; + } + if (0 != + (rc = ivtvfb_execute_user_dma_to_device(&ivtvfb_current_fb_dma))) { + IVTV_DEBUG_FB(IVTV_DEBUG_DMA, + "err exec user dma to device=%x\n", rc); + goto out_dma_lock; + } + + return 0; + out_dma_lock: + clear_bit(IVTV_F_I_BUSY, &ivtv_fb->i_flags); + wake_up(&ivtv_fb->dec_master_w); + return rc; +} + +int ivtv_fb_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg, struct fb_info *info) +{ + + int rc; + + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "ivtv_fb_ioctl\n"); + switch (cmd) { + case IVTVFB_IOCTL_GET_STATE:{ + struct ivtvfb_ioctl_state_info state; + state.status = (ivtv_api_fb_get_state(ivtv_fb) & 0x7); + state.status |= + (ivtv_api_fb_get_flicker_state(ivtv_fb) << 3); + state.alpha = ivtv_api_fb_get_global_alpha(ivtv_fb); + IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL, + "IVTVFB_IOCTL_GET_STATE: status = %lu, alpha = %lu\n", + state.status, state.alpha); + if (copy_to_user((void *)arg, &state, sizeof(state))) + return -EFAULT; + return 0; + } + case IVTVFB_IOCTL_SET_STATE:{ + struct ivtvfb_ioctl_state_info state; + if (copy_from_user(&state, (void *)arg, sizeof(state))) + return -EFAULT; + IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL, + "IVTVFB_IOCTL_SET_STATE: status = %lu, alpha = %lu\n", + state.status, state.alpha); + ivtv_api_fb_set_state(ivtv_fb, + (state.status + && IVTVFB_STATUS_ENABLED)); + ivtv_api_fb_set_global_alpha(ivtv_fb, + (state. + status & + IVTVFB_STATUS_GLOBAL_ALPHA) + ? 1 : 0, state.alpha, + (state. + status & + IVTVFB_STATUS_LOCAL_ALPHA) + ? 1 : 0); + ivtv_api_fb_set_flicker_state(ivtv_fb, + (state. + status & + IVTVFB_STATUS_FLICKER_REDUCTION) + ? 1 : 0); + IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL, "new state = %d\n", + ivtv_api_fb_get_state(ivtv_fb)); + IVTV_DEBUG_FB(IVTV_DEBUG_IOCTL, + "global alpha now = %d\n", + ivtv_api_fb_get_global_alpha(ivtv_fb)); + return 0; + } + case IVTVFB_IOCTL_PREP_FRAME:{ + struct ivtvfb_ioctl_dma_host_to_ivtv_args args; + if (copy_from_user(&args, (void *)arg, sizeof(args))) + return -EFAULT; + return ivtv_fb_prep_frame(ivtv_fb, args.dest_offset, + args.source, args.count); + } + case IVTVFB_IOCTL_BLT_COPY:{ + struct ivtvfb_ioctl_blt_copy_args args; + if (copy_from_user(&args, (void *)arg, sizeof(args))) + return -EFAULT; + + return ivtv_fb_blt_copy(ivtv_fb, args.x, args.y, + args.width, args.height, + args.source_stride, + args.source_offset); + } + case IVTVFB_IOCTL_GET_ACTIVE_BUFFER:{ + struct ivtv_osd_coords bufinfo; + rc = ivtv_api_fb_get_osd_coords(ivtv_fb, &bufinfo); + return copy_to_user((void *)arg, &bufinfo, + sizeof(bufinfo)); + } + case IVTVFB_IOCTL_SET_ACTIVE_BUFFER:{ + struct ivtv_osd_coords bufinfo; + if (copy_from_user + (&bufinfo, (void *)arg, sizeof(bufinfo))) + return -EFAULT; + return ivtv_api_fb_set_osd_coords(ivtv_fb, &bufinfo); + } + case IVTVFB_IOCTL_GET_FRAME_BUFFER:{ + struct ivtvfb_ioctl_get_frame_buffer getfb; + getfb.mem = (void *)video_vbase; + getfb.bytes = video_size; + getfb.sizex = video_width; + getfb.sizey = video_height; + + return copy_to_user((void *)arg, &getfb, sizeof(getfb)); + } + default: + return -EINVAL; + } + return 0; +} + +static struct fb_ops ivtvfb_ops = { + owner:THIS_MODULE, + fb_check_var:ivtvfb_check_var, + fb_set_par:ivtvfb_set_par, + fb_setcolreg:ivtvfb_setcolreg, + fb_ioctl:ivtv_fb_ioctl, + fb_pan_display:NULL, +}; + +int __init ivtvfb_init(void) +{ + int rc; + u32 fbbase; + u32 fblength; + struct ivtv_osd_coords osd; + struct rectangle rect; + + if ((ivtv_fb_card_id < 0) || (ivtv_fb_card_id >= ivtv_cards_active)) { + printk(KERN_ERR + "Error! ivtv-fb: ivtv_fb_card_id parameter is out of range (valid range: 0-%d)\n", + ivtv_cards_active - 1); + return -1; + } + + ivtv_fb = &ivtv_cards[ivtv_fb_card_id]; + if (!ivtv_fb || (ivtv_fb->card_type != IVTV_350_V1)) { + printk(KERN_ERR + "Error! ivtv-fb: Specified card (id %d) is either not present or does not support TV out (PVR350 only)\n", + ivtv_fb_card_id); + return -1; + } + + printk(KERN_INFO + "ivtv-fb: Framebuffer module loaded (attached to ivtv card id %d)\n", + ivtv_fb_card_id); + + rc = ivtv_api_fb_set_pixel_format(ivtv_fb, 4); // 4 = AlphaRGB 8:8:8:8 + + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "Current pixel format = %d\n", + ivtv_api_fb_get_pixel_format(ivtv_fb)); + + rc = ivtv_api_fb_get_framebuffer(ivtv_fb, (void **)&fbbase, &fblength); + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, + "Framebuffer is at decoder-relative address 0x%08x and has %d bytes.\n", + fbbase, fblength); + + rc = ivtv_api_fb_get_osd_coords(ivtv_fb, &osd); + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, + "OSD: offset = 0x%08x (max offset = 0x%08x), pixel_stride = %d, lines = %d, x = %d, y = %d\n", + (u32) osd.offset, (u32) osd.max_offset, osd.pixel_stride, + osd.lines, osd.x, osd.y); + + /* setup OSD and screen for PAL */ + if (ivtv_pal) { + osd.lines = 576; + rc = ivtv_api_fb_set_osd_coords(ivtv_fb, &osd); + if (rc) + IVTV_DEBUG_FB(IVTV_DEBUG_ERR, + "failed setting PAL osd\n"); + + rect.x0 = 0; + rect.x1 = 720; + rect.y0 = 0; + rect.y1 = 576; + rc = ivtv_api_fb_set_screen_coords(ivtv_fb, &rect); + if (rc) + IVTV_DEBUG_FB(IVTV_DEBUG_ERR, + "failed setting PAL screen\n"); + } + + rc = ivtv_api_fb_get_screen_coords(ivtv_fb, &rect); + printk(KERN_INFO "ivtv-fb: screen coords: [%d %d] -> [%d %d]\n", + rect.x0, rect.y0, rect.x1, rect.y1); + + printk(KERN_INFO "ivtv-fb: original global alpha = %d\n", + ivtv_api_fb_get_global_alpha(ivtv_fb)); + + /* + * Normally a 32-bit RGBA framebuffer would be fine, however XFree86's fbdev + * driver doesn't understand the concept of alpha channel and always sets + * bits 24-31 to zero when using a 24bpp-on-32bpp framebuffer device. We fix + * this behavior by enabling the iTVC15's global alpha feature, which causes + * the chip to ignore the per-pixel alpha data and instead use one value (e.g., + * full brightness = 255) for the entire framebuffer. The local alpha is also + * disabled in this step. + * + *++MTY Need to update http://ivtv.sourceforge.net/ivtv/firmware-api.html + * call 0x4b: param[2] says 1 = enable local alpha, when in reality + * it means *disable* local alpha... + * + */ + ivtv_api_fb_set_global_alpha(ivtv_fb, 1, 255, 0); + printk(KERN_INFO "ivtv-fb: new global alpha = %d\n", + ivtv_api_fb_get_global_alpha(ivtv_fb)); + + rc = ivtv_api_fb_set_state(ivtv_fb, 1); // 1 = enabled + printk(KERN_INFO "ivtv-fb: current OSD state = %d\n", + ivtv_api_fb_get_state(ivtv_fb)); + + video_rel_base = fbbase; + video_base = ivtv_fb->base_addr + IVTV_DEC_MEM_START + video_rel_base; + video_width = rect.x1 - rect.x0; + video_height = rect.y1 - rect.y0; + video_linelength = 4 * osd.pixel_stride; + video_size = fblength; + + shadow_framebuf_size = (video_width * video_height * 4); + shadow_framebuf_offset = (video_size - shadow_framebuf_size) & ~3; + + if (!request_mem_region(video_base, video_size, "ivtvfb")) { + printk(KERN_WARNING + "ivtv-fb: warning: cannot reserve video memory at 0x%lx\n", + video_base); + /* We cannot make this fatal. Sometimes this comes from magic spaces our resource handlers simply don't know about */ + } + + video_vbase = ioremap(video_base, video_size); + if (!video_vbase) { + release_mem_region(video_base, video_size); + printk(KERN_ERR + "ivtv-fb: abort, cannot ioremap video memory 0x%x @ 0x%lx\n", + video_size, video_base); + return -EIO; + } + + printk(KERN_INFO + "ivtv-fb: framebuffer at 0x%lx, mapped to 0x%p, size %dk\n", + video_base, video_vbase, video_size / 1024); + printk(KERN_INFO "ivtv-fb: mode is %dx%dx%d, linelength=%d\n", + video_width, video_height, 32, video_linelength); + + ivtvfb_defined.xres = video_width; + ivtvfb_defined.yres = video_height; + ivtvfb_defined.xres_virtual = video_width; + ivtvfb_defined.yres_virtual = video_height; + ivtvfb_defined.bits_per_pixel = 32; + video_height_virtual = ivtvfb_defined.yres_virtual; + + /* some dummy values for timing to make fbset happy */ + ivtvfb_defined.pixclock = 10000000 / video_width * 1000 / video_height; + ivtvfb_defined.left_margin = (video_width / 8) & 0xf8; + ivtvfb_defined.right_margin = 32; + ivtvfb_defined.upper_margin = 16; + ivtvfb_defined.lower_margin = 4; + ivtvfb_defined.hsync_len = (video_width / 8) & 0xf8; + ivtvfb_defined.vsync_len = 4; + + ivtvfb_defined.red.offset = 0; + ivtvfb_defined.red.length = 8; + ivtvfb_defined.green.offset = 8; + ivtvfb_defined.green.length = 8; + ivtvfb_defined.blue.offset = 16; + ivtvfb_defined.blue.length = 8; + ivtvfb_defined.transp.offset = 24; + ivtvfb_defined.transp.length = 8; + +#ifdef CONFIG_MTRR + if (mtrr) { + /* Find the largest power of two that maps the whole buffer */ + int size_shift = 31; + while (!(video_size & (1 << size_shift))) { + size_shift--; + } + size_shift++; + + fb_start_aligned_physaddr = + video_base & ~((1 << size_shift) - 1); + fb_end_aligned_physaddr = + (video_base + (1 << size_shift) - 1) & ~((1 << size_shift) - + 1); + if (mtrr_add + (fb_start_aligned_physaddr, + (fb_end_aligned_physaddr - fb_start_aligned_physaddr), + MTRR_TYPE_WRCOMB, 1) < 0) { + printk(KERN_WARNING + "ivtv-fb: warning: mtrr_add() failed to add write combining region 0x%08x-0x%08x\n", + (unsigned int)fb_start_aligned_physaddr, + (unsigned int)fb_end_aligned_physaddr); + } + } +#endif + + fb_info.node = -1; + fb_info.flags = FBINFO_FLAG_DEFAULT; + fb_info.fbops = &ivtvfb_ops; + + struct fb_fix_screeninfo fix; + _ivtvfb_get_fix(&fix); + fb_info.var = ivtvfb_defined; + fb_info.fix = fix; + fb_info.screen_base = video_vbase; + fb_info.fbops = &ivtvfb_ops; + fb_alloc_cmap(&fb_info.cmap, 0, 0); + + if (register_framebuffer(&fb_info) < 0) + return -EINVAL; + + ivtv_fb->fb_id = fb_info.node; + + printk(KERN_INFO "fb%d: %s frame buffer device\n", + ivtv_fb->fb_id, fix.id); + + /* Set up DMA and BLT copy structures */ + ivtvfb_alloc_user_dma_to_device(&ivtvfb_current_fb_dma); + ivtv_fb->user_dma_to_device_state = &ivtvfb_current_fb_dma; + return 0; +} + +static void ivtvfb_cleanup(void) +{ + IVTV_DEBUG_FB(IVTV_DEBUG_INFO, "Unloading framebuffer module\n"); + unregister_framebuffer(&fb_info); + iounmap(video_vbase); +#ifdef CONFIG_MTRR + mtrr_del(-1, fb_start_aligned_physaddr, + (fb_end_aligned_physaddr - fb_start_aligned_physaddr)); +#endif + ivtv_fb->user_dma_to_device_state = NULL; + ivtvfb_free_user_dma_to_device(&ivtvfb_current_fb_dma); + ivtv_fb->fb_id = -1; + //release_mem_region(video_base, video_size); +} + +module_init(ivtvfb_init); +module_exit(ivtvfb_cleanup); diff -upN /dev/null current/drivers/media/video/ivtv-i2c.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/ivtv-i2c.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,221 @@ +#include "ivtv.h" + +/* i2c implementation for iTVC15 chip, ivtv project. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.sourceforge.net/projects/ivtv/ + */ + +/* moved here from ivtv.h */ +static int writeregs(struct i2c_client *client, const unsigned char *regs); +static int attach_inform(struct i2c_client *client); +static int detach_inform(struct i2c_client *client); + +int writereg(struct i2c_client *client, unsigned char reg, unsigned char data) +{ + int ret; + unsigned char msg[] = { 0x1f, 0x00 }; + + printk("<1>writing reg 0x%02x, data 0x%02x\n", reg, data); + + msg[0] = reg; + msg[1] = data; + ret = i2c_master_send(client, msg, 2); + if (ret != 2) + printk("writereg error\n"); + return ret; +} + +static int writeregs(struct i2c_client *client, const unsigned char *regs) +{ + unsigned char reg, data; + + while (*regs != 0x00) { + reg = *(regs++); + data = *(regs++); + if (writereg(client, reg, data) < 0) + return -1; + } + return 0; +} + +static struct i2c_adapter ivtv_i2c_adapter_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_BT848, /*algo-bit is OR'd with this */ + .algo = NULL, /*set by i2c-algo-bit */ + .algo_data = NULL, /*filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, +/* i2c-2.8.0 and later */ + .owner = THIS_MODULE, + .class = I2C_ADAP_CLASS_TV_ANALOG, +}; + +static struct i2c_algo_bit_data ivtv_i2c_algo_template = { + NULL, /*?? */ + ivtv_setsda, /*setsda function */ + ivtv_setscl, /*" */ + ivtv_getsda, /*" */ + ivtv_getscl, /*" */ + 5, /*udelay or mdelay */ + 5, /*whatever above isn't */ + 200 /*timeout */ +}; + +void ivtv_setscl(void *data, int state) +{ + struct ivtv *itv = (struct ivtv *)data; + + if (state) + itv->i2c_state |= 0x01; + else + itv->i2c_state &= ~0x01; + + /* write them out */ + /* write bits are inverted */ + writel(~itv->i2c_state, (itv->reg_mem + IVTV_REG_I2C_SETSCL_OFFSET)); +} + +void ivtv_setsda(void *data, int state) +{ + struct ivtv *itv = (struct ivtv *)data; + + if (state) + itv->i2c_state |= 0x01; + else + itv->i2c_state &= ~0x01; + + /* write them out */ + /* write bits are inverted */ + writel(~itv->i2c_state, (itv->reg_mem + IVTV_REG_I2C_SETSDA_OFFSET)); +} + +int ivtv_getscl(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + return readb(itv->reg_mem + IVTV_REG_I2C_GETSCL_OFFSET); +} + +int ivtv_getsda(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + return readb(itv->reg_mem + IVTV_REG_I2C_GETSDA_OFFSET); +} + +static struct i2c_client ivtv_i2c_client_template = { + .name = "ivtv internal use only", + .id = -1, +}; + +static int attach_inform(struct i2c_client *client) +{ + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + int i; + + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c client attach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (itv->i2c_clients[i] == NULL) { + itv->i2c_clients[i] = client; + break; + } + } + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c attach [client=%s,%s]\n", + client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + int i; + + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c client detach\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (itv->i2c_clients[i] == client) { + itv->i2c_clients[i] = NULL; + break; + } + } + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c detach [client=%s,%s]\n", + client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + + return 0; +} + +void ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, + void *arg) +{ + int i; + + IVTV_DEBUG(IVTV_DEBUG_I2C, "call_i2c_client\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (NULL == itv->i2c_clients[i]) + continue; + if (NULL == itv->i2c_clients[i]->driver->command) + continue; + if (addr == itv->i2c_clients[i]->addr) { + itv->i2c_clients[i]->driver->command(itv-> + i2c_clients[i], + cmd, arg); + return; + } + } + IVTV_DEBUG(IVTV_DEBUG_ERR, "i2c client addr: 0x%02x not found!\n", + addr); +} + +int ivtv_i2c_direct(struct ivtv *itv, int addr, const unsigned char *regs) +{ + int i, ret = 0; + + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c_direct\n"); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + if (NULL == itv->i2c_clients[i]) + continue; + if (addr == itv->i2c_clients[i]->addr) { + ret = writeregs(itv->i2c_clients[i], regs); + break; + } + } + + if (ret) { + IVTV_DEBUG(IVTV_DEBUG_ERR, "error %d writing reg\n", ret); + return -EIO; + } + + return 0; +} + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c init\n"); + memcpy(&itv->i2c_adap, &ivtv_i2c_adapter_template, + sizeof(struct i2c_adapter)); + memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, + sizeof(struct i2c_algo_bit_data)); + memcpy(&itv->i2c_client, &ivtv_i2c_client_template, + sizeof(struct i2c_client)); + + sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), + " #%d", itv->num); + itv->i2c_algo.data = itv; + i2c_set_adapdata(&itv->i2c_adap, itv); + itv->i2c_adap.algo_data = &itv->i2c_algo; + itv->i2c_client.adapter = &itv->i2c_adap; + + IVTV_DEBUG(IVTV_DEBUG_I2C, "setting scl and sda to 1\n"); + ivtv_setscl(itv, 1); + ivtv_setsda(itv, 1); + + itv->i2c_rc = i2c_bit_add_bus(&itv->i2c_adap); + return itv->i2c_rc; +} + +void __devexit exit_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG(IVTV_DEBUG_I2C, "i2c exit\n"); + + i2c_bit_del_bus(&itv->i2c_adap); +} diff -upN /dev/null current/drivers/media/video/ivtv.h --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/ivtv.h Thu Jun 17 12:58:30 2004 @@ -0,0 +1,846 @@ +#ifndef IVTV_H +#define IVTV_H + +/* Header for ivtv project: + * Driver for the iTVC15 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.sourceforge.net/projects/ivtv/ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "msp3400.h" +/* If you don't want to patch to v4l2, grab a copy of + * videodev2.h and put it in the same dir as this file */ +#ifndef HAVE_V4L2 + #define HAVE_V4L2 1 + #include "videodev2.h" +#endif +#include +#include +#include + +#define IVTV_ENCODER_OFFSET 0x00000000 +#define IVTV_ENCODER_SIZE 0x01000000 + +#define IVTV_DECODER_OFFSET 0x01000000 +#define IVTV_DECODER_SIZE 0x01000000 + +#define IVTV_ENCDEC_SIZE (IVTV_ENCODER_SIZE + IVTV_DECODER_SIZE) + +#define IVTV_REG_OFFSET 0x02000000 +#define IVTV_REG_SIZE 0x00010000 + +#define IVTV_IOREMAP_SIZE (IVTV_ENCDEC_SIZE + IVTV_REG_SIZE) + +#define IVTV_IOREMAP_ERROR "ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h " \ + "or disabling CONFIG_HIMEM4G into the kernel would help" + +/* General */ +#define IVTV_DRIVER_NAME "ivtv" +#define IVTV_DRIVER_VERSION_MAJOR 0 +#define IVTV_DRIVER_VERSION_MINOR 1 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 9 +#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) +#define IVTV_MAX_CARDS 9 +#define IVTV_DEFAULT_NUM_CARDS 1 +#define IVTV_MAX_YUV_BUFFERS 500 +#define IVTV_MIN_YUV_BUFFERS 40 +#define IVTV_DEFAULT_YUV_BUFFERS 60 +#define IVTV_MAX_MPG_BUFFERS 100 +#define IVTV_MIN_MPG_BUFFERS 15 +#define IVTV_DEFAULT_MPG_BUFFERS IVTV_MAX_MPG_BUFFERS +#define IVTV_MAX_DEC_YUV_BUFFERS 500 +#define IVTV_MIN_DEC_YUV_BUFFERS 17 +#define IVTV_DEFAULT_DEC_YUV_BUFFERS 0 +#define IVTV_MAX_DEC_MPG_BUFFERS 100 +#define IVTV_MIN_DEC_MPG_BUFFERS 5 +#define IVTV_DEFAULT_DEC_MPG_BUFFERS 8 +#define IVTV_MAX_VBI_BUFFERS 100 +#define IVTV_MIN_VBI_BUFFERS 3 +#define IVTV_DEFAULT_VBI_BUFFERS 10 +#define IVTV_MIN_DEC_MPG_QLEN 0 +#define IVTV_DEFAULT_DEC_MPG_QLEN 2 +#define IVTV_MIN_DEC_YUV_QLEN 0 +#define IVTV_DEFAULT_DEC_YUV_QLEN 0 +#define IVTV_IOCTL_SET_DEBUG_LEVEL _IOWR('@', 98, int *) +#define IVTV_IOCTL_GET_DEBUG_LEVEL _IOR('@', 99, int *) + +#define IVTV_PCI_ID_250_V1 0x4001 /* subsystem id */ +#define IVTV_PCI_ID_250_V2 0x4009 +#define IVTV_PCI_ID_250_V3 0x4801 /* treat like 250_V1 */ +#define IVTV_PCI_ID_250_V4 0x4803 /* treat like 250_V2 */ +#define IVTV_PCI_ID_350_V1 0x4000 +#define IVTV_PCI_ID_350_V2 0x4800 /* treat like 350_V1 */ +#define IVTV_250_V1 0 /* wintv pvr 250, encoder and decoder */ +#define IVTV_250_V2 1 /* pvr 250, encoder only */ +#define IVTV_350_V1 2 /* encoder, decoder, tv-out */ +#define IVTV_250_V1_STREAMS 3 +#define IVTV_250_V2_STREAMS 3 +#define IVTV_350_V1_STREAMS 5 +#define IVTV_V4L2_DEC_OFFSET 16 /* offset from 0 to register dec. v4l2 minors on */ +#define IVTV_V4L2_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ + +#define IVTV_ENC_STREAM_TYPE_MPG 0 +#define IVTV_ENC_STREAM_TYPE_YUV 1 +#define IVTV_ENC_STREAM_TYPE_VBI 2 +#define IVTV_DEC_STREAM_TYPE_MPG 3 +#define IVTV_DEC_STREAM_TYPE_YUV 4 + +#define IVTV_ENC_MEM_START 0x00000000 +#define IVTV_DEC_MEM_START 0x01000000 +#define PCI_VENDOR_ID_ICOMP 0x4444 +#define PCI_DEVICE_ID_IVTV15 0x0803 +#define PCI_DEVICE_ID_IVTV16 0x0016 +#define IVTV_DEVNAME "ivtv: iTVC15/16 mpg2 encoder chip" +#define IVTV_MBOX_MAX_BOXES 20 +#define IVTV_MBOX_API_BOXES 6 +#define IVTV_MBOX_DMA_START 6 +#define IVTV_MBOX_DMA_END 8 +#define IVTV_MBOX_MAX_DATA 16 +#define IVTV_MBOX_DMA 9 +#define IVTV_MBOX_FIELD_DISPLAYED 8 +#define IVTV_MBOX_SIZE 80 +#define IVTV_SAA7115_I2C_ADDR 0x21 +#define IVTV_TUNER_I2C_ADDR 0x61 +#define IVTV_MSP3400_I2C_ADDR 0x40 +#define IVTV_DMA_BUF_SIZE 34560 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */ +#define IVTV_DMA_DEC_BUF_SIZE 32768 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */ +//#define IVTV_DMA_BUF_SIZE 65536 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */ +//#define IVTV_DMA_DEC_BUF_SIZE 65536 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */ + +#define IVTV_DMA_MAX_XFER 0x00080000 /* 0x8000 = 32kbytes, 0x20000 = 128kbytes */ +#define IVTV_DEC_MIN_BUF 0x00050000 /* want this many bytes+ in decoder buffer */ +#define IVTV_SLEEP_WAIT (HZ/10) /*100 ms*/ +#define IVTV_MAX_DATA_SLEEP 30 +#define DEC_DMA_TIMEOUT (15*HZ/100) /* used to be 100/15 */ + +#define IVTV_DMA_ERR_LIST 0x00000008 +#define IVTV_DMA_ERR_WRITE 0x00000004 +#define IVTV_DMA_ERR_READ 0x00000002 +#define IVTV_DMA_SUCCESS 0x00000001 +#define IVTV_DMA_READ_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_READ) +#define IVTV_DMA_WRITE_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE) +#define IVTV_DMA_ERR (IVTV_DMA_ERR_LIST | IVTV_DMA_ERR_WRITE | IVTV_DMA_ERR_READ) + +/* video related */ +#define IVTV_MAX_INPUTS 9 + +/*ioctl's*/ +#define IVTV_CTL_PRINTBOXES 0x00000001 +#define IVTV_CTL_CLEANUP 0x00000002 +#define IVTV_CTL_INIT_VIDCAP 0x00000003 + +/* Registers */ +#define IVTV_REG_DMASTATUS (0x0004 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_IRQSTATUS (0x0040 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_IRQMASK (0x0048 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_VDM (0x2800 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_AO (0x2D00 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_BYTEFLUSH (0x2D24 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_SPU (0x9050 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_HW_BLOCKS (0x9054 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_VPU (0x9058 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_APU (0xA064 /*| IVTV_REG_OFFSET*/) + +/* IRQ Masks */ +#define IVTV_IRQ_MASK_DEFAULT 0x00000404 /*stuff to ignore*/ +#define IVTV_IRQ_MASK_CAPTURE 0xFC000400 /*inverse mask, we want the high bits!*/ +#define IVTV_IRQ_MASK_DECODE 0x00FC0400 + +#define IVTV_IRQ_ENC_START_CAP (0x1 << 31) +#define IVTV_IRQ_ENC_EOS (0x1 << 30) +#define IVTV_IRQ_ENC_VBI_CAP (0x1 << 29) +#define IVTV_IRQ_ENC_VIM_RST (0x1 << 28) +#define IVTV_IRQ_ENC_DMA_COMPLETE (0x1 << 27) + +#define IVTV_IRQ_DEC_COPY_PROTECT (0x1 << 25) +#define IVTV_IRQ_DEC_AUD_MODE_CHG (0x1 << 24) +#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) +#define IVTV_IRQ_DEC_IFRAME_DONE (0x1 << 21) +#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) +#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19) +#define IVTV_IRQ_DEC_DMA_ERR (0x1 << 18) +#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) + +#define IVTV_IRQ_DEBUG_KLUGE ( IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_ENC_VIM_RST | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DEC_COPY_PROTECT | IVTV_IRQ_DEC_AUD_MODE_CHG | IVTV_IRQ_DEC_DATA_REQ | IVTV_IRQ_DEC_IFRAME_DONE | IVTV_IRQ_DEC_DMA_COMPLETE | IVTV_IRQ_DEC_VBI_RE_INSERT | IVTV_IRQ_DEC_DMA_ERR ) + +/* commands */ +#define IVTV_MASK_SPU_ENABLE 0xFFFFFFFE +#define IVTV_MASK_VPU_ENABLE15 0xFFFFFFF6 +#define IVTV_MASK_VPU_ENABLE16 0xFFFFFFFB +#define IVTV_CMD_VDM_STOP 0x00000000 +#define IVTV_CMD_AO_STOP 0x00000005 +#define IVTV_CMD_APU_PING 0x00000000 +#define IVTV_CMD_VPU_STOP15 0xFFFFFFFE +#define IVTV_CMD_VPU_STOP16 0xFFFFFFEE +#define IVTV_CMD_HW_BLOCKS_RST 0xFFFFFFFF +#define IVTV_CMD_SPU_STOP 0x00000001 +#define IVTV_CMD_SDRAM_PRECHARGE_INIT 0x0000001A +#define IVTV_CMD_SDRAM_REFRESH_INIT 0x80000640 +#define IVTV_SDRAM_SLEEPTIME (60 * HZ / 100) /* 600ms */ + +/*Used for locating the firmware mailboxes*/ +#define IVTV_FIRM_ENC_FILENAME "/lib/modules/ivtv-fw-enc.bin" +#define IVTV_FIRM_DEC_FILENAME "/lib/modules/ivtv-fw-dec.bin" +#define IVTV_FIRM_IMAGE_SIZE 256*1024 +#define IVTV_FIRM_SEARCH_ENCODER_START IVTV_ENCODER_OFFSET +#define IVTV_FIRM_SEARCH_DECODER_START IVTV_DECODER_OFFSET +#define IVTV_FIRM_SEARCH_ENCODER_END (IVTV_ENCODER_OFFSET + IVTV_ENCODER_SIZE - 1) +#define IVTV_FIRM_SEARCH_DECODER_END (IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE - 1) +#define IVTV_FIRM_SEARCH_STEP 0x00000100 + +/* Firmware mailbox flags*/ +#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 +#define IVTV_MBOX_DRIVER_DONE 0x00000002 +#define IVTV_MBOX_IN_USE 0x00000001 +#define IVTV_MBOX_FREE 0x00000000 + +/*Firmware API commands*/ +#define IVTV_API_ENC_GETVER 0x000000C4 +#define IVTV_API_DEC_GETVER 0x00000011 +#define IVTV_API_ENC_HALT_FW 0x000000C3 +#define IVTV_API_DEC_HALT_FW 0x0000000E +#define IVTV_API_DEC_START_PLAYBACK 0x00000001 +#define IVTV_API_DEC_STOP_PLAYBACK 0x00000002 +#define IVTV_API_DEC_PLAYBACK_SPEED 0x00000003 +#define IVTV_API_DEC_STEP_VIDEO 0x00000005 +#define IVTV_API_DEC_PAUSE_PLAYBACK 0x0000000d +#define IVTV_API_DEC_DMA_BLOCKSIZE 0x00000008 +#define IVTV_API_DEC_DMA_FROM_HOST 0x00000000b +#define IVTV_API_DEC_DISP_STANDARD 0x00000010 +#define IVTV_API_DEC_STREAM_INPUT 0x00000014 +#define IVTV_API_DEC_TIMING_INFO 0x00000015 +#define IVTV_API_DEC_SELECT_AUDIO 0x00000016 +#define IVTV_API_DEC_EVENT_NOTIFICATION 0x00000017 +#define IVTV_API_DEC_DISPLAY_BUFFERS 0x00000018 +#define IVTV_API_DEC_DECODE_SOURCE 0x0000001a +#define IVTV_API_DEC_AUDIO_OUTPUT 0x0000001b +#define IVTV_API_DEC_SET_AV_DELAY 0x0000001c +#define IVTV_API_DEC_BUFFER 0x0000001e +#define IVTV_API_DEC_DMA_STATUS 0x0000000a +#define IVTV_API_DEC_XFER_INFO 0x00000009 +#define IVTV_API_STD_TIMEOUT 0x00010000 /*units??*/ +#define IVTV_API_ASSIGN_DMA_BLOCKLEN 0x000000c9 +#define IVTV_API_ASSIGN_PGM_INDEX_INFO 0x000000c7 +#define IVTV_API_ASSIGN_STREAM_TYPE 0x000000b9 +#define IVTV_API_ASSIGN_OUTPUT_PORT 0x000000bb +#define IVTV_API_ASSIGN_FRAMERATE 0x0000008f +#define IVTV_API_ASSIGN_FRAME_SIZE 0x00000091 +#define IVTV_API_ASSIGN_ASPECT_RATIO 0x00000099 +#define IVTV_API_ASSIGN_BITRATES 0x00000095 +#define IVTV_API_ASSIGN_GOP_PROPERTIES 0x00000097 +#define IVTV_API_ASSIGN_3_2_PULLDOWN 0x000000b1 +#define IVTV_API_ASSIGN_GOP_CLOSURE 0x000000c5 +#define IVTV_API_ASSIGN_AUDIO_PROPERTIES 0x000000bd +#define IVTV_API_ASSIGN_DNR_FILTER_MODE 0x0000009b +#define IVTV_API_ASSIGN_DNR_FILTER_PROPS 0x0000009d +#define IVTV_API_ASSIGN_CORING_LEVELS 0x0000009f +#define IVTV_API_ASSIGN_SPATIAL_FILTER_TYPE 0x000000a1 +#define IVTV_API_ASSIGN_FRAME_DROP_RATE 0x000000d0 +#define IVTV_API_ASSIGN_PLACEHOLDER 0x000000d8 +#define IVTV_API_INITIALIZE_INPUT 0x000000cd +#define IVTV_API_ASSIGN_NUM_VSYNC_LINES 0x000000d6 +#define IVTV_API_BEGIN_CAPTURE 0x00000081 +#define IVTV_API_PAUSE_ENCODER 0x000000d2 +#define IVTV_API_EVENT_NOTIFICATION 0x000000d5 +#define IVTV_API_END_CAPTURE 0x00000082 +#define IVTV_API_SCHED_DMA_TO_HOST 0x000000cc +#define IVTV_API_FB_GET_FRAMEBUFFER 0x00000041 +#define IVTV_API_FB_GET_PIXEL_FORMAT 0x00000042 +#define IVTV_API_FB_SET_PIXEL_FORMAT 0x00000043 +#define IVTV_API_FB_GET_STATE 0x00000044 +#define IVTV_API_FB_SET_STATE 0x00000045 +#define IVTV_API_FB_GET_OSD_COORDS 0x00000046 +#define IVTV_API_FB_SET_OSD_COORDS 0x00000047 +#define IVTV_API_FB_GET_SCREEN_COORDS 0x00000048 +#define IVTV_API_FB_SET_SCREEN_COORDS 0x00000049 +#define IVTV_API_FB_GET_GLOBAL_ALPHA 0x0000004a +#define IVTV_API_FB_SET_GLOBAL_ALPHA 0x0000004b +#define IVTV_API_FB_SET_BLEND_COORDS 0x0000004c +// 0x4d unknown +// 0x4e unknown +#define IVTV_API_FB_GET_FLICKER_STATE 0x0000004f +#define IVTV_API_FB_SET_FLICKER_STATE 0x00000050 +// 0x51 unknown +#define IVTV_API_FB_BLT_COPY 0x00000052 +#define IVTV_API_FB_BLT_FILL 0x00000053 +#define IVTV_API_FB_BLT_TEXT 0x00000054 +// 0x55 unknown +#define IVTV_API_FB_SET_FRAMEBUFFER_WINDOW 0x00000056 +// 0x57 - 0x5f unknown +#define IVTV_API_FB_SET_CHROMA_KEY 0x00000060 +#define IVTV_API_FB_GET_ALPHA_CONTENT_INDEX 0x00000061 +#define IVTV_API_FB_SET_ALPHA_CONTENT_INDEX 0x00000062 + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 +#define I2C_TIMING (0x7<<4) +#define IVTV_REG_I2C_SETSCL_OFFSET (0x7000 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_I2C_SETSDA_OFFSET (0x7004 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_I2C_GETSCL_OFFSET (0x7008 /*| IVTV_REG_OFFSET*/) +#define IVTV_REG_I2C_GETSDA_OFFSET (0x700c /*| IVTV_REG_OFFSET*/) + +/* debugging */ +#define IVTV_DEBUG_ERR (1 << 0) +#define IVTV_DEBUG_INFO (1 << 1) +#define IVTV_DEBUG_API (1 << 2) +#define IVTV_DEBUG_DMA (1 << 3) +#define IVTV_DEBUG_IOCTL (1 << 4) +#define IVTV_DEBUG_I2C (1 << 5) +#define IVTV_DEBUG_IRQ (1 << 6) +#define IVTV_DEBUG(x,args...) if((x)&ivtv_debug) printk("ivtv: " args); +#define IVTV_DEBUG_FB(x,args...) if((x)&ivtv_debug) printk("ivtv-fb: " args); + +/* Temp saa7115 hack FIXME */ +#define DECODER_SET_SIZE 76598 +#define DECODER_GET_PICTURE 76599 + +/* Values for IVTV_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */ +#define MPEG_FRAME_TYPE_IFRAME 1 +#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3 +#define MPEG_FRAME_TYPE_ALL 7 + +/* External API stuff */ +#define IVTV_IOC_FWAPI 0xFFEE7701 /*just some values i picked for now*/ +#define IVTV_IOC_ZCOUNT 0xFFEE7702 +#define IVTV_IOC_G_CODEC 0xFFEE7703 +#define IVTV_IOC_S_CODEC 0xFFEE7704 + +/* allow direct access to the saa7115 registers for testing */ +#define SAA7115_GET_REG 0xFFEE7705 +#define SAA7115_SET_REG 0xFFEE7706 + + +#define DECODER_SET_AUDIO 0xFFEE7707 +#define DECODER_AUDIO_32_KHZ 0 +#define DECODER_AUDIO_441_KHZ 1 +#define DECODER_AUDIO_48_KHZ 2 + +#define IVTV_IOC_PLAY 0xFFEE7781 +#define IVTV_IOC_PAUSE 0xFFEE7782 +#define IVTV_IOC_FRAMESYNC 0xFFEE7783 +#define IVTV_IOC_GET_TIMING 0xFFEE7784 +#define IVTV_IOC_S_SLOW_FAST 0xFFEE7785 +#define IVTV_IOC_S_START_DECODE 0xFFEE7786 +#define IVTV_IOC_S_STOP_DECODE 0xFFEE7787 +#define IVTV_IOC_S_OSD 0xFFEE7788 +#define IVTV_IOC_GET_FB 0xFFEE7789 + +#define IVTV_IOC_START_DECODE _IOW('@', 29, struct ivtv_cfg_start_decode) +#define IVTV_IOC_STOP_DECODE _IOW('@', 30, struct ivtv_cfg_stop_decode) +#define IVTV_IOC_G_SPEED _IOR('@', 31, struct ivtv_speed) +#define IVTV_IOC_S_SPEED _IOW('@', 32, struct ivtv_speed) +#define IVTV_IOC_DEC_STEP _IOW('@', 33, int) +#define IVTV_IOC_DEC_FLUSH _IOW('@', 34, int) + +/* Framebuffer external API */ +/* NOTE: These must *exactly* match the structures and constants in driver/ivtv.h */ + +struct ivtvfb_ioctl_state_info { + unsigned long status; + unsigned long alpha; +}; + +struct ivtvfb_ioctl_blt_copy_args { + int x, y, width, height, source_offset, source_stride; +}; + +struct ivtvfb_ioctl_dma_host_to_ivtv_args { + void* source; + unsigned long dest_offset; + int count; +}; + +struct ivtvfb_ioctl_get_frame_buffer { + void* mem; + int bytes; + int sizex; + int sizey; +}; + +struct ivtv_osd_coords { + unsigned long offset; + unsigned long max_offset; + int pixel_stride; + int lines; + int x; + int y; +}; + +struct rectangle { + int x0; + int y0; + int x1; + int y1; +}; + +#define IVTVFB_IOCTL_GET_STATE _IOR('@', 1, struct ivtvfb_ioctl_state_info) +#define IVTVFB_IOCTL_SET_STATE _IOW('@', 2, struct ivtvfb_ioctl_state_info) +#define IVTVFB_IOCTL_PREP_FRAME _IOW('@', 3, struct ivtvfb_ioctl_dma_host_to_ivtv_args) +#define IVTVFB_IOCTL_BLT_COPY _IOW('@', 4, struct ivtvfb_ioctl_blt_copy_args) +#define IVTVFB_IOCTL_GET_ACTIVE_BUFFER _IOR('@', 5, struct ivtv_osd_coords) +#define IVTVFB_IOCTL_SET_ACTIVE_BUFFER _IOW('@', 6, struct ivtv_osd_coords) +#define IVTVFB_IOCTL_GET_FRAME_BUFFER _IOR('@', 7, struct ivtvfb_ioctl_get_frame_buffer) + +#define IVTVFB_STATUS_ENABLED (1 << 0) +#define IVTVFB_STATUS_GLOBAL_ALPHA (1 << 1) +#define IVTVFB_STATUS_LOCAL_ALPHA (1 << 2) +#define IVTVFB_STATUS_FLICKER_REDUCTION (1 << 3) + +/* Stream types */ +#define IVTV_STREAM_PS 0 +#define IVTV_STREAM_TS 1 +#define IVTV_STREAM_MPEG1 2 +#define IVTV_STREAM_PES_AV 3 +#define IVTV_STREAM_PES_V 5 +#define IVTV_STREAM_PES_A 7 +#define IVTV_STREAM_DVD 10 +#define IVTV_STREAM_VCD 11 +#define IVTV_STREAM_SVCD 12 +#define IVTV_STREAM_DVD_S1 13 +#define IVTV_STREAM_DVD_S2 14 + +/* Custom v4l controls */ +#ifndef V4L2_CID_PRIVATE_BASE +#define V4L2_CID_PRIVATE_BASE 0x08000000 +#endif + +#define V4L2_CID_IVTV_FREQ (V4L2_CID_PRIVATE_BASE) +#define V4L2_CID_IVTV_ENC (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_IVTV_BITRATE (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_IVTV_MONO (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_IVTV_JOINT (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_IVTV_EMPHASIS (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_IVTV_CRC (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_IVTV_COPYRIGHT (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_IVTV_GEN (V4L2_CID_PRIVATE_BASE + 8) + +#define IVTV_V4L2_AUDIO_MENUCOUNT 9 /* # of v4l controls */ + +#define IVTV_DEC_PRIVATE_BASE (V4L2_CID_PRIVATE_BASE + IVTV_V4L2_AUDIO_MENUCOUNT) + +#define V4L2_CID_IVTV_DEC_SMOOTH_FF (IVTV_DEC_PRIVATE_BASE + 0) +#define V4L2_CID_IVTV_DEC_FR_MASK (IVTV_DEC_PRIVATE_BASE + 1) +#define V4L2_CID_IVTV_DEC_SP_MUTE (IVTV_DEC_PRIVATE_BASE + 2) +#define V4L2_CID_IVTV_DEC_FR_FIELD (IVTV_DEC_PRIVATE_BASE + 3) +#define V4L2_CID_IVTV_DEC_AUD_SKIP (IVTV_DEC_PRIVATE_BASE + 4) +#define V4L2_CID_IVTV_DEC_NUM_BUFFERS (IVTV_DEC_PRIVATE_BASE + 5) +#define V4L2_CID_IVTV_DEC_PREBUFFER (IVTV_DEC_PRIVATE_BASE + 6) + +#define IVTV_V4L2_DEC_MENUCOUNT 7 + +#ifdef SAA7115_REGTEST +/* allow direct access to the saa7115 registers for testing */ +#define SAA7115_GET_REG 0xFFEE7705 +#define SAA7115_SET_REG 0xFFEE7706 + +struct saa7115_reg_t { + u8 reg; + u8 val; +}; +#endif +struct saa7114 { + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; + int playback; +}; + + +struct ivtv_cfg_start_decode { + u32 gop_offset; /*Frames in GOP to skip before starting */ + u32 muted_audio_frames; /* #of audio frames to mute */ +}; + +struct ivtv_cfg_stop_decode { + int hide_last; /* 1 = show black after stop, 0 = show last frame */ + u64 pts_stop; /* PTS to stop at */ +}; + +struct ivtv_speed { + int scale; /* 1-?? (50 for now) */ + int smooth; /* Smooth mode when in slow/fast mode */ + int speed; /* 0 = slow, 1 = fast */ + int direction; /* 0 = forward, 1 = reverse (not supportd */ + int fr_mask; /* 0 = I, 1 = I,P, 2 = I,P,B */ + int b_per_gop; /* frames per GOP (reverse only) */ + int aud_mute; /* Mute audio while in slow/fast mode */ + int fr_field; /* 1 = show every field, 0 = show every frame */ + int mute; /* # of audio frames to mute on playback resume */ +}; + +struct ivtv_slow_fast { + int speed; /* 0 = slow, 1 = fast */ + int scale; /* 1-?? (50 for now) */ +}; + +struct ivtv_ioctl_fwapi { + u32 cmd; + u32 result; + int args; + u32 data[IVTV_MBOX_MAX_DATA]; +}; + +struct ivtv_ioctl_framesync { + u32 frame; + u64 pts; + u64 scr; +}; + +struct ivtv_audio_meta { + struct v4l2_queryctrl *ctrl; + struct v4l2_querymenu *menu; + u32 *table; + u32 mask; + s32 setting; +}; + +/* For use with IVTV_IOC_G_CODEC and IVTV_IOC_S_CODEC */ +struct ivtv_ioctl_codec { + u32 aspect; + u32 audio_bitmap; + u32 bframes; + u32 bitrate_mode; + u32 bitrate; + u32 bitrate_peak; + u32 dnr_mode; + u32 dnr_spatial; + u32 dnr_temporal; + u32 dnr_type; + u32 framerate; + u32 framespergop; + u32 gop_closure; + u32 pulldown; + u32 stream_type; +}; + +extern int ivtv_debug; +extern int ivtv_pal; + +/* Scatter-Gather array element, used in DMA transfers */ +struct ivtv_SG_element { + u32 src; + u32 dst; + u32 size; +}; + +/* ivtv-specific mailbox template */ +struct ivtv_mailbox { + unsigned long flags; + u32 cmd; + u32 retval; + u32 timeout; + u32 data[IVTV_MBOX_MAX_DATA]; +}; + +struct ivtv_state { + unsigned long freq; /* Current tuned frequency */ + int input; /* Current digitizer input */ + u32 flags;/* tuner, audio */ + u16 type; /* tv or camera */ + u16 norm; /* Current video standard */ + /* more to come! */ +}; + +struct ivtv_buffer { + size_t readpos; + dma_addr_t dma_handle; + struct v4l2_buffer buffer; + struct list_head list; + unsigned long ts; +}; + +struct ivtv_buffer_list { + struct video_device *vdev; /* to get itv from */ + int elements; + struct list_head list; +}; + +struct ivtv_options { + int yuv_fixup; /* Should we re-work YUV to a standard format? */ + int yuv_buffers; /* How many yuv buffers to allocate? */ + int mpg_buffers; /* how many mpg buffers to allocate? */ + int vbi_buffers; /* how many vbi buffers to allocate? */ + int dec_mpg_buffers; /* how many decoder mpg buffers to allocate? */ + int dec_yuv_buffers; /* How many decoder yuv buffers to allocate? */ + int dec_mpg_qlen; /* how many decoder mpg buffers to queue? */ + int dec_yuv_qlen; /* how many decoder yuv buffers to queue? */ + int num_devices; /* how many cards to detect? */ +}; + +struct ivtv_dec_options { + int hide_last_frame; /* 0 = display last frame on stop_decode + * 1 = display black */ + u32 pts_low; /* low bits PTS to stop playback at */ + u32 pts_hi; /* hi bits PTS to stop playback at */ + int gop_offset; /* on start-playback, skip this + * # of frames in the GOP */ + int mute_frames; /* # of audio frames to mute on playback start */ + int decbuffers; /* 0 = 6 buffers, 1 = 9 buffers */ + int prebuffer; /* 0 = no prebuffer, 1 = enabled, see docs */ + struct ivtv_speed speed; +}; + +/* per-stream, s_flags */ +#define IVTV_F_S_DMAP 0 +#define IVTV_F_S_OVERFLOW 1 +#define IVTV_F_S_CAP 2 +#define IVTV_F_S_UNINIT 3 + +/* per-ivtv, i_flags */ +#define IVTV_F_I_BUSY 0 +#define IVTV_F_I_NEEDS_DATA 1 +#define IVTV_F_I_EOS 2 +#define IVTV_F_I_OSD_DMA 3 + +struct ivtv_v4l2_stream { + int buf_size; /* size of buffers this stream */ + long id; + long seq; + int ubytes; /* bytes written back to user this frame */ + unsigned long s_flags; + int v4l_reg_type; + wait_queue_head_t waitq; + struct video_device *v4l2dev; + struct v4l2_format format; + + // FIXME need to make sure no read() if streaming + struct ivtv_buffer_list free_q; /* unused buffers */ + struct ivtv_buffer_list full_q; /* filled buffers */ + struct ivtv_buffer_list dma_q; /* awaiting dma to fill them */ + /* only updated in interrupt time! */ + + int controlcount; /* Number of elements in controls */ + struct v4l2_control *controls; +}; + +struct ivtv_v4l2_table { + int count; + int active; + union { + struct v4l2_input *input; + struct v4l2_output *output; + struct v4l2_audio *audio; + struct v4l2_tuner *tuner; + struct v4l2_control *control; + struct v4l2_standard *std; + } table; +}; + +struct ivtv_v4l2 { + u32 capabilities; + struct ivtv_v4l2_table input; + int audio_output; + struct ivtv_v4l2_table output; + struct ivtv_v4l2_table audio; + struct ivtv_v4l2_table tuner; + struct ivtv_v4l2_table standard; + struct v4l2_capability capability; + struct v4l2_frequency freq; + int streamcount; /* Number of elements in streams */ + struct ivtv_v4l2_stream *streams; + + /* codec settings */ + struct ivtv_ioctl_codec codec; + struct ivtv_audio_meta audio_meta[IVTV_V4L2_AUDIO_MENUCOUNT]; + + /* FIXME probably should get rid of this */ + wait_queue_head_t waitq; +}; + +struct ivtv_open_id { + int open_id; + int type; + struct ivtv *itv; + struct list_head list; +}; + +struct ivtvfb_user_dma_to_device { + int page_count; + struct ivtv_SG_element* sglist; + struct page **map; + dma_addr_t sg_dma_handle; +}; + +/* Stuct to hold info about ivtv cards */ +struct ivtv { + int card_type; /* pvr 250 rev1, 250 rev2, 350 are options so far */ + struct pci_dev *dev; + struct ivtv_options options; + struct ivtv_dec_options dec_options; + int num; /* invalidate during init! */ + int first_read; /* used to clean up stream */ + unsigned long i_flags; + atomic_t capturing; + atomic_t decoding; + struct semaphore sem_lock ____cacheline_aligned_in_smp; + spinlock_t lock ____cacheline_aligned_in_smp; + + long open_id; /* incremented each time an open occurs + used as unique ID */ + + /* FIXME should use part of v4l2_performace instead */ + unsigned long trans_id; + + struct tasklet_struct dma_sched_tq; + + u32 enc_fw_ver, dec_fw_ver, base_addr; /*is base_addr needed? */ + u32 irqmask; + + struct ivtv_mailbox *enc_mbox, *dec_mbox; + struct semaphore enc_msem ____cacheline_aligned_in_smp; + struct semaphore dec_msem ____cacheline_aligned_in_smp; + + unsigned char card_rev, *io_mem, *reg_mem; + + wait_queue_head_t cap_w, vsync_w; + + /*FIXME perhaps move these to the v4l2_stream struct */ + struct ivtv_SG_element *SGarray, *DSGarray; + dma_addr_t SG_handle, DSG_handle; + + /* Decoder */ + struct ivtv_ioctl_framesync dec_timestamp; + wait_queue_head_t dec_master_w; + struct timer_list dec_timeout; + + /* Framebuffer DMA support */ + struct ivtvfb_user_dma_to_device* user_dma_to_device_state; + int fb_id; + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + int i2c_state, i2c_rc, i2c_command; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + + /* v4l2 and User settings*/ + struct ivtv_state state; + struct ivtv_v4l2 v4l2; + struct list_head client_list; +}; + +/* Globals */ +extern struct ivtv ivtv_cards[]; +extern int ivtv_cards_active; +extern int dec_yuv_buffers; +extern int dec_mpg_buffers; +extern int yuv_buffers; +extern int mpg_buffers; +extern int vbi_buffers; +extern spinlock_t ivtv_lock; + +/*==============Prototypes==================*/ +/* FIXME some of these proably need fine-tuning + * to avoid warnings + */ + +void ivtv_setscl(void *data, int state); +void ivtv_setsda(void *data, int state); +int ivtv_getscl(void *data); +int ivtv_getsda(void *data); + +void ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); +int ivtv_i2c_direct(struct ivtv *itv, int addr, const unsigned char *regs); + +void ivtv_inc(struct i2c_adapter *adapter); +void ivtv_dec(struct i2c_adapter *adapter); + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv); +void __devexit exit_ivtv_i2c(struct ivtv *itv); + +/* end i2c stuff */ + +/* Initialization stuff */ +int ivtv_firmware_copy(struct ivtv *itv); + +/* Unload stuff */ +void ivtv_v4l2_cleanup(struct ivtv *itv); +int ivtv_stop_firmware(struct ivtv *itv); +void ivtv_zero_usage_count(void); +void ivtv_flush_queues(struct ivtv_open_id *id); + +/* API Related */ +int ivtv_find_firmware_mailbox(struct ivtv *itv); +int ivtv_get_free_mailbox(struct ivtv_mailbox *mbox); +int ivtv_api_call(struct ivtv_mailbox *mbox, u32 cmd, struct semaphore *sem, + int elements, u32 data[]); +int ivtv_api_getresult_nosleep(struct ivtv_mailbox *mbox, u32 *result, u32 data[]); +int ivtv_api_getresult(struct ivtv_mailbox *mbox, struct semaphore *sem, + u32 *result, u32 data[]); +int ivtv_api(struct ivtv_mailbox *mbox, struct semaphore *sem, int cmd, + u32 *result, int args, u32 data[]); +extern int __ivtv_api(struct ivtv_mailbox *mbox, int cmd, + u32 *result, int args, u32 data[]); +int ivtv_v4l2_setup(struct ivtv *itv); + +/* Capture related */ +int ivtv_stop_decode(struct ivtv_open_id *id); +int ivtv_stop_all_captures(struct ivtv *itv); +int ivtv_stop_capture(struct ivtv_open_id *id); +long ivtv_read(struct ivtv_open_id *id, char *ubuf, size_t count, int block); +int ivtv_get_timing_info(struct ivtv *itv, struct ivtv_ioctl_framesync *info); +ssize_t ivtv_write(struct ivtv_open_id *id, const char *buf, size_t count, + int block); +unsigned int ivtv_poll(struct file *filp, poll_table *wait); +unsigned int ivtv_dec_poll(struct file *filp, poll_table *wait); + /* makes a queue complete with 'length' items */ + /* NOTE: This returns the # of buffers allocated */ +extern int ivtv_init_queue(struct ivtv *itv,struct ivtv_buffer_list *queue, + int length, enum v4l2_buf_type type); + /* moves all items in queue 'src' to queue 'dst' */ +extern int ivtv_move_queue(struct ivtv *itv, struct ivtv_buffer_list *src, + struct ivtv_buffer_list *dst); + +/* Hardware/IRQ */ +extern void ivtv_set_irq_mask(struct ivtv *itv, unsigned long mask); +extern void ivtv_clear_irq_mask(struct ivtv *itv, unsigned long mask); +extern void ivtv_sleep_timeout(int timeout); + +/* Testing/Debugging */ +extern int ivtv_close(struct ivtv_open_id *id); + +/* debug stuff, to get the locking right */ +#ifndef WARN_ON +#define WARN_ON(condition) do { \ + if (unlikely((condition)!=0)) { \ + printk("Badness in %s at %s:%d\n", __FUNCTION__, __FILE__, __LINE__); \ + dump_stack(); \ + } \ +} while (0) +#endif + +#define IVTV_ASSERT(x) WARN_ON(!(x)) + +static inline int ivtv_sem_count(struct semaphore *sem) +{ + return atomic_read(&sem->count); +} + +#endif diff -upN reference/drivers/media/video/msp3400.c current/drivers/media/video/msp3400.c --- reference/drivers/media/video/msp3400.c Wed Jun 16 08:19:19 2004 +++ current/drivers/media/video/msp3400.c Thu Jun 17 12:58:30 2004 @@ -58,6 +58,7 @@ static int debug = 0; /* debug out static int once = 0; /* no continous stereo monitoring */ static int amsound = 0; /* hard-wire AM sound at 6.5 Hz (france), the autoscan seems work well only with FM... */ +static int standard = 1; /* Override auto detect of audio standard, if needed. */ static int simple = -1; /* use short programming (>= msp3410 only) */ static int dolby = 0; @@ -113,6 +114,7 @@ struct msp3400c { MODULE_PARM(once,"i"); MODULE_PARM(debug,"i"); MODULE_PARM(simple,"i"); +MODULE_PARM(standard,"i"); MODULE_PARM(amsound,"i"); MODULE_PARM(dolby,"i"); @@ -384,7 +386,7 @@ static void msp3400c_setvolume(struct i2 if (!muted) { vol = (left > right) ? left : right; - val = (vol * 0x73 / 65535) << 8; + val = (vol * 0x7F / 65535) << 8; } if (vol > 0) { balance = ((right-left) * 127) / vol; @@ -395,8 +397,11 @@ static void msp3400c_setvolume(struct i2 muted ? "on" : "off", left, right, val>>8, balance); msp3400c_write(client,I2C_MSP3400C_DFP, 0x0000, val); /* loudspeaker */ msp3400c_write(client,I2C_MSP3400C_DFP, 0x0006, val); /* headphones */ - /* scart - on/off only */ - msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, val ? 0x4000 : 0); + // scart - on/off only - AEW why? undone NOTE values below + // 40000 are mostly useless, 59343 is a good default (0x73) + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0007, + muted ? 0x1 : (val | 0x1)); + msp3400c_write(client,I2C_MSP3400C_DFP, 0x0001, balance << 8); } @@ -473,6 +478,20 @@ static void msp3400c_setmode(struct i2c_ } } +// given a bitmask of VIDEO_SOUND_XXX returns the "best" in the bitmask +static int best_video_sound(int mode) { + int ret_cap = VIDEO_SOUND_MONO; + if (mode & VIDEO_SOUND_STEREO) { + ret_cap = VIDEO_SOUND_STEREO; + } else if (mode & VIDEO_SOUND_LANG1) { + ret_cap = VIDEO_SOUND_LANG1; + } else if (mode & VIDEO_SOUND_LANG2) { + ret_cap = VIDEO_SOUND_LANG2; + } + return ret_cap; +} + + /* turn on/off nicam + stereo */ static void msp3400c_setstereo(struct i2c_client *client, int mode) { @@ -547,7 +566,7 @@ static void msp3400c_setstereo(struct i2 } /* switch audio */ - switch (mode) { + switch (best_video_sound(mode)) { case VIDEO_SOUND_STEREO: src = 0x0020 | nicam; #if 0 @@ -1056,7 +1075,7 @@ static int msp3410d_thread(void *data) switch (msp->norm) { case VIDEO_MODE_PAL: mode = 0x1003; - std = 1; + std = standard; break; case VIDEO_MODE_NTSC: /* BTSC */ mode = 0x2003; @@ -1064,15 +1083,19 @@ static int msp3410d_thread(void *data) break; case VIDEO_MODE_SECAM: mode = 0x0003; - std = 1; + std = standard; break; case VIDEO_MODE_RADIO: mode = 0x0003; std = 0x0040; break; + case VIDEO_MODE_AUTO: + mode = 0x2003; + std = standard; + break; default: mode = 0x0003; - std = 1; + std = standard; break; } msp3400c_write(client, I2C_MSP3400C_DEM, 0x30, mode); @@ -1189,8 +1212,11 @@ static int msp3410d_thread(void *data) msp->nicam_on = 0; msp->watch_stereo = 1; break; - } - + } + // AEW a true reset has probably messed with our ACB register + // we need to restore this. + msp3400c_write(client, I2C_MSP3400C_DFP, 0x0013, msp->acb); + /* unmute + restore dfp registers */ msp3400c_setbass(client, msp->bass); msp3400c_settreble(client, msp->treble); @@ -1257,12 +1283,12 @@ static int msp_attach(struct i2c_adapter } memset(msp,0,sizeof(struct msp3400c)); - msp->left = 65535; - msp->right = 65535; + msp->left = 59343; + msp->right = 59343; msp->bass = 32768; msp->treble = 32768; msp->input = -1; - msp->muted = 1; + /* msp->muted = 1; */ for (i = 0; i < DFP_COUNT; i++) msp->dfp_regs[i] = -1; @@ -1482,7 +1508,7 @@ static int msp_command(struct i2c_client struct video_audio *va = arg; dprintk(KERN_DEBUG "msp34xx: VIDIOCGAUDIO\n"); - va->flags |= VIDEO_AUDIO_VOLUME | + va->flags = VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE | VIDEO_AUDIO_MUTABLE; @@ -1537,6 +1563,7 @@ static int msp_command(struct i2c_client dprintk(KERN_DEBUG "msp34xx: VIDIOCSCHAN (norm=%d)\n",vc->norm); msp->norm = vc->norm; + msp_wake_thread(client); break; } case VIDIOCSFREQ: @@ -1546,14 +1573,22 @@ static int msp_command(struct i2c_client msp_wake_thread(client); break; } + case MSP_SET_MATRIX: + { + struct msp_matrix *mspm = arg; - default: - /* nothing */ + dprintk(KERN_DEBUG "msp34xx: MSP_SET_MATRIX\n"); + msp3400c_set_scart(client, mspm->input, mspm->output); break; } - return 0; -} + default: + /* nothing */ + break; + } + return 0; +} + /* ----------------------------------------------------------------------- */ static int msp3400_init_module(void) diff -upN reference/drivers/media/video/msp3400.h current/drivers/media/video/msp3400.h --- reference/drivers/media/video/msp3400.h Wed Dec 17 18:59:38 2003 +++ current/drivers/media/video/msp3400.h Thu Jun 17 12:58:30 2004 @@ -8,7 +8,15 @@ struct msp_dfpreg { int value; }; +struct msp_matrix { + int input; + int output; +}; + #define MSP_SET_DFPREG _IOW('m',15,struct msp_dfpreg) #define MSP_GET_DFPREG _IOW('m',16,struct msp_dfpreg) + +/* ioctl for MSP_SET_MATRIX will have to be registered */ +#define MSP_SET_MATRIX _IOW('m',17,struct msp_matrix) #endif /* MSP3400_H */ diff -upN /dev/null current/drivers/media/video/saa7115.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/saa7115.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,1130 @@ +/* + * saa7114 - Philips SAA7114H video decoder driver version 0.0.1 + * + * Copyright (C) 2002 Maxim Yevtyushkin + * + * Based on saa7111 driver by Dave Perks + * + * Copyright (C) 1998 Dave Perks + * + * Slight changes for video timing and attachment output by + * Wolfgang Scherr + * + * Changes by Ronald Bultje + * - moved over to linux>=2.4.x i2c protocol (1/1/2003) + * + * Changes by Kevin Thayer + * - changed to saa7115. (2/17/2003) + * + * 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. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +MODULE_DESCRIPTION("Philips SAA7115 video decoder driver"); +MODULE_AUTHOR("Kevin Thayer"); +MODULE_LICENSE("GPL"); + +#include +#include + +#ifndef I2C_DRIVERID_SAA7114 +#warning Using temporary hack for missing I2C driver-ID for saa7114 +#define I2C_DRIVERID_SAA7114 I2C_DRIVERID_EXP1 +#endif + +#include + +static int debug = 1; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug level (0-1)"); + +/* FIXME need to get this properly allocated + * also defined in ivtv.h, so change it there too */ +#define DECODER_SET_SIZE 76598 +#define DECODER_GET_PICTURE 76599 + +/* Need to be able to set the audio bitrates */ +#define DECODER_SET_AUDIO 0xFFEE7707 +#define DECODER_AUDIO_32_KHZ 0 +#define DECODER_AUDIO_441_KHZ 1 +#define DECODER_AUDIO_48_KHZ 2 + +#ifdef SAA7115_REGTEST +/* allow direct access to the saa7115 registers for testing */ +#define SAA7115_GET_REG 0xFFEE7705 +#define SAA7115_SET_REG 0xFFEE7706 + +struct saa7115_reg_t { + u8 reg; + u8 val; +}; +#endif + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +static u8 readreg(struct i2c_client *client, unsigned char reg) +{ + struct i2c_adapter *adap = client->adapter; + unsigned char mm1[] = { 0x1e }; + unsigned char mm2[] = { 0x00 }; + struct i2c_msg msgs[2]; + + msgs[0].flags = 0; + msgs[1].flags = I2C_M_RD; + msgs[0].addr = msgs[1].addr = client->addr; + mm1[0] = reg; + msgs[0].len = 1; + msgs[1].len = 1; + msgs[0].buf = mm1; + msgs[1].buf = mm2; + i2c_transfer(adap, msgs, 2); + + return mm2[0]; +} + +struct saa7114 { + int norm; + int input; + int enable; + int bright; + int contrast; + int hue; + int sat; + int playback; + int audio; +}; + +#define I2C_SAA7114 0x42 +#define I2C_SAA7114A 0x40 + +#define I2C_DELAY 10 + +//#define SAA_7114_NTSC_HSYNC_START (-3) +//#define SAA_7114_NTSC_HSYNC_STOP (-18) + +#define SAA_7114_NTSC_HSYNC_START (-17) +#define SAA_7114_NTSC_HSYNC_STOP (-32) + +//#define SAA_7114_NTSC_HOFFSET (5) +#define SAA_7114_NTSC_HOFFSET (6) +#define SAA_7114_NTSC_VOFFSET (10) +#define SAA_7114_NTSC_WIDTH (720) +#define SAA_7114_NTSC_HEIGHT (480) /* was 250 */ + +#define SAA_7114_SECAM_HSYNC_START (-17) +#define SAA_7114_SECAM_HSYNC_STOP (-32) + +#define SAA_7114_SECAM_HOFFSET (2) +#define SAA_7114_SECAM_VOFFSET (10) +#define SAA_7114_SECAM_WIDTH (720) +#define SAA_7114_SECAM_HEIGHT (300) + +#define SAA_7114_PAL_HSYNC_START (-17) +#define SAA_7114_PAL_HSYNC_STOP (-32) + +#define SAA_7114_PAL_HOFFSET (2) +#define SAA_7114_PAL_VOFFSET (10) +#define SAA_7114_PAL_WIDTH (720) +#define SAA_7114_PAL_HEIGHT (300) + +#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040 +#define SAA_7114_VERTICAL_LUMA_OFFSET 0 + +#define REG_ADDR(x) (((x) << 1) + 1) +#define LOBYTE(x) ((unsigned char)((x) & 0xff)) +#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff)) +#define LOWORD(x) ((unsigned short int)((x) & 0xffff)) +#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff)) + +/* ----------------------------------------------------------------------- */ + +static inline int saa7114_write(struct i2c_client *client, u8 reg, u8 value) +{ +// struct saa7114 *decoder = i2c_get_clientdata(client); + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int writeregs(struct i2c_client *client, const unsigned char *regs) +{ + unsigned char reg, data; + + while (*regs != 0x00) { + reg = *(regs++); + data = *(regs++); + if (saa7114_write(client, reg, data) < 0) + return -1; + } + return 0; +} + +static inline int saa7114_read(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* ----------------------------------------------------------------------- */ + +static const unsigned char init_saa7115_auto_input[] = { + 0x01, 0x08, //(was 0x48) // 0x08: white peak control enabled, 0x48: white peak control disabled + 0x03, 0x2C, //was 0x20 // 0x20: automatic gain control, 0x2c: user programmable gain + 0x04, 0x90, // analog gain set to 0 + 0x05, 0x90, // analog gain set to 0 + 0x06, 0xEB, // horiz sync begin = -21 + 0x07, 0xE0, // horiz sync stop = -17 + + // done in misc +// 0x09, 0x40, //80 for svideo // 0x40: use luminance comb filter, 0x80: don't use ... + + 0x0A, 0x80, //i2c dump ends up at 96 (was 80) // decoder brightness, 0x80 is itu standard + 0x0B, 0x44, // decoder contrast, 0x44 is itu standard + 0x0C, 0x40, // decoder saturation, 0x40 is itu standard + 0x0D, 0x00, //i2c dump ends up at 04 (was 00) // chrominance hue control + 0x0F, 0x24, //i2c dump says 0x30 (was 0x2A) // chrominance gain control , should be 0x00 for agc, otherwise 0x80+0x24: 0xA4 + 0x10, 0x06, + 0x11, 0x00, + 0x12, 0x9D, //i2c dump says 0x9D (was 0x00) + 0x13, 0x80, //" 0x80 (was 0x00) + 0x14, 0x01, //" 0x01 (was 0x01) + 0x15, 0x04, //" 0x00 (was 0x11) //should also be moved to NTSC/PAL VGATE start + /* moved to NTSC/PAL sections + 0x16, 0x11, //" 0x11 (was 0xFE) // VGATE stop + */ + 0x17, 0x98, //" 0x98 (was 0xD8) //may set to 98 // VGATE MSB and other values + 0x18, 0x40, // raw data gain 0x00 = nominal + 0x19, 0x80, // raw data offset 0x80 = 0 LSB + 0x1A, 0x77, // color killer level control 0x77 = recommended + 0x1B, 0x42, // misc chroma control 0x42 = recommended + 0x1C, 0xA9, // combfilter control 0xA9 = recommended + 0x1D, 0x01, // combfilter control 0x01 = recommended + 0x88, 0xD0, //reset device // set programmed, reset + 0x88, 0xF0, //Set device programmed, all in operational mode // set programmed, active ?programmed should be 0? + 0x00, 0x00 // ? not necessary, version readback register +}; + +/* ============== SAA7715 VIDEO templates ============= */ + +static const unsigned char cfg_saa7115_reset_scaler[] = { + 0x87, 0x00, //Disable I-port output + 0x88, 0x0B, //reset scaler (was 0xD0) // ?should be 0xD0 + 0x88, 0xF0, //activate scaler + 0x87, 0x01, //Enable I-port output // what about high bits? how is ICLK used? + 0x00, 0x00 +}; +static const unsigned char cfg_saa7115_NTSC_fullres_x[] = { + 0xCC, 0xD0, //hsize low (output) //hor output window size = 0x2d0 = 720 + 0xCD, 0x02, //hsize hi (output) + + 0xD0, 0x01, // down scale = 1 + 0xD1, 0x00, // prescale accumulation length = 1 + 0xD2, 0x00, // dc gain and fir prefilter control + 0xD4, 0x80, //Lum Brightness // nominal value = 0x80 + 0xD5, 0x40, //Lum contrast // nominal value = 0x40 + 0xD6, 0x40, //Chroma satur. // nominal value = 0x80 + 0xD8, 0x00, // hor lum scaling 0x0400 = 1 + 0xD9, 0x04, + 0xDA, 0x00, //H-phase offset Luma = 0 + 0xDC, 0x00, // hor chrom scaling 0x0200. must be hor lum scaling /2 + 0xDD, 0x02, //H-scaling incr chroma + 0xDE, 0x00, //H-phase offset chroma // must be offset luma /2 + + 0x00, 0x00 +}; +static const unsigned char cfg_saa7115_NTSC_fullres_y[] = { + 0xCE, 0xFD, //vsize low (output) was FD // ver output window size = 253 ??240 + 0xCF, 0x00, //vsize hi (output) + + 0xE0, 0x00, //V-scaling incr luma low 0x0400 = 1 + 0xE1, 0x04, //" hi + 0xE2, 0x00, //V-scaling incr chroma low // must be same as luma + 0xE3, 0x04, //" hi + 0xE4, 0x01, //V-scaling mode control // no mirroring, higher order accumulation + 0xE8, 0x00, //V-phase offset chroma 00 //?only regs E8 and EC necessary? + 0xE9, 0x00, //V-phase offset chroma 01 + 0xEA, 0x00, //V-phase offset chroma 10 + 0xEB, 0x00, //V-phase offset chroma 11 + 0xEC, 0x00, //V-phase offset luma 00 + 0xED, 0x00, //V-phase offset luma 01 + 0xEE, 0x00, //V-phase offset luma 10 + + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_NTSC_video[] = { + 0x80, 0x00, //reset tasks + 0x88, 0x0B, //reset scaler (was 0xD0) + + 0x16, 0x11, //" 0x11 (was 0xFE) //VGATE pulse stop + + 0x08, 0x68, //i2c dump says 0x68 (was 0xB0) NTSC ONLY // 0xBO: auto detection, 0x68 = NTSC + 0x0E, 0x07, //i2c dump says 0x0d (was 0x07) // lots of different stuff... video autodetection is on + + 0xC0, 0x00, //Task Handling Control (was 0x00) + 0xC1, 0x08, //X-port formats/config + 0xC2, 0x00, //Input Ref. signal Def. + 0xC3, 0x80, //I-port config (was 0x80) + 0xC4, 0x02, //hoffset low (input) // 0x0002 is minimum + 0xC5, 0x00, //hoffset hi (input) + 0xC6, 0xD0, //hsize low (input) // 0x02d0 = 720 + 0xC7, 0x02, //hsize hi (input) + 0xC8, 0x14, //voff low was 0x14, changing to 0x0E (14) // 0x0014 = 20 + 0xC9, 0x00, //voff hi + 0xCA, 0xFD, //vsize low (input) was FD // 0x00fd = 253 + 0xCB, 0x00, //vsize hi (input) + + 0xF0, 0xAD, //Set PLL Register. NTSC 525 lines per frame, 27 MHz + 0xF1, 0x05, //low bit with 0xF0, (was 0x05) + 0xF5, 0xAD, //Set pulse generator register + 0xF6, 0x01, + + 0x87, 0x00, //Disable I-port output + 0x88, 0x0B, //reset scaler (was 0xD0) + 0x80, 0x20, //Activate only task "B", continuous mode (was 0xA0) + 0x88, 0xF0, //activate scaler + 0x87, 0x01, //Enable I-port output + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_PAL_fullres_x[] = { + 0xCC, 0xD0, //hsize low (output) //720 same as NTSC + 0xCD, 0x02, //hsize hi (output) + + 0xD0, 0x01, + 0xD1, 0x00, + 0xD2, 0x00, + 0xD4, 0x80, //Lum Brightness + 0xD5, 0x40, //Lum contrast + 0xD6, 0x40, //Chroma satur. + 0xD8, 0x00, + 0xD9, 0x04, + 0xDA, 0x00, //H-phase offset Luma + 0xDC, 0x00, + 0xDD, 0x02, //H-scaling incr chroma + 0xDE, 0x00, //H-phase offset chroma + + 0x00, 0x00 +}; +static const unsigned char cfg_saa7115_PAL_fullres_y[] = { + 0xCE, 0x20, //vsize low (output) // 0x0120 = 288 + 0xCF, 0x01, //vsize hi (output) + + 0xE0, 0x00, //V-scaling incr luma low + 0xE1, 0x04, //" hi + 0xE2, 0x00, //V-scaling incr chroma low + 0xE3, 0x04, //" hi + 0xE4, 0x01, //V-scaling mode control + 0xE8, 0x00, //V-phase offset chroma 00 + 0xE9, 0x00, //V-phase offset chroma 01 + 0xEA, 0x00, //V-phase offset chroma 10 + 0xEB, 0x00, //V-phase offset chroma 11 + 0xEC, 0x00, //V-phase offset luma 00 + 0xED, 0x00, //V-phase offset luma 01 + 0xEE, 0x00, //V-phase offset luma 10 + 0xEF, 0x00, //V-phase offset luma 11 + + 0x00, 0x00 +}; + +/* FIXME need to input proper height/width */ +static const unsigned char cfg_saa7115_PAL_video[] = { + 0x80, 0x00, //reset tasks + 0x88, 0x0B, //reset scaler (was 0xD0) + + 0x16, 0x15, //" 0x11 (was 0xFE) + + 0x08, 0x28, //i2c dump says 0x28 (was 0xB0) PAL ONLY // 0x28 = PAL + 0x0E, 0x07, //i2c dump says 0x0d (was 0x07) + + 0xC0, 0x00, //Task Handling Control (was 0x00) + 0xC1, 0x08, //X-port formats/config + 0xC2, 0x00, //Input Ref. signal Def. + 0xC3, 0x80, //I-port config (was 0x80) + 0xC4, 0x00, //hoffset low (input) + 0xC5, 0x00, //hoffset hi (input) + 0xC6, 0xD0, //hsize low (input) // 0x02D0 = 720 + 0xC7, 0x02, //hsize hi (input) + 0xC8, 0x14, //voffset low (input) low was 0x14, changing to 0x0E (14) + 0xC9, 0x00, //voffset hi (input) + 0xCA, 0x20, //vsize low (input) // 288 + 0xCB, 0x01, //vsize hi (input) + + 0xF0, 0xB0, //Set PLL Register. PAL 625 lines per frame, 27 MHz + 0xF1, 0x05, //low bit with 0xF0, (was 0x05) + 0xF5, 0xB0, //Set pulse generator register + 0xF6, 0x01, + + 0x87, 0x00, //Disable I-port output + 0x88, 0x0B, //reset scaler (was 0xD0) + 0x80, 0xA0, //Activate only task "B", continuous mode (was 0xA0) + 0x88, 0xF0, //activate scaler + 0x87, 0x01, //Enable I-port output + 0x00, 0x00 +}; + +/* ============== SAA7715 VIDEO templates (end) ======= */ + +static const unsigned char init_saa7115_misc[] = { + 0x38, 0x03, // audio stuff + 0x39, 0x10, + 0x3A, 0x00, + +// 0x80, 0x00, // set below + 0x81, 0x01, //reg 0x15,0x16 define blanking window + 0x82, 0x00, + 0x83, 0x01, //was 0x01 // I port settings + 0x84, 0x20, + 0x85, 0x21, + 0x86, 0xC5, + 0x87, 0x01, +// 0x88, 0xD0, // unnecessary + +// 0xF0, 0xAD, //this goes in PAL/NTSC video +// 0xF1, 0x05, + 0xF2, 0x50, // crystal clock = 24.576 MHz, target = 27MHz + 0xF3, 0x46, + 0xF4, 0x00, +// 0xF5, 0xAD, //this goes in PAL/NTSC video +// 0xF6, 0x01, + 0xF7, 0x4B, // not the recommended settings! + 0xF8, 0x00, + 0xF9, 0x4B, + 0xFA, 0x00, + 0xFB, 0x4B, +// 0xFC, 0x00, // unused +// 0xFD, 0x00, +// 0xFE, 0x00, + 0xFF, 0x88, // PLL2 lock detection settings: 71 lines 50% phase error + +// 0x88, 0xF0, // unnecessary + +// 0x0D, 0x04, // already set in auto_input +// 0x0C, 0x40, +// 0x0A, 0x96, +// 0x0B, 0x41, +// 0x98, 0x05, // belongs to task A; unnecessary +/* Turn off VBI */ + 0x40, 0x00, + 0x41, 0xFF, + 0x42, 0xFF, + 0x43, 0xFF, + 0x44, 0xFF, + 0x45, 0xFF, + 0x46, 0xFF, + 0x47, 0xFF, + 0x48, 0xFF, + 0x49, 0xFF, + 0x4A, 0xFF, + 0x4B, 0xFF, + 0x4C, 0xFF, + 0x4D, 0xFF, + 0x4E, 0xFF, + 0x4F, 0xFF, + 0x50, 0xFF, + 0x51, 0xFF, + 0x52, 0xFF, + 0x53, 0xFF, + 0x54, 0xFF, + 0x55, 0xFF, + 0x56, 0xFF, + 0x57, 0xFF, + 0x58, 0x00, + 0x59, 0x47, + 0x5A, 0x06, + 0x5B, 0x88, + 0x5D, 0xBF, + 0x5E, 0x35, + + 0x02, 0x84, //input tuner -> input 4, amplifier active + 0x09, 0x53, //chrom trap for tuner // special tuner stuff? + + 0x80, 0x20, //was 0x30 // 0x20 clock from PLL2, 0x30 clock from ICLK + 0x88, 0xD0, + 0x88, 0xF0, + 0x00, 0x00 +}; + +/* ============== SAA7715 AUDIO settings ============= */ +static const unsigned char cfg_saa7115_48_audio[] = { + 0x34, 0xCE, // 48khz + 0x35, 0xFB, // " + 0x36, 0x30, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_441_audio[] = { + 0x34, 0xF2, // 44.1khz + 0x35, 0x00, // " + 0x36, 0x2D, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_32_audio[] = { + 0x34, 0xDF, // 32.0khz + 0x35, 0xA7, // " + 0x36, 0x20, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_NTSC_48_audio[] = { + 0x30, 0xCD, // 48.0khz NTSC + 0x31, 0x20, // " + 0x32, 0x03, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_PAL_48_audio[] = { + 0x30, 0x00, // 48.0khz PAL + 0x31, 0xC0, // " + 0x32, 0x03, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_NTSC_441_audio[] = { + 0x30, 0xBC, // 44.1khz NTSC + 0x31, 0xDF, // " + 0x32, 0x02, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_PAL_441_audio[] = { + 0x30, 0x00, // 44.1khz PAL + 0x31, 0x72, // " + 0x32, 0x03, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_NTSC_32_audio[] = { + 0x30, 0xDE, // 32.0khz NTSC + 0x31, 0x15, // " + 0x32, 0x02, // " + 0x00, 0x00 +}; + +static const unsigned char cfg_saa7115_PAL_32_audio[] = { + 0x30, 0x00, // 32.0khz PAL + 0x31, 0x80, // " + 0x32, 0x02, // " + 0x00, 0x00 +}; + +/* ============ SAA7715 AUDIO settings (end) ============= */ + +static int +saa7114_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct saa7114 *decoder = i2c_get_clientdata(client); + + switch (cmd) { + + case 0: + //dprintk(1, KERN_INFO "%s: writing init\n", client->name); + //saa7114_write_block(client, init, sizeof(init)); + break; +#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; + + saa7115_reg->val = + saa7114_read(client, saa7115_reg->reg); + break; + } + case SAA7115_SET_REG: + { + struct saa7115_reg_t *saa7115_reg = + (struct saa7115_reg_t *)arg; + + saa7114_write(client, saa7115_reg->reg, + saa7115_reg->val); + break; + } +#endif + case DECODER_SET_SIZE: + { + /* Used video_window because it has height/width and is + * already defined */ + struct video_window *wind = arg; + int HPSC, HFSC; + int VSCY, Vsrc; + + dprintk(1, KERN_INFO "%s: decoder set size\n", + client->name); + + /* FIXME need better bounds checking here */ + if ((wind->width < 1) || (wind->width > 1440)) + return -EINVAL; + if ((wind->height < 1) || (wind->height > 960)) + return -EINVAL; + + /* probably have a valid size, let's set it */ +/* Set output width/height */ + /* width */ + saa7114_write(client, 0xCC, (u8) (wind->width & 0xFF)); + saa7114_write(client, 0xCD, + (u8) ((wind->width >> 8) & 0xFF)); + /* height */ + saa7114_write(client, 0xCE, (u8) (wind->height & 0xFF)); + saa7114_write(client, 0xCF, + (u8) ((wind->height >> 8) & 0xFF)); + +/* Scaling settings */ + /* Hprescaler is floor(inres/outres) */ + /* FIXME hardcoding input res */ + if (wind->width != 720) { + HPSC = (int)(720 / wind->width); + HFSC = + (int)((1024 * 720) / (HPSC * wind->width)); + + printk("Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, + HFSC); + /* FIXME hardcodes to "Task B" + * write H prescaler integer */ + saa7114_write(client, 0xD0, (u8) (HPSC & 0x3F)); + + /* write H fine-scaling (luminance) */ + saa7114_write(client, 0xD8, (u8) (HFSC & 0xFF)); + saa7114_write(client, 0xD9, + (u8) ((HFSC >> 8) & 0xFF)); + /* write H fine-scaling (chrominance) + * must be lum/2, so i'll just bitshift :) */ + saa7114_write(client, 0xDC, + (u8) ((HFSC >> 1) & 0xFF)); + saa7114_write(client, 0xDD, + (u8) ((HFSC >> 9) & 0xFF)); + } else { + if (decoder->norm != VIDEO_MODE_NTSC) { + printk("Setting full PAL width\n"); + writeregs(client, + cfg_saa7115_PAL_fullres_x); + } else { + printk("Setting full NTSC width\n"); + writeregs(client, + cfg_saa7115_NTSC_fullres_x); + } + } + + Vsrc = 480; + if (decoder->norm != VIDEO_MODE_NTSC) + Vsrc = 576; + + if (wind->height != Vsrc) { + VSCY = (int)((1024 * Vsrc) / wind->height); + printk("Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY); + /* write V fine-scaling (luminance) */ + saa7114_write(client, 0xE0, (u8) (VSCY & 0xFF)); + saa7114_write(client, 0xE1, + (u8) ((VSCY >> 8) & 0xFF)); + /* write V fine-scaling (chrominance) */ + saa7114_write(client, 0xE2, (u8) (VSCY & 0xFF)); + saa7114_write(client, 0xE3, + (u8) ((VSCY >> 8) & 0xFF)); + } else { + if (decoder->norm != VIDEO_MODE_NTSC) { + printk("Setting full PAL height\n"); + writeregs(client, + cfg_saa7115_PAL_fullres_y); + } else { + printk("Setting full NTSC height\n"); + writeregs(client, + cfg_saa7115_NTSC_fullres_y); + } + } + + writeregs(client, cfg_saa7115_reset_scaler); + break; + } + case DECODER_DUMP: + { + int i; + + dprintk(1, KERN_INFO "%s: decoder dump\n", + client->name); + + for (i = 0; i < 32; i += 16) { + int j; + + printk(KERN_DEBUG "%s: %03x", client->name, i); + for (j = 0; j < 16; ++j) { + printk(" %02x", + saa7114_read(client, i + j)); + } + printk("\n"); + } + } + break; + + case DECODER_GET_CAPABILITIES: + { + struct video_decoder_capability *cap = arg; + + dprintk(1, KERN_DEBUG "%s: decoder get capabilities\n", + client->name); + + cap->flags = VIDEO_DECODER_PAL | + VIDEO_DECODER_NTSC | + VIDEO_DECODER_SECAM | + VIDEO_DECODER_AUTO | VIDEO_DECODER_CCIR; + cap->inputs = 8; + cap->outputs = 1; + } + break; + + case DECODER_SET_AUDIO: + { + int *iarg = arg; + dprintk(1, KERN_DEBUG "%s set audio: 0x%02x\n", + client->name, *iarg); + switch (*iarg) { + case DECODER_AUDIO_32_KHZ: + writeregs(client, cfg_saa7115_32_audio); + if (decoder->norm == VIDEO_MODE_NTSC) { + writeregs(client, + cfg_saa7115_NTSC_32_audio); + } else { + writeregs(client, + cfg_saa7115_PAL_32_audio); + } + break; + case DECODER_AUDIO_441_KHZ: + writeregs(client, cfg_saa7115_441_audio); + if (decoder->norm == VIDEO_MODE_NTSC) { + writeregs(client, + cfg_saa7115_NTSC_441_audio); + } else { + writeregs(client, + cfg_saa7115_PAL_441_audio); + } + break; + case DECODER_AUDIO_48_KHZ: + writeregs(client, cfg_saa7115_48_audio); + if (decoder->norm == VIDEO_MODE_NTSC) { + writeregs(client, + cfg_saa7115_NTSC_48_audio); + } else { + writeregs(client, + cfg_saa7115_PAL_48_audio); + } + break; + default: + printk(KERN_DEBUG + "%s invalid audio setting 0x%02x\n", + client->name, *iarg); + } + + /*FIXME digitizer reset needed? + * if so, uncomment this line */ + //writeregs(client, cfg_saa7115_reset_scaler); + + decoder->audio = *iarg; + + } + break; + case DECODER_GET_STATUS: + { + int *iarg = arg; + int status; + int res; + + status = saa7114_read(client, 0x1f); + + dprintk(1, KERN_DEBUG "%s status: 0x%02x\n", + client->name, status); + res = 0; + if ((status & (1 << 6)) == 0) { + res |= DECODER_STATUS_GOOD; + } + switch (decoder->norm) { + case VIDEO_MODE_NTSC: + res |= DECODER_STATUS_NTSC; + break; + case VIDEO_MODE_PAL: + res |= DECODER_STATUS_PAL; + break; + case VIDEO_MODE_SECAM: + res |= DECODER_STATUS_SECAM; + break; + default: + case VIDEO_MODE_AUTO: + if ((status & (1 << 5)) != 0) { + res |= DECODER_STATUS_NTSC; + } else { + res |= DECODER_STATUS_PAL; + } + break; + } + if ((status & (1 << 0)) != 0) { + res |= DECODER_STATUS_COLOR; + } + *iarg = res; + } + break; + + case DECODER_SET_NORM: + { + u16 *iarg = arg; + + dprintk(1, KERN_DEBUG "%s: decoder set norm ", + client->name); + + switch (*iarg) { + + case VIDEO_MODE_NTSC: + dprintk(1, "NTSC\n"); + writeregs(client, cfg_saa7115_NTSC_video); + break; + + case VIDEO_MODE_PAL: + dprintk(1, "PAL\n"); + writeregs(client, cfg_saa7115_PAL_video); + break; + + case VIDEO_MODE_SECAM: + dprintk(1, "SECAM\n"); + writeregs(client, cfg_saa7115_PAL_video); + break; + + default: + dprintk(1, " Unknown video mode!!!\n"); + return -EINVAL; + + } + + decoder->norm = *iarg; + + /* switch audio mode too! */ + saa7114_command(client, DECODER_SET_AUDIO, + &decoder->audio); + + } + break; + + case DECODER_SET_INPUT: + { + int *iarg = arg; + + dprintk(1, KERN_DEBUG "%s: decoder set input (%d)\n", + client->name, *iarg); + /* inputs from 0-9 are available */ + if (*iarg < 0 || *iarg > 9) { + return -EINVAL; + } + + if (decoder->input != *iarg) { + dprintk(1, + KERN_DEBUG "%s: now setting %s input\n", + client->name, + *iarg >= 6 ? "S-Video" : "Composite"); + decoder->input = *iarg; + + /* select mode */ + saa7114_write(client, + 0x02, + (saa7114_read(client, 0x02) & + 0xf0) | decoder->input); + + /* bypass chrominance trap for modes 6..9 */ + saa7114_write(client, 0x09, + (saa7114_read(client, 0x09) & + 0x7f) | (decoder->input < + 6 ? 0x0 : 0x80)); + } + } + break; + + case DECODER_SET_OUTPUT: + { + int *iarg = arg; + + dprintk(1, KERN_DEBUG "%s: decoder set output\n", + client->name); + + /* not much choice of outputs */ + if (*iarg != 0) { + return -EINVAL; + } + } + break; + + case DECODER_ENABLE_OUTPUT: + { + int *iarg = arg; + int enable = (*iarg != 0); + + dprintk(1, KERN_DEBUG "%s: decoder %s output\n", + client->name, enable ? "enable" : "disable"); + + decoder->playback = !enable; + + if (decoder->enable != enable) { + decoder->enable = enable; + + if (decoder->enable) { + saa7114_write(client, 0x87, 0x01); + } else { + saa7114_write(client, 0x87, 0x00); + } + } + } + break; + + case DECODER_GET_PICTURE: + { + struct saa7114 *pic = arg; + + pic->bright = decoder->bright; + pic->contrast = decoder->contrast; + pic->sat = decoder->sat; + pic->hue = decoder->hue; + } + break; + + case DECODER_SET_PICTURE: + { + struct saa7114 *pic = arg; + + dprintk(1, + KERN_DEBUG + "%s: decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n", + client->name, pic->bright, pic->contrast, + pic->sat, pic->hue); + + if (decoder->bright != pic->bright) { + /* We want 0 to 255 */ + if (pic->bright < 0 || pic->bright > 255) { + dprintk(0, + KERN_ERR + "%s: invalid brightness setting %d", + client->name, pic->bright); + return -EINVAL; + } + decoder->bright = pic->bright; + saa7114_write(client, 0x0a, decoder->bright); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 */ + if (pic->contrast < 0 || pic->contrast > 127) { + dprintk(0, + KERN_ERR + "%s: invalid contrast setting %d", + client->name, pic->contrast); + return -EINVAL; + } + decoder->contrast = pic->contrast; + saa7114_write(client, 0x0b, decoder->contrast); + } + if (decoder->sat != pic->sat) { + /* We want 0 to 127 */ + if (pic->sat < 0 || pic->sat > 127) { + dprintk(0, + KERN_ERR + "%s: invalid saturation setting %d", + client->name, pic->sat); + return -EINVAL; + } + decoder->sat = pic->sat; + saa7114_write(client, 0x0c, decoder->sat); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 */ + if (pic->hue < -128 || pic->hue > 127) { + dprintk(0, + KERN_ERR + "%s: invalid hue setting %d", + client->name, pic->hue); + return -EINVAL; + } + decoder->hue = pic->hue; + saa7114_write(client, 0x0d, decoder->hue); + } + } + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = + { I2C_SAA7114 >> 1, I2C_SAA7114A >> 1, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int saa7114_i2c_id = 0; +struct i2c_driver i2c_driver_saa7114; + +static int +saa7114_detect_client(struct i2c_adapter *adapter, int address, int kind) +{ +// int i, err[30]; +// short int hoff = SAA_7114_NTSC_HOFFSET; +// short int voff = SAA_7114_NTSC_VOFFSET; +// short int w = SAA_7114_NTSC_WIDTH; +// short int h = SAA_7114_NTSC_HEIGHT; + struct i2c_client *client; + struct saa7114 *decoder; + + dprintk(1, + KERN_INFO + "saa7114.c: detecting saa7114 client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + memset(client, 0, sizeof(struct i2c_client)); + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7114; + client->flags = I2C_CLIENT_ALLOW_USE; + client->id = saa7114_i2c_id++; + snprintf(client->name, sizeof(client->name) - 1, "saa7115[%d]", + client->id); + + decoder = kmalloc(sizeof(struct saa7114), GFP_KERNEL); + i2c_set_clientdata(client, decoder); + if (decoder == NULL) { + kfree(client); + return -ENOMEM; + } + memset(decoder, 0, sizeof(struct saa7114)); + decoder->norm = VIDEO_MODE_NTSC; + decoder->input = -1; + decoder->enable = 1; + decoder->bright = 128; + decoder->contrast = 64; + decoder->hue = 0; + decoder->sat = 64; + decoder->playback = 0; // initially capture mode used + decoder->audio = DECODER_AUDIO_48_KHZ; + + dprintk(1, KERN_INFO "saa7115.c: writing init values\n"); + + /* init to NTSC/48khz */ + writeregs(client, init_saa7115_auto_input); + writeregs(client, init_saa7115_misc); + writeregs(client, cfg_saa7115_NTSC_fullres_x); + writeregs(client, cfg_saa7115_NTSC_fullres_y); + writeregs(client, cfg_saa7115_NTSC_video); + writeregs(client, cfg_saa7115_48_audio); + writeregs(client, cfg_saa7115_NTSC_48_audio); + writeregs(client, cfg_saa7115_reset_scaler); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2 * HZ); + + printk("status: (1E) 0x%02x, (1F) 0x%02x\n", + readreg(client, 0x1e), readreg(client, 0x1f)); + + i2c_attach_client(client); + + return 0; +} + +static int saa7114_attach_adapter(struct i2c_adapter *adapter) +{ + dprintk(1, + KERN_INFO + "saa7114.c: starting probe for adapter %s (0x%x)\n", + adapter->name, adapter->id); + return i2c_probe(adapter, &addr_data, &saa7114_detect_client); +} + +static int saa7114_detach_client(struct i2c_client *client) +{ + struct saa7114 *decoder = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(decoder); + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +struct i2c_driver i2c_driver_saa7114 = { + .name = "saa7115", + + .id = I2C_DRIVERID_SAA7114, + .flags = I2C_DF_NOTIFY, + + .attach_adapter = saa7114_attach_adapter, + .detach_client = saa7114_detach_client, + .command = saa7114_command, + .owner = THIS_MODULE, +}; + +static int __init saa7114_init(void) +{ + return i2c_add_driver(&i2c_driver_saa7114); +} + +static void __exit saa7114_exit(void) +{ + i2c_del_driver(&i2c_driver_saa7114); +} + +module_init(saa7114_init); +module_exit(saa7114_exit); diff -upN /dev/null current/drivers/media/video/saa7127.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/saa7127.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,854 @@ +/* + * saa7127 - Philips SAA7127 video encoder driver version 0.2 + * + * Copyright (C) 2003 Roy Bulter + * + * Based on SAA7126 video encoder driver by Gillem & Andreas Oberritter + * + * Copyright (C) 2000-2001 Gillem + * Copyright (C) 2002 Andreas Oberritter + * + * Based on Stadis 4:2:2 MPEG-2 Decoder Driver by Nathan Laredo + * + * Copyright (C) 1999 Nathan Laredo + * + * This driver is designed for the Hauppauge 250/350 Linux driver + * designed by the Ivytv Project (ivtv.sf.net) + * + * Copyright (C) 2003 Kevin Thayer + * + * 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. + */ + +/* + * Revision History + * + * + * Revision : 0.1 (09-05-2003) + * Change : First Version + * + * Revision : 0.2 (21-05-2003) + * Change : solved compiler error on line 785(800) + * reg61h variable was not set in saa7127_set_norm function + * + * Revision : 0.3 (21-07-2003) Matt T. Yourst + * Change : Update configuration tables to make NTSC appear correctly; + * Enable alternative outputs (s-video, composite, RGB, etc.) + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include "saa7127.h" + +/* + ********************************************************************** + * Debug Macro's + * + * + ********************************************************************** + */ + +#define CURRENT_MASK 3 +#define NO_MASK 0 +#define INFO_MASK 1 /* 0b0000000000001 */ +#define ERROR_MASK 2 /* 0b0000000000010 */ +#define ENTER_MASK 4 /* 0b0000000000100 */ +#define RETURN_MASK 8 /* 0b0000000001000 */ +#define TRACE1_MASK 16 /* 0b0000000010000 */ +#define TRACE2_MASK 32 /* 0b0000000100000 */ +#define TRACE3_MASK 64 /* 0b0000001000000 */ +#define TRACE4_MASK 128 /* 0b0000010000000 */ +#define TRACE5_MASK 256 /* 0b0000100000000 */ +#define TRACE6_MASK 512 /* 0b0001000000000 */ +#define TRACE7_MASK 1024 /* 0b0010000000000 */ +#define TRACE8_MASK 2048 /* 0b0100000000000 */ +#define TRACE9_MASK 4096 /* 0b1000000000000 */ + +static int debug_mask = CURRENT_MASK; +static int test_image = 0; +static int pal = 0; +static int enable_output = 0; +static int output_select = SAA7127_OUTPUT_TYPE_SVIDEO; + +#define INFO(format, args...)\ + if ((debug_mask&INFO_MASK) == INFO_MASK)\ + {\ + printk("[%s: INFO]: ", __FILE__);\ + printk(format, ##args);\ + printk("\n");\ + }\ + +#define ERROR(format, args...)\ + if ((debug_mask&ERROR_MASK) == ERROR_MASK)\ + {\ + printk("[%s: %d: ERROR]: ", __FILE__,__LINE__);\ + printk(format, ##args);\ + printk("\n");\ + }\ + + +#ifdef DEBUG + +#define ENTER\ + if ((debug_mask&ENTER_MASK) == ENTER_MASK)\ + {\ + printk("[%s : %s: %d: ENTER]: ",__FILE__,__FUNCTION__,__LINE__);\ + printk("\n");\ + }\ + +#define RETURN(value)\ + if ((debug_mask&RETURN_MASK) == RETURN_MASK)\ + {\ + printk("[%s : %s : %d: RETURN]: ", __FILE__,__FUNCTION__,__LINE__);\ + printk("value: %x",value);\ + printk("\n");\ + }\ + return(value);\ + +static int get_trace_mask(int num) +{ + switch (num) { + case 1: + return TRACE1_MASK; + break; + case 2: + return TRACE2_MASK; + break; + case 3: + return TRACE3_MASK; + break; + case 4: + return TRACE4_MASK; + break; + case 5: + return TRACE5_MASK; + break; + case 6: + return TRACE6_MASK; + break; + case 7: + return TRACE7_MASK; + break; + case 8: + return TRACE8_MASK; + break; + case 9: + return TRACE9_MASK; + break; + default: + return NO_MASK; + } +} + +#define TRACE(num, format, args...) \ + if ((debug_mask&get_trace_mask(num)) == get_trace_mask(num)) \ + {\ + printk("[%s: %d: TRACE%d] ", __FILE__, __LINE__,num);\ + printk(format, ##args);\ + printk("\n");\ + }\ + +#else + +#define ENTER +#define RETURN(value) return(value); +#define TRACE(num, format, args...) + +#endif /* DEBUG */ + +/* + ********************************************************************** + * + * Array's with configuration parameters for the SAA7127 + * + ********************************************************************** + */ + +struct i2c_reg_value { + unsigned char reg; + unsigned char value; +}; + +struct i2c_reg_value saa7127_init_config_common[] = { + {SAA7127_REG_WIDESCREEN_CONFIG, 0x0d}, + {SAA7127_REG_WIDESCREEN_ENABLE, 0x00}, + {SAA7127_REG_COPYGEN_0, 0x77}, + {SAA7127_REG_COPYGEN_1, 0x41}, + {SAA7127_REG_COPYGEN_2, 0x00}, // (Macrovision enable/disable) + {SAA7127_REG_OUTPUT_PORT_CONTROL, 0x9e}, + {SAA7127_REG_GAIN_LUMINANCE_RGB, 0x00}, + {SAA7127_REG_GAIN_COLORDIFF_RGB, 0x00}, + {SAA7127_REG_INPUT_PORT_CONTROL_1, 0x80}, // (for color bars) + {SAA7127_REG_LINE_21_ODD_0, 0x77}, + {SAA7127_REG_LINE_21_ODD_1, 0x41}, + {SAA7127_REG_LINE_21_EVEN_0, 0x88}, + {SAA7127_REG_LINE_21_EVEN_1, 0x41}, + {SAA7127_REG_RCV_PORT_CONTROL, 0x12}, + {SAA7127_REG_VTRIG, 0xf9}, + {SAA7127_REG_HTRIG_HI, 0x00}, + {SAA7127_REG_RCV2_OUTPUT_START, 0x41}, + {SAA7127_REG_RCV2_OUTPUT_END, 0xc3}, + {SAA7127_REG_RCV2_OUTPUT_MSBS, 0x00}, + {SAA7127_REG_TTX_REQUEST_H_START, 0x3e}, + {SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH, 0xb8}, + {SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT, 0x03}, + {SAA7127_REG_TTX_ODD_REQ_VERT_START, 0x15}, + {SAA7127_REG_TTX_ODD_REQ_VERT_END, 0x16}, + {SAA7127_REG_TTX_EVEN_REQ_VERT_START, 0x15}, + {SAA7127_REG_TTX_EVEN_REQ_VERT_END, 0x16}, + {SAA7127_REG_FIRST_ACTIVE, 0x1a}, + {SAA7127_REG_LAST_ACTIVE, 0x01}, + {SAA7127_REG_MSB_VERTICAL, 0xc0}, + {SAA7127_REG_DISABLE_TTX_LINE_LO_0, 0x00}, + {SAA7127_REG_DISABLE_TTX_LINE_LO_1, 0x00}, + {0, 0} +}; + +#define SAA7127_NTSC_DAC_CONTROL 0x05 +struct i2c_reg_value saa7127_init_config_ntsc[] = { + {SAA7127_REG_BURST_START, 0x19}, + {SAA7127_REG_BURST_END, 0x1d}, + {SAA7127_REG_CHROMA_PHASE, 0x27}, + {SAA7127_REG_GAINU, 0x88}, + {SAA7127_REG_GAINV, 0xc0}, + {SAA7127_REG_BLACK_LEVEL, 0x3f}, + {SAA7127_REG_BLANKING_LEVEL, 0x36}, + {SAA7127_REG_VBI_BLANKING, 0x36}, + {SAA7127_REG_DAC_CONTROL, 0x05}, + {SAA7127_REG_BURST_AMP, 0x4a}, + {SAA7127_REG_SUBC3, 0x1f}, + {SAA7127_REG_SUBC2, 0x7c}, + {SAA7127_REG_SUBC1, 0xf0}, + {SAA7127_REG_SUBC0, 0x21}, + {SAA7127_REG_MULTI, 0x90}, + {SAA7127_REG_CLOSED_CAPTION, 0x14}, + {0, 0} +}; + +#define SAA7127_PAL_DAC_CONTROL 0x02 +struct i2c_reg_value saa7127_init_config_pal[] = { + {SAA7127_REG_BURST_START, 0x21}, + {SAA7127_REG_BURST_END, 0x1d}, + {SAA7127_REG_CHROMA_PHASE, 0x3f}, + {SAA7127_REG_GAINU, 0x7d}, + {SAA7127_REG_GAINV, 0xaf}, + {SAA7127_REG_BLACK_LEVEL, 0x23}, + {SAA7127_REG_BLANKING_LEVEL, 0x35}, + {SAA7127_REG_VBI_BLANKING, 0x35}, + {SAA7127_REG_DAC_CONTROL, 0x02}, + {SAA7127_REG_BURST_AMP, 0x2f}, + {SAA7127_REG_SUBC3, 0xcb}, + {SAA7127_REG_SUBC2, 0x8a}, + {SAA7127_REG_SUBC1, 0x09}, + {SAA7127_REG_SUBC0, 0x2a}, + {SAA7127_REG_MULTI, 0xa0}, + {SAA7127_REG_CLOSED_CAPTION, 0x00}, + {0, 0} +}; + +/* + ********************************************************************** + * + * Encoder Struct, holds the configuration state of the encoder + * + ********************************************************************** + */ + +struct saa7127 { + enum SAA7127_video_norm norm; + enum SAA7127_input_type input_type; + enum SAA7127_output_type output_type; + enum SAA7127_enable_type enable; + enum SAA7127_wss_enable_type wss_enable; + enum SAA7127_wss_mode_type wss_mode; + u8 reg_2d; + u8 reg_3a; + u8 reg_61; +}; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_read(struct i2c_client *client, u8 reg) +{ + ENTER; + RETURN(i2c_smbus_read_byte_data(client, reg)); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_writereg(struct i2c_client *client, u8 reg, u8 val) +{ + ENTER; + if (i2c_smbus_write_byte_data(client, reg, val) < 0) { + ERROR("I2C Write Problem"); + return (-1); + } + TRACE(4, "I2C Write to reg: %x, data: %x", reg, val); + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_write_inittab(struct i2c_client *client, + const struct i2c_reg_value *regs) +{ + ENTER; + + while (regs->reg != 0) { + if (i2c_smbus_write_byte_data(client, regs->reg, regs->value) < + 0) { + ERROR("I2C Write Problem"); + RETURN(-1); + } + TRACE(4, "I2C Write to reg: %x, data: %x", regs->reg, + regs->value); + regs++; + } + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_wss(struct i2c_client *client) +{ + struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client); + + ENTER; + switch (encoder->wss_enable) { + case SAA7127_WSS_DISABLE: + TRACE(3, "Disable Wide Screen Signal"); + saa7127_writereg(client, 0x27, 0x00); + break; + case SAA7127_WSS_ENABLE: + TRACE(3, "Enable Wide Screen Signal"); + saa7127_writereg(client, 0x27, 0x80); + break; + default: + return (-EINVAL); + } + return (0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_wss_mode(struct i2c_client *client) +{ + struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client); + + ENTER; + + switch (encoder->wss_mode) { + case SAA7127_WSS_MODE_4_3_FULL_FORMAT: + TRACE(3, "Widescreen Mode 4:3 Full Format"); + saa7127_writereg(client, 0x26, 0x08); + break; + case SAA7127_WSS_MODE_BOX_14_9_C: + TRACE(3, "Widescreen Mode Box 14:9 Center"); + saa7127_writereg(client, 0x26, 0x01); + break; + case SAA7127_WSS_MODE_BOX_14_9_TOP: + TRACE(3, "Widescreen Mode Box 14:9 Top"); + saa7127_writereg(client, 0x26, 0x02); + break; + case SAA7127_WSS_MODE_BOX_16_9_C: + TRACE(3, "Widescreen Mode Box 16:9 Center"); + saa7127_writereg(client, 0x26, 0x0b); + break; + case SAA7127_WSS_MODE_BOX_16_9_TOP: + TRACE(3, "Widescreen Mode Box 16:9 Top"); + saa7127_writereg(client, 0x26, 0x04); + break; + case SAA7127_WSS_MODE_SMALL_BOX_16_9_C: + TRACE(3, "Widescreen Mode Small Box 16:9 Center"); + saa7127_writereg(client, 0x26, 0x0d); + break; + case SAA7127_WSS_MODE_4_3_14_9_FULL_FORMAT: + TRACE(3, "Widescreen Mode 14:9 Full Format"); + saa7127_writereg(client, 0x26, 0x0e); + break; + case SAA7127_WSS_MODE_16_9_ANAMORPHIC: + TRACE(3, "Widescreen Mode 16:9 Full Format"); + saa7127_writereg(client, 0x26, 0x07); + break; + default: + RETURN(-EINVAL); + } + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_enable(struct i2c_client *client) +{ + struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client); + + ENTER; + + switch (encoder->enable) { + case SAA7127_DISABLE: + TRACE(3, "Disable Video Output"); + saa7127_writereg(client, 0x2d, (encoder->reg_2d & 0xf0)); + saa7127_writereg(client, 0x61, (encoder->reg_61 | 0xc0)); + break; + case SAA7127_ENABLE: + TRACE(3, "Enable Video Output"); + saa7127_writereg(client, 0x2d, encoder->reg_2d); + saa7127_writereg(client, 0x61, encoder->reg_61); + break; + default: + RETURN(-EINVAL); + } + +#if 0 + int j; + for (j = 0; j < 128 / 16; j++) { + TRACE(3, + "saa7127 registers 0x%02x-0x%02x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + j * 16, j * 16 + 15, saa7127_read(client, j * 16 + 0), + saa7127_read(client, j * 16 + 1), saa7127_read(client, + j * 16 + + 2), + saa7127_read(client, j * 16 + 3), saa7127_read(client, + j * 16 + + 4), + saa7127_read(client, j * 16 + 5), saa7127_read(client, + j * 16 + + 6), + saa7127_read(client, j * 16 + 7), saa7127_read(client, + j * 16 + + 8), + saa7127_read(client, j * 16 + 9), saa7127_read(client, + j * 16 + + 10), + saa7127_read(client, j * 16 + 11), saa7127_read(client, + j * 16 + + 12), + saa7127_read(client, j * 16 + 13), saa7127_read(client, + j * 16 + + 14), + saa7127_read(client, j * 16 + 15)); + } +#endif + + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_norm(struct i2c_client *client) +{ + struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client); + const struct i2c_reg_value *inittab; + + ENTER; + + switch (encoder->norm) { + case SAA7127_VIDEO_NORM_NTSC: + TRACE(3, "Selecting NTSC video Standard"); + inittab = saa7127_init_config_ntsc; + encoder->reg_61 = SAA7127_NTSC_DAC_CONTROL; + break; + case SAA7127_VIDEO_NORM_PAL: + TRACE(3, "Selecting PAL video Standard"); + inittab = saa7127_init_config_pal; + encoder->reg_61 = SAA7127_PAL_DAC_CONTROL; + break; + default: + RETURN(-EINVAL); + } + + /* Write Table */ + saa7127_write_inittab(client, inittab); + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_set_output_type(struct i2c_client *client) +{ + struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client); + + ENTER; + + encoder->reg_3a = 0x13; // by default swithch YUV to RGB-matrix on + + switch (encoder->output_type) { + case SAA7127_OUTPUT_TYPE_RGB: + TRACE(3, "Selecting RGB Output Type"); + encoder->reg_2d = 0x0f; // RGB + CVBS (for sync) + break; + case SAA7127_OUTPUT_TYPE_COMPOSITE: + TRACE(3, "Selecting Composite Output Type"); + encoder->reg_2d = 0x08; // 00001000 CVBS only, RGB DAC's off (high impedance mode) !!! + break; + case SAA7127_OUTPUT_TYPE_SVIDEO: + TRACE(3, "Selecting S-Video Output Type"); + encoder->reg_2d = 0xff; // 11111111 croma -> R, luma -> CVBS + G + B + break; + case SAA7127_OUTPUT_TYPE_YUV_V: + TRACE(3, "Selecting YUV V Output Type"); + encoder->reg_2d = 0x4f; // reg 2D = 01001111, all DAC's on, RGB + VBS + encoder->reg_3a = 0x0b; // reg 3A = 00001011, bypass RGB-matrix + break; + case SAA7127_OUTPUT_TYPE_YUV_C: + TRACE(3, "Selecting YUV C Output Type"); + encoder->reg_2d = 0x0f; // reg 2D = 00001111, all DAC's on, RGB + CVBS + encoder->reg_3a = 0x0b; // reg 3A = 00001011, bypass RGB-matrix + break; + default: + RETURN(-EINVAL); + } + + /* Configure Encoder */ + + saa7127_writereg(client, 0x2d, encoder->reg_2d); + saa7127_writereg(client, 0x3a, + (encoder->input_type == + SAA7127_INPUT_TYPE_TEST_IMAGE) ? 0x80 : encoder-> + reg_3a); + + RETURN(0); +} + +static int saa7127_set_input_type(struct i2c_client *client) +{ + struct saa7127 *encoder = (struct saa7127 *)i2c_get_clientdata(client); + + ENTER; + + switch (encoder->input_type) { + case SAA7127_INPUT_TYPE_NORMAL: /* avia */ + TRACE(3, "Selecting Normal Encoder Input"); + saa7127_writereg(client, 0x3a, encoder->reg_3a); + break; + case SAA7127_INPUT_TYPE_TEST_IMAGE: /* color bar */ + TRACE(3, "Selecting Colour Bar generator"); + saa7127_writereg(client, 0x3a, 0x80); + break; + default: + RETURN(-EINVAL); + } + + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_command(struct i2c_client *client, + unsigned int cmd, void *parg) +{ + struct saa7127 *encoder = i2c_get_clientdata(client); + unsigned long arg = (unsigned long)parg; + struct video_encoder_capability *cap = parg; + ENTER; + printk("saa7127_command: entered with cmd = %d\n", cmd); + + switch (cmd) { + case ENCODER_GET_CAPABILITIES: + TRACE(3, "Asking Encoder Capabilities"); + cap->flags = VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC; + cap->inputs = 1; + cap->outputs = 1; + break; + + case ENCODER_SET_NORM: + TRACE(3, "Setting Encoder Video Standard"); + switch (arg) { + case VIDEO_MODE_NTSC: + TRACE(3, "Set NTSC Video Mode"); + encoder->norm = SAA7127_VIDEO_NORM_NTSC; + break; + case VIDEO_MODE_PAL: + TRACE(3, "Set PAL Video Mode"); + encoder->norm = SAA7127_VIDEO_NORM_PAL; + break; + default: + return (-EINVAL); + } + saa7127_set_norm(client); + break; + + case ENCODER_SET_INPUT: + TRACE(3, "Setting Encoder Input"); + switch (arg) { + case SAA7127_INPUT_NORMAL: /* encoder input selected */ + TRACE(3, "Select Normal input"); + encoder->input_type = SAA7127_INPUT_TYPE_NORMAL; + break; + case SAA7127_INPUT_TESTIMAGE: /* Internal colourbars selected */ + TRACE(3, "Select ColourBar Generator"); + encoder->input_type = SAA7127_INPUT_TYPE_TEST_IMAGE; + break; + default: + RETURN(-EINVAL); + } + saa7127_set_input_type(client); + break; + + case ENCODER_SET_OUTPUT: + TRACE(3, "Setting Encoder Output"); + encoder->output_type = arg; + saa7127_set_output_type(client); + break; + + case ENCODER_ENABLE_OUTPUT: + TRACE(3, "Turn on/off Output"); + switch (arg) { + case SAA7127_VIDEO_ENABLE: + TRACE(3, "Turn on Video Output"); + encoder->enable = SAA7127_ENABLE; + break; + case SAA7127_VIDEO_DISABLE: + TRACE(3, "Turn off Video Output"); + encoder->enable = SAA7127_DISABLE; + break; + default: + RETURN(-EINVAL); + } + saa7127_set_enable(client); + break; + + default: + RETURN(-EINVAL); + } + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static unsigned short normal_i2c[] = + { I2C_SAA7127_ADRESS >> 1, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int saa7127_i2c_id = 0; +struct i2c_driver i2c_driver_saa7127; + +/* ----------------------------------------------------------------------- */ + +static int saa7127_detect_client(struct i2c_adapter *adapter, + int address, int kind) +{ + struct i2c_client *client; + struct saa7127 *encoder; + int read_result = 0; + + ENTER; + + TRACE(1, "detecting saa7127 client on address 0x%x", address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return (0); + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return (-ENOMEM); + + memset(client, 0, sizeof(struct i2c_client)); + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_saa7127; + client->flags = I2C_CLIENT_ALLOW_USE; + client->id = saa7127_i2c_id++; + snprintf(client->name, sizeof(client->name) - 1, "saa7127[%d]", + client->id); + + encoder = kmalloc(sizeof(struct saa7127), GFP_KERNEL); + + if (encoder == NULL) { + kfree(client); + return (-ENOMEM); + } + + i2c_set_clientdata(client, encoder); + memset(encoder, 0, sizeof(struct saa7127)); + + /* Initialize default values */ + encoder->output_type = output_select; + encoder->wss_enable = SAA7127_WSS_DISABLE; + encoder->wss_mode = SAA7127_WSS_MODE_4_3_FULL_FORMAT; + + /* Look if the pal module parameter is set */ + + if (pal == 1) { + /* Select PAL Video Standard */ + encoder->norm = SAA7127_VIDEO_NORM_PAL; + } else { + /* Select NTSC Video Standard, default */ + encoder->norm = SAA7127_VIDEO_NORM_NTSC; + } + + /* Look if the Encoder needs to be enabled */ + + if (enable_output == 1) { + encoder->enable = SAA7127_ENABLE; + } else { + /* for default disable output */ + /* Because the MPEG DECODER is not initialised */ + encoder->enable = SAA7127_DISABLE; + } + + /* The Encoder is does have internal Colourbar generator */ + /* This can be used for debugging, configuration values for the encoder */ + + if (test_image == 1) { + /* Select ColourBar Generator */ + encoder->input_type = SAA7127_INPUT_TYPE_TEST_IMAGE; + } else { + /* Select normal input */ + encoder->input_type = SAA7127_INPUT_TYPE_NORMAL; + } + + TRACE(2, "writing init values"); + + /* Configure Encoder */ + + printk("saa7127: Configuring encoder..."); + saa7127_write_inittab(client, saa7127_init_config_common); + saa7127_set_norm(client); + saa7127_set_output_type(client); + saa7127_set_wss(client); + saa7127_set_wss_mode(client); + saa7127_set_input_type(client); + saa7127_set_enable(client); + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2 * HZ); + + read_result = saa7127_read(client, 0x00); + + TRACE(4, "Read status register (00h) : 0x%02x ", read_result); + + i2c_attach_client(client); + + RETURN(0); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_attach_adapter(struct i2c_adapter *adapter) +{ + + TRACE(2, "starting probe for adapter %s (0x%x)", adapter->name, + adapter->id); + return (i2c_probe(adapter, &addr_data, &saa7127_detect_client)); +} + +/* ----------------------------------------------------------------------- */ + +static int saa7127_detach_client(struct i2c_client *client) +{ + struct saa7127 *encoder = i2c_get_clientdata(client); + int err; + + /* Turn off TV output */ + + encoder->enable = SAA7127_DISABLE; + saa7127_set_enable(client); + + err = i2c_detach_client(client); + + if (err) { + return (err); + } + + kfree(encoder); + kfree(client); + return (0); +} + +/* ----------------------------------------------------------------------- */ + +struct i2c_driver i2c_driver_saa7127 = { + .name = "saa7127", + .id = I2C_DRIVERID_SAA7127, + .flags = I2C_DF_NOTIFY, + .attach_adapter = saa7127_attach_adapter, + .detach_client = saa7127_detach_client, + .command = saa7127_command, + .owner = THIS_MODULE, +}; + +/* ----------------------------------------------------------------------- */ + +static int __init saa7127_init(void) +{ + INFO("SAA7127 video encoder driver loaded"); + TRACE(1, "Driver version: V %s", SAA7127_DRIVER_VERSION); + return (i2c_add_driver(&i2c_driver_saa7127)); +} + +/* ----------------------------------------------------------------------- */ + +static void __exit saa7127_exit(void) +{ + + INFO("SAA7127 video encoder driver unloaded"); + i2c_del_driver(&i2c_driver_saa7127); +} + +/* ----------------------------------------------------------------------- */ + +module_init(saa7127_init); +module_exit(saa7127_exit); + +MODULE_DESCRIPTION("Philips SAA7127 video encoder driver"); +MODULE_AUTHOR("Roy Bulter"); +MODULE_LICENSE("GPL"); +MODULE_PARM(debug_mask, "i"); +MODULE_PARM(test_image, "i"); +MODULE_PARM(pal, "i"); +MODULE_PARM(enable_output, "i"); +MODULE_PARM(output_select, "i"); +MODULE_PARM_DESC(debug_mask, "debug_mask (0-8192) "); +MODULE_PARM_DESC(test_image, "test_image (0-1) "); +MODULE_PARM_DESC(pal, "pal (0-1) "); +MODULE_PARM_DESC(enable_output, "enable_output (0-1) "); +MODULE_PARM_DESC(output_select, + "output_select (0 = composite, 1 = s-video, 2 = rgb, 3 = YUVc, 4 = YUVv)"); diff -upN /dev/null current/drivers/media/video/saa7127.h --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/saa7127.h Thu Jun 17 12:58:30 2004 @@ -0,0 +1,154 @@ +#ifndef _SAA7127_H +#define _SAA7127_H + +/* + ********************************************************************** + * + * Define's + * + * + ********************************************************************** + */ + + +#define SAA7127_DRIVER_VERSION "0.3" + + +#ifndef I2C_DRIVERID_SAA7127 + #warning Using temporary hack for missing I2C driver-ID for saa7127 + #define I2C_DRIVERID_SAA7127 I2C_DRIVERID_EXP2 +#endif + + +#define I2C_SAA7127_ADRESS 0x88 + +#define SAA7127_VIDEO_ENABLE 0x01 +#define SAA7127_VIDEO_DISABLE 0x00 + +#define SAA7127_INPUT_TESTIMAGE 0x01 +#define SAA7127_INPUT_NORMAL 0x00 + +/* + * SAA7127 registers + */ + +#define SAA7127_REG_STATUS 0x00 +/* (registers 0x01-0x25 unused.) */ +#define SAA7127_REG_WIDESCREEN_CONFIG 0x26 +#define SAA7127_REG_WIDESCREEN_ENABLE 0x27 +#define SAA7127_REG_BURST_START 0x28 +#define SAA7127_REG_BURST_END 0x29 +#define SAA7127_REG_COPYGEN_0 0x2a +#define SAA7127_REG_COPYGEN_1 0x2b +#define SAA7127_REG_COPYGEN_2 0x2c +#define SAA7127_REG_OUTPUT_PORT_CONTROL 0x2d +/* (registers 0x2e-0x37 unused.) */ +#define SAA7127_REG_GAIN_LUMINANCE_RGB 0x38 +#define SAA7127_REG_GAIN_COLORDIFF_RGB 0x39 +#define SAA7127_REG_INPUT_PORT_CONTROL_1 0x3A +/* (registers 0x3b-0x53 undefined) */ +#define SAA7127_REG_CHROMA_PHASE 0x5A +#define SAA7127_REG_GAINU 0x5B +#define SAA7127_REG_GAINV 0x5C +#define SAA7127_REG_BLACK_LEVEL 0x5D +#define SAA7127_REG_BLANKING_LEVEL 0x5E +#define SAA7127_REG_VBI_BLANKING 0x5F +/* (register 0x60 unused) */ +#define SAA7127_REG_DAC_CONTROL 0x61 +#define SAA7127_REG_BURST_AMP 0x62 +#define SAA7127_REG_SUBC3 0x63 +#define SAA7127_REG_SUBC2 0x64 +#define SAA7127_REG_SUBC1 0x65 +#define SAA7127_REG_SUBC0 0x66 +#define SAA7127_REG_LINE_21_ODD_0 0x67 +#define SAA7127_REG_LINE_21_ODD_1 0x68 +#define SAA7127_REG_LINE_21_EVEN_0 0x69 +#define SAA7127_REG_LINE_21_EVEN_1 0x6A +#define SAA7127_REG_RCV_PORT_CONTROL 0x6B +#define SAA7127_REG_VTRIG 0x6C +#define SAA7127_REG_HTRIG_HI 0x6D +#define SAA7127_REG_MULTI 0x6E +#define SAA7127_REG_CLOSED_CAPTION 0x6F +#define SAA7127_REG_RCV2_OUTPUT_START 0x70 +#define SAA7127_REG_RCV2_OUTPUT_END 0x71 +#define SAA7127_REG_RCV2_OUTPUT_MSBS 0x72 +#define SAA7127_REG_TTX_REQUEST_H_START 0x73 +#define SAA7127_REG_TTX_REQUEST_H_DELAY_LENGTH 0x74 +#define SAA7127_REG_CSYNC_ADVANCE_VSYNC_SHIFT 0x75 +#define SAA7127_REG_TTX_ODD_REQ_VERT_START 0x76 +#define SAA7127_REG_TTX_ODD_REQ_VERT_END 0x77 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_START 0x78 +#define SAA7127_REG_TTX_EVEN_REQ_VERT_END 0x79 +#define SAA7127_REG_FIRST_ACTIVE 0x7A +#define SAA7127_REG_LAST_ACTIVE 0x7B +#define SAA7127_REG_MSB_VERTICAL 0x7C +/* (register 0x7d unused) */ +#define SAA7127_REG_DISABLE_TTX_LINE_LO_0 0x7E +#define SAA7127_REG_DISABLE_TTX_LINE_LO_1 0x7F + + + +/* + ********************************************************************** + * + * Enumurations + * + ********************************************************************** + */ + + +/* Enumeration for the Video Standard */ + + +enum SAA7127_video_norm { + SAA7127_VIDEO_NORM_NTSC, + SAA7127_VIDEO_NORM_PAL + }; + + +/* Enumeration for the Supported input types */ + +enum SAA7127_input_type { + SAA7127_INPUT_TYPE_NORMAL, + SAA7127_INPUT_TYPE_TEST_IMAGE + }; + + +/* Enumeration for the Supported Output signal types */ + +enum SAA7127_output_type { + SAA7127_OUTPUT_TYPE_COMPOSITE, + SAA7127_OUTPUT_TYPE_SVIDEO, + SAA7127_OUTPUT_TYPE_RGB, + SAA7127_OUTPUT_TYPE_YUV_C, + SAA7127_OUTPUT_TYPE_YUV_V + }; + +/* Enumeration for the enable/disabeling the output signal */ + +enum SAA7127_enable_type { + SAA7127_DISABLE, + SAA7127_ENABLE + }; +/* Enumeration for the turning on/off the Wide screen signal for Wide screen TV */ + +enum SAA7127_wss_enable_type { + SAA7127_WSS_DISABLE, + SAA7127_WSS_ENABLE + }; + +/* Enumeration for the selecting the different Wide screen mode */ + +enum SAA7127_wss_mode_type { + SAA7127_WSS_MODE_4_3_FULL_FORMAT, /* full format 4:3 */ + SAA7127_WSS_MODE_BOX_14_9_C, /* box 14:9 c */ + SAA7127_WSS_MODE_BOX_14_9_TOP, /* box 14:9 top */ + SAA7127_WSS_MODE_BOX_16_9_C, /* box 16:9 c */ + SAA7127_WSS_MODE_BOX_16_9_TOP, /* box 16:9 top */ + SAA7127_WSS_MODE_SMALL_BOX_16_9_C, /* box > 16:9 c */ + SAA7127_WSS_MODE_4_3_14_9_FULL_FORMAT, /* full format 4:3 with 14:9 c letterbox content */ + SAA7127_WSS_MODE_16_9_ANAMORPHIC /* full format 16:9 (anamorphic) */ + }; + + +#endif // _SAA7127_H diff -upN /dev/null current/drivers/media/video/tveeprom.c --- /dev/null Tue Mar 30 06:13:35 2004 +++ current/drivers/media/video/tveeprom.c Thu Jun 17 12:58:30 2004 @@ -0,0 +1,546 @@ +/* + * tveeprom - eeprom decoder for tvcard configuration eeproms + * + * Data and decoding routines shamelessly borrowed from bttv-cards.c + * eeprom access routine shamelessly borrowed from bttv-if.c + * which are: + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999-2001 Gerd Knorr + + * Adjustments to fit a more general model and all bugs: + + Copyright (C) 2003 John Klar + + * 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. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include + +MODULE_DESCRIPTION("i2c eeprom decoder driver"); +MODULE_AUTHOR("John Klar"); +MODULE_LICENSE("GPL"); + +#include +#include + +#include + +#ifndef I2C_DRIVERID_TVEEPROM +#warning Using temporary hack for missing I2C driver-ID for tveeprom +#define I2C_DRIVERID_TVEEPROM I2C_DRIVERID_EXP2 +#endif + +static int debug = 1; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +static int verbose = 0; +MODULE_PARM(verbose, "i"); +MODULE_PARM_DESC(verbose, "Verbose level (0-1)"); + +#define STRM(array,i) (i < sizeof(array)/sizeof(char*) ? array[i] : "unknown") + +#define dprintk(num, format, args...) \ + do { \ + if (debug >= num) \ + printk(format, ##args); \ + } while (0) + +/* ----------------------------------------------------------------------- */ + +static unsigned char eeprom_buf[256]; + +struct tveeprom { + u32 has_radio; + + u32 tuner_type; + u32 tuner_formats; + + u32 digitizer; + u32 digitizer_formats; + + u32 audio_processor; + /* a_p_fmts? */ + + u32 model; + u32 revision; + u32 serial_number; + char rev_str[5]; +}; + +#define I2C_TVEEPROM 0xA0 +#define I2C_TVEEPROMA 0xA0 + +#define I2C_DELAY 10 + +#define REG_ADDR(x) (((x) << 1) + 1) +#define LOBYTE(x) ((unsigned char)((x) & 0xff)) +#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff)) +#define LOWORD(x) ((unsigned short int)((x) & 0xffff)) +#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff)) + +/* ----------------------------------------------------------------------- */ +/* some hauppauge specific stuff */ + +static struct HAUPPAUGE_TUNER_FMT { + int id; + char *name; +} hauppauge_tuner_fmt[] __devinitdata = { + { + 0x00000000, "unknown1"}, { + 0x00000000, "unknown2"}, { + 0x00000007, "PAL(B/G)"}, { + 0x00001000, "NTSC(M)"}, { + 0x00000010, "PAL(I)"}, { + 0x00400000, "SECAM(L/Lī)"}, { + 0x00000e00, "PAL(D/K)"}, { +0x03000000, "ATSC Digital"},}; + +static struct HAUPPAUGE_TUNER { + int id; + char *name; +} hauppauge_tuner[] __devinitdata = { + { + TUNER_ABSENT, ""}, { + TUNER_ABSENT, "External"}, { + TUNER_ABSENT, "Unspecified"}, { + TUNER_PHILIPS_PAL, "Philips FI1216"}, { + TUNER_PHILIPS_SECAM, "Philips FI1216MF"}, { + TUNER_PHILIPS_NTSC, "Philips FI1236"}, { + TUNER_PHILIPS_PAL_I, "Philips FI1246"}, { + TUNER_PHILIPS_PAL_DK, "Philips FI1256"}, { + TUNER_PHILIPS_PAL, "Philips FI1216 MK2"}, { + TUNER_PHILIPS_SECAM, "Philips FI1216MF MK2"}, { + TUNER_PHILIPS_NTSC, "Philips FI1236 MK2"}, { + TUNER_PHILIPS_PAL_I, "Philips FI1246 MK2"}, { + TUNER_PHILIPS_PAL_DK, "Philips FI1256 MK2"}, { + TUNER_TEMIC_NTSC, "Temic 4032FY5"}, { + TUNER_TEMIC_PAL, "Temic 4002FH5"}, { + TUNER_TEMIC_PAL_I, "Temic 4062FY5"}, { + TUNER_PHILIPS_PAL, "Philips FR1216 MK2"}, { + TUNER_PHILIPS_SECAM, "Philips FR1216MF MK2"}, { + TUNER_PHILIPS_NTSC, "Philips FR1236 MK2"}, { + TUNER_PHILIPS_PAL_I, "Philips FR1246 MK2"}, { + TUNER_PHILIPS_PAL_DK, "Philips FR1256 MK2"}, { + TUNER_PHILIPS_PAL, "Philips FM1216"}, { + TUNER_PHILIPS_SECAM, "Philips FM1216MF"}, { + TUNER_PHILIPS_NTSC, "Philips FM1236"}, { + TUNER_PHILIPS_PAL_I, "Philips FM1246"}, { + TUNER_PHILIPS_PAL_DK, "Philips FM1256"}, { + TUNER_TEMIC_4036FY5_NTSC, "Temic 4036FY5"}, { + TUNER_ABSENT, "Samsung TCPN9082D"}, { + TUNER_ABSENT, "Samsung TCPM9092P"}, { + TUNER_TEMIC_4006FH5_PAL, "Temic 4006FH5"}, { + TUNER_ABSENT, "Samsung TCPN9085D"}, { + TUNER_ABSENT, "Samsung TCPB9085P"}, { + TUNER_ABSENT, "Samsung TCPL9091P"}, { + TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5"}, { + TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME"}, { + TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5"}, { + TUNER_ABSENT, "Philips TD1536"}, { + TUNER_ABSENT, "Philips TD1536D"}, { + TUNER_PHILIPS_NTSC, "Philips FMR1236"}, /* mono radio */ + { + TUNER_ABSENT, "Philips FI1256MP"}, { + TUNER_ABSENT, "Samsung TCPQ9091P"}, { + TUNER_TEMIC_4006FN5_MULTI_PAL, "Temic 4006FN5"}, { + TUNER_TEMIC_4009FR5_PAL, "Temic 4009FR5"}, { + TUNER_TEMIC_4046FM5, "Temic 4046FM5"}, { + TUNER_TEMIC_4009FN5_MULTI_PAL_FM, "Temic 4009FN5"}, { + TUNER_ABSENT, "Philips TD1536D_FH_44"}, { + TUNER_LG_NTSC_FM, "LG TP18NSR01F"}, { + TUNER_LG_PAL_FM, "LG TP18PSB01D"}, { + TUNER_LG_PAL, "LG TP18PSB11D"}, { + TUNER_LG_PAL_I_FM, "LG TAPC-I001D"}, { + TUNER_LG_PAL_I, "LG TAPC-I701D"} +}; + +static char *sndtype[] = { + "None", "TEA6300", "TEA6320", "TDA9850", "MSP3400C", "MSP3410D", + "MSP3415", "MSP3430", "MSP3438", "CS5331", "MSP3435", "MSP3440", + "MSP3445", "MSP3411", "MSP3416", "MSP3425", + + "Type 0x10", "Type 0x11", "Type 0x12", "Type 0x13", + "Type 0x14", "Type 0x15", "Type 0x16", "Type 0x17", + "Type 0x18", "MSP4418", "Type 0x1a", "MSP4448", + "Type 0x1c", "Type 0x1d", "Type 0x1e", "Type 0x1f", +}; + +static void __devinit hauppauge_eeprom(struct tveeprom *tvee, + unsigned char *eeprom_data) +{ + + /* ---------------------------------------------- + ** The hauppauge eeprom format is tagged + ** + ** if packet[0] == 0x84, then packet[0..1] == length + ** else length = packet[0] & 3f; + ** if packet[0] & f8 == f8, then EOD and packet[1] == checksum + ** + ** In our (ivtv) case we're interested in the following: + ** tuner type: tag [00].05 or [0a].01 (index into hauppauge_tuners) + ** tuner fmts: tag [00].04 or [0a].00 (bitmask index into hauppauge_fmts) + ** radio: tag [00].{last} or [0e].00 (bitmask. bit2=FM) + ** audio proc: tag [02].01 or [05].00 (lower nibble indexes lut?) + + ** Fun info: + ** model: tag [00].07-08 or [06].00-01 + ** revision: tag [00].09-0b or [06].04-06 + ** serial#: tag [01].05-07 or [04].04-06 + + ** # of inputs/outputs ??? + */ + + int i, j, len, done, tag, tuner = 0, t_format = 0; + char *t_name = NULL, *t_fmt_name = NULL; + + tvee->revision = done = len = 0; + for (i = 0; !done && i < 256; i += len) { + + dprintk(2, + KERN_INFO + "tvee: processing pos=%02x (%02x,%02x)\n", + i, eeprom_data[i], eeprom_data[i + 1]); + + if (eeprom_data[i] == 0x84) { + len = eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); + i += 3; + } else if ((eeprom_data[i] & 0xf0) == 0x70) { + if ((eeprom_data[i] & 0x08)) { + /* verify checksum! */ + done = 1; + break; + } + len = eeprom_data[i] & 0x07; + ++i; + } else { + printk(KERN_WARNING + "Encountered bad packet header [%02x]. " + "Corrupt or not a Hauppauge eeprom.\n", + eeprom_data[i]); + return; + } + + dprintk(1, KERN_INFO "%3d [%02x] ", len, eeprom_data[i]); + for (j = 1; j < len; j++) { + dprintk(1, "%02x ", eeprom_data[i + j]); + } + dprintk(1, "\n"); + + /* process by tag */ + tag = eeprom_data[i]; + switch (tag) { + case 0x00: + tuner = eeprom_data[i + 6]; + t_format = eeprom_data[i + 5]; + tvee->has_radio = eeprom_data[i + len - 1]; + tvee->model = + eeprom_data[i + 8] + (eeprom_data[i + 9] << 8); + tvee->revision = eeprom_data[i + 10] + + (eeprom_data[i + 11] << 8) + + (eeprom_data[i + 12] << 16); + break; + case 0x01: + tvee->serial_number = + eeprom_data[i + 6] + + (eeprom_data[i + 7] << 8) + + (eeprom_data[i + 8] << 16); + break; + case 0x02: + tvee->audio_processor = eeprom_data[i + 2] & 0x0f; + break; + case 0x04: + tvee->serial_number = + eeprom_data[i + 5] + + (eeprom_data[i + 6] << 8) + + (eeprom_data[i + 7] << 16); + break; + case 0x05: + tvee->audio_processor = eeprom_data[i + 1] & 0x0f; + break; + case 0x06: + tvee->model = + eeprom_data[i + 1] + (eeprom_data[i + 2] << 8); + tvee->revision = eeprom_data[i + 5] + + (eeprom_data[i + 6] << 8) + + (eeprom_data[i + 7] << 16); + break; + case 0x0a: + tuner = eeprom_data[i + 2]; + t_format = eeprom_data[i + 1]; + break; + case 0x0e: + tvee->has_radio = eeprom_data[i + 1]; + break; + default: + printk(KERN_WARNING + "Not sure what to do with tag [%02x]\n", tag); + /* dump the rest of the packet? */ + } + + } + + if (!done) { + printk(KERN_WARNING "Ran out of data!\n"); + return; + } + + if (tvee->revision != 0) { + tvee->rev_str[0] = 32 + ((tvee->revision >> 18) & 0x3f); + tvee->rev_str[1] = 32 + ((tvee->revision >> 12) & 0x3f); + tvee->rev_str[2] = 32 + ((tvee->revision >> 6) & 0x3f); + tvee->rev_str[3] = 32 + (tvee->revision & 0x3f); + tvee->rev_str[4] = 0; + } + + if (tuner < sizeof(hauppauge_tuner) / sizeof(struct HAUPPAUGE_TUNER)) { + tvee->tuner_type = hauppauge_tuner[tuner].id; + t_name = hauppauge_tuner[tuner].name; + } else { + t_name = ""; + } + + tvee->tuner_formats = 0; + t_fmt_name = ""; + for (i = 0; i < 8; i++) { + if ((t_format & (1 << i))) { + tvee->tuner_formats |= hauppauge_tuner_fmt[i].id; + /* yuck */ + t_fmt_name = hauppauge_tuner_fmt[i].name; + } + } + +#if 0 + if (t_format < + sizeof(hauppauge_tuner_fmt) / sizeof(struct HAUPPAUGE_TUNER_FMT)) { + tvee->tuner_formats = hauppauge_tuner_fmt[t_format].id; + t_fmt_name = hauppauge_tuner_fmt[t_format].name; + } else { + t_fmt_name = ""; + } +#endif + + printk(KERN_INFO "tvee: Hauppauge: model=%d, rev=%s, serial#=%d\n", + tvee->model, tvee->rev_str, tvee->serial_number); + printk(KERN_INFO "tvee: tuner=%s (idx=%d, type=%d)\n", + t_name, tuner, tvee->tuner_type); + printk(KERN_INFO "tvee: tuner fmt=%s (eeprom=0x%02x, v4l2=0x%08x)\n", + t_fmt_name, t_format, tvee->tuner_formats); + + printk(KERN_INFO "tvee: audio_processor=%s (type=%d)\n", + STRM(sndtype, tvee->audio_processor), tvee->audio_processor); + +} + +/* ----------------------------------------------------------------------- */ + +/* write I2C */ +int tvee_I2CWrite(struct i2c_client *client, + unsigned char b1, unsigned char b2, int both) +{ + unsigned char buffer[2]; + int bytes = both ? 2 : 1; + + buffer[0] = b1; + buffer[1] = b2; + if (bytes != i2c_master_send(client, buffer, bytes)) + return -1; + return 0; +} + +void __devinit tvee_readee(struct i2c_client *client, unsigned char *eedata) +{ + int i; + + if (tvee_I2CWrite(client, 0, -1, 0) < 0) { + printk(KERN_WARNING "tvee: readee error\n"); + return; + } + + for (i = 0; i < 256; i += 16) { + if (16 != i2c_master_recv(client, eedata + i, 16)) { + printk(KERN_WARNING "tvee: readee error\n"); + break; + } + } +} + +/* ----------------------------------------------------------------------- */ + +static int +tveeprom_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + + struct tveeprom *eeprom = i2c_get_clientdata(client); + u32 *eeprom_props = arg; + + switch (cmd) { + + case 0: + eeprom_props[0] = eeprom->tuner_type; + eeprom_props[1] = eeprom->tuner_formats; + eeprom_props[2] = eeprom->model; + eeprom_props[3] = eeprom->revision; + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ +static unsigned short normal_i2c[] = + { I2C_TVEEPROM >> 1, I2C_TVEEPROMA >> 1, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +static int tveeprom_i2c_id = 0; +struct i2c_driver i2c_driver_tveeprom; + +static int +tveeprom_detect_client(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct tveeprom *eeprom; + + dprintk(1, + KERN_INFO + "tveeprom.c: detecting tveeprom client on address 0x%x\n", + address << 1); + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return 0; + + client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (client == 0) + return -ENOMEM; + memset(client, 0, sizeof(struct i2c_client)); + client->addr = address; + client->adapter = adapter; + client->driver = &i2c_driver_tveeprom; + client->flags = I2C_CLIENT_ALLOW_USE; + client->id = tveeprom_i2c_id++; + snprintf(client->name, sizeof(client->name) - 1, "tveeprom[%d]", + client->id); + + eeprom = kmalloc(sizeof(struct tveeprom), GFP_KERNEL); + if (eeprom == NULL) { + kfree(client); + return -ENOMEM; + } + i2c_set_clientdata(client, eeprom); + memset(eeprom, 0, sizeof(struct tveeprom)); + eeprom->tuner_type = -1; + eeprom->has_radio = 0; + eeprom->model = 0; + + tvee_readee(client, eeprom_buf); + hauppauge_eeprom(eeprom, eeprom_buf); + + return 0; +} + +static int tveeprom_attach_adapter(struct i2c_adapter *adapter) +{ + if (adapter->id != (I2C_ALGO_BIT | I2C_HW_B_BT848)) + return 0; + dprintk(1, + KERN_INFO + "tveeprom.c: starting probe for adapter %s (0x%x)\n", + adapter->name, adapter->id); + return i2c_probe(adapter, &addr_data, tveeprom_detect_client); +} + +static int tveeprom_detach_client(struct i2c_client *client) +{ + struct tveeprom *eeprom = i2c_get_clientdata(client); + int err; + + err = i2c_detach_client(client); + if (err) { + return err; + } + + kfree(eeprom); + kfree(client); + return 0; +} + +/* ----------------------------------------------------------------------- */ + +/* i2c implementation */ +struct i2c_driver i2c_driver_tveeprom = { + .name = "tveeprom", + + .id = I2C_DRIVERID_TVEEPROM, + .flags = I2C_DF_NOTIFY, + + .attach_adapter = tveeprom_attach_adapter, + .detach_client = tveeprom_detach_client, + .command = tveeprom_command, + .owner = THIS_MODULE, +}; + +static int __init tveeprom_init(void) +{ + return i2c_add_driver(&i2c_driver_tveeprom); +} + +static void __exit tveeprom_exit(void) +{ + i2c_del_driver(&i2c_driver_tveeprom); +} + +module_init(tveeprom_init); +module_exit(tveeprom_exit);