From 9160723ed620f31bf38332dee02041b1cb4c9967 Mon Sep 17 00:00:00 2001 From: Hermann Pitton Date: Thu, 7 Dec 2006 21:45:28 -0300 Subject: V4L/DVB (4961): Add support for the ASUS P7131 remote control Besides adding the board specific code, this patch moves the RC5 decoding code from bt8xx to ir-functions.c to make it available for all drivers. Signed-off-by: Marc Fargas Signed-off-by: Hermann Pitton Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- include/media/ir-common.h | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) (limited to 'include/media') diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 4bb0ad810179..c60a30997973 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -36,6 +36,14 @@ #define IR_KEYCODE(tab,code) (((unsigned)code < IR_KEYTAB_SIZE) \ ? tab[code] : KEY_RESERVED) +extern int ir_rc5_remote_gap; +extern int ir_rc5_key_timeout; + +#define RC5_START(x) (((x)>>12)&3) +#define RC5_TOGGLE(x) (((x)>>11)&1) +#define RC5_ADDR(x) (((x)>>6)&31) +#define RC5_INSTR(x) ((x)&63) + struct ir_input_state { /* configuration */ int ir_type; @@ -48,6 +56,40 @@ struct ir_input_state { int keypressed; /* current state */ }; +/* this was saa7134_ir and bttv_ir, moved here for + * rc5 decoding. */ +struct card_ir { + struct input_dev *dev; + struct ir_input_state ir; + char name[32]; + char phys[32]; + + /* Usual gpio signalling */ + + u32 mask_keycode; + u32 mask_keydown; + u32 mask_keyup; + u32 polling; + u32 last_gpio; + int shift_by; + int start; // What should RC5_START() be + int addr; // What RC5_ADDR() should be. + int rc5_key_timeout; + int rc5_remote_gap; + struct work_struct work; + struct timer_list timer; + + /* RC5 gpio */ + u32 rc5_gpio; + struct timer_list timer_end; /* timer_end for code completion */ + struct timer_list timer_keyup; /* timer_end for key release */ + u32 last_rc5; /* last good rc5 code */ + u32 last_bit; /* last raw bit seen */ + u32 code; /* raw code under construction */ + struct timeval base_time; /* time of last seen code */ + int active; /* building raw code */ +}; + void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, IR_KEYTAB_TYPE *ir_codes); void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir); @@ -58,6 +100,10 @@ int ir_dump_samples(u32 *samples, int count); int ir_decode_biphase(u32 *samples, int count, int low, int high); int ir_decode_pulsedistance(u32 *samples, int count, int low, int high); +u32 ir_rc5_decode(unsigned int code); +void ir_rc5_timer_end(unsigned long data); +void ir_rc5_timer_keyup(unsigned long data); + /* Keymaps to be used by other modules */ extern IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE]; @@ -94,6 +140,7 @@ extern IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE]; #endif -- cgit v1.2.3-59-g8ed1b From 75558ab92dc95c3b5a99df7c77e95a6e8484e05c Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 18 Dec 2006 22:52:21 -0300 Subject: V4L/DVB (4986): Removed unimplemented cx2341x API commands The commands CX2341X_DEC_SET_AUDIO_OUTPUT, CX2341X_DEC_SET_AV_DELAY and CX2341X_ENC_SET_3_2_PULLDOWN are not implemented in the Conexant firmware. So these commands are removed. This also means that the V4L2_CID_MPEG_VIDEO_PULLDOWN control in cx2341x.c and pvrusb2-hdw.c is removed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-encoder-api.txt | 10 ---------- drivers/media/video/cx2341x.c | 17 ++--------------- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 3 --- include/media/cx2341x.h | 4 ---- 4 files changed, 2 insertions(+), 32 deletions(-) (limited to 'include/media') diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 9a5710646778..2412718c3d0b 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -213,16 +213,6 @@ Param[1] ------------------------------------------------------------------------------- -Name CX2341X_ENC_SET_3_2_PULLDOWN -Enum 177/0xB1 -Description - 3:2 pulldown properties (This command is not implemented in the firmware) -Param[0] - 0=enabled - 1=disabled - -------------------------------------------------------------------------------- - Name CX2341X_ENC_SET_VBI_LINE Enum 183/0xB7 Description diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 2f5ca71e0261..d60cd5ecf821 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -56,7 +56,6 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_VIDEO_B_FRAMES, V4L2_CID_MPEG_VIDEO_GOP_SIZE, V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_PULLDOWN, V4L2_CID_MPEG_VIDEO_BITRATE_MODE, V4L2_CID_MPEG_VIDEO_BITRATE, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, @@ -118,9 +117,6 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: ctrl->value = params->video_gop_closure; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - ctrl->value = params->video_pulldown; - break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: ctrl->value = params->video_bitrate_mode; break; @@ -231,9 +227,6 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: params->video_gop_closure = ctrl->value; break; - case V4L2_CID_MPEG_VIDEO_PULLDOWN: - params->video_pulldown = ctrl->value; - break; case V4L2_CID_MPEG_VIDEO_BITRATE_MODE: /* MPEG-1 only allows CBR */ if (params->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1 && @@ -679,7 +672,6 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .video_b_frames = 2, .video_gop_size = 12, .video_gop_closure = 1, - .video_pulldown = 0, .video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR, .video_bitrate = 6000000, .video_bitrate_peak = 8000000, @@ -783,10 +775,6 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, err = cx2341x_api(priv, func, CX2341X_ENC_SET_GOP_CLOSURE, 1, new->video_gop_closure); if (err) return err; } - if (old == NULL || old->video_pulldown != new->video_pulldown) { - err = cx2341x_api(priv, func, CX2341X_ENC_SET_3_2_PULLDOWN, 1, new->video_pulldown); - if (err) return err; - } if (old == NULL || old->audio_properties != new->audio_properties) { err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties); if (err) return err; @@ -888,11 +876,10 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) printk(", Peak %d", p->video_bitrate_peak); } printk("\n"); - printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n", + printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure\n", prefix, p->video_gop_size, p->video_b_frames, - p->video_gop_closure ? "" : "No ", - p->video_pulldown ? "" : "No "); + p->video_gop_closure ? "" : "No "); if (p->video_temporal_decimation) { printk(KERN_INFO "%s: Video: Temporal Decimation %d\n", prefix, p->video_temporal_decimation); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index d2004965187b..c37f8e18b70b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -159,9 +159,6 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = { },{ .strid = "video_gop_closure", .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - },{ - .strid = "video_pulldown", - .id = V4L2_CID_MPEG_VIDEO_PULLDOWN, },{ .strid = "video_bitrate_mode", .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE, diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index ecad55bf0162..d758a52cf556 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h @@ -57,7 +57,6 @@ struct cx2341x_mpeg_params { u16 video_b_frames; u16 video_gop_size; u16 video_gop_closure; - u16 video_pulldown; enum v4l2_mpeg_video_bitrate_mode video_bitrate_mode; u32 video_bitrate; u32 video_bitrate_peak; @@ -121,8 +120,6 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); #define CX2341X_DEC_SET_DISPLAY_BUFFERS 0x18 #define CX2341X_DEC_EXTRACT_VBI 0x19 #define CX2341X_DEC_SET_DECODER_SOURCE 0x1a -#define CX2341X_DEC_SET_AUDIO_OUTPUT 0x1b -#define CX2341X_DEC_SET_AV_DELAY 0x1c #define CX2341X_DEC_SET_PREBUFFERING 0x1e /* MPEG encoder API */ @@ -141,7 +138,6 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); #define CX2341X_ENC_SET_DNR_FILTER_PROPS 0x9d #define CX2341X_ENC_SET_CORING_LEVELS 0x9f #define CX2341X_ENC_SET_SPATIAL_FILTER_TYPE 0xa1 -#define CX2341X_ENC_SET_3_2_PULLDOWN 0xb1 #define CX2341X_ENC_SET_VBI_LINE 0xb7 #define CX2341X_ENC_SET_STREAM_TYPE 0xb9 #define CX2341X_ENC_SET_OUTPUT_PORT 0xbb -- cgit v1.2.3-59-g8ed1b From c36c459a5530da8869a4de832188cdcb75b60359 Mon Sep 17 00:00:00 2001 From: Juan Pablo Sormani Date: Wed, 27 Dec 2006 12:46:36 -0300 Subject: V4L/DVB (5015): Add support for more Encore TV cards Signed-off-by: Juan Pablo Sormani Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/CARDLIST.saa7134 | 3 +- drivers/media/common/ir-keymaps.c | 78 ++++++++++++++++++++++++++ drivers/media/video/saa7134/saa7134-cards.c | 86 ++++++++++++++++++++++++++++- drivers/media/video/saa7134/saa7134-input.c | 7 +++ drivers/media/video/saa7134/saa7134.h | 1 + include/media/ir-common.h | 1 + 6 files changed, 172 insertions(+), 4 deletions(-) (limited to 'include/media') diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 1a53513c6250..bcba180194e8 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -104,4 +104,5 @@ 103 -> Compro Videomate DVB-T200A 104 -> Hauppauge WinTV-HVR1110 DVB-T/Hybrid [0070:6701] 105 -> Terratec Cinergy HT PCMCIA [153b:1172] -106 -> Encore ENLTV [1131:2342] +106 -> Encore ENLTV [1131:2342,1131:2341,3016:2344] +107 -> Encore ENLTV-FM [1131:230f] diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 4a54a559d81c..8b290204ff6c 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1660,3 +1660,81 @@ IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_asus_pc39); + + +/* Encore ENLTV-FM - black plastic, white front cover with white glowing buttons + Juan Pablo Sormani */ +IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = { + + /* Power button does nothing, neither in Windows app, + although it sends data (used for BIOS wakeup?) */ + [ 0x0d ] = KEY_MUTE, + + [ 0x1e ] = KEY_TV, + [ 0x00 ] = KEY_VIDEO, + [ 0x01 ] = KEY_AUDIO, /* music */ + [ 0x02 ] = KEY_MHP, /* picture */ + + [ 0x1f ] = KEY_1, + [ 0x03 ] = KEY_2, + [ 0x04 ] = KEY_3, + [ 0x05 ] = KEY_4, + [ 0x1c ] = KEY_5, + [ 0x06 ] = KEY_6, + [ 0x07 ] = KEY_7, + [ 0x08 ] = KEY_8, + [ 0x1d ] = KEY_9, + [ 0x0a ] = KEY_0, + + [ 0x09 ] = KEY_LIST, /* -/-- */ + [ 0x0b ] = KEY_LAST, /* recall */ + + [ 0x14 ] = KEY_HOME, /* win start menu */ + [ 0x15 ] = KEY_EXIT, /* exit */ + [ 0x16 ] = KEY_UP, + [ 0x12 ] = KEY_DOWN, + [ 0x0c ] = KEY_RIGHT, + [ 0x17 ] = KEY_LEFT, + + [ 0x18 ] = KEY_ENTER, /* OK */ + + [ 0x0e ] = KEY_ESC, + [ 0x13 ] = KEY_D, /* desktop */ + [ 0x11 ] = KEY_TAB, + [ 0x19 ] = KEY_SWITCHVIDEOMODE, /* switch */ + + [ 0x1a ] = KEY_MENU, + [ 0x1b ] = KEY_ZOOM, /* fullscreen */ + [ 0x44 ] = KEY_TIME, /* time shift */ + [ 0x40 ] = KEY_MODE, /* source */ + + [ 0x5a ] = KEY_RECORD, + [ 0x42 ] = KEY_PLAY, /* play/pause */ + [ 0x45 ] = KEY_STOP, + [ 0x43 ] = KEY_CAMERA, /* camera icon */ + + [ 0x48 ] = KEY_REWIND, + [ 0x4a ] = KEY_FASTFORWARD, + [ 0x49 ] = KEY_PREVIOUS, + [ 0x4b ] = KEY_NEXT, + + [ 0x4c ] = KEY_FAVORITES, /* tv wall */ + [ 0x4d ] = KEY_SOUND, /* DVD sound */ + [ 0x4e ] = KEY_LANGUAGE, /* DVD lang */ + [ 0x4f ] = KEY_TEXT, /* DVD text */ + + [ 0x50 ] = KEY_SLEEP, /* shutdown */ + [ 0x51 ] = KEY_MODE, /* stereo > main */ + [ 0x52 ] = KEY_SELECT, /* stereo > sap */ + [ 0x53 ] = KEY_PROG1, /* teletext */ + + + [ 0x59 ] = KEY_RED, /* AP1 */ + [ 0x41 ] = KEY_GREEN, /* AP2 */ + [ 0x47 ] = KEY_YELLOW, /* AP3 */ + [ 0x57 ] = KEY_BLUE, /* AP4 */ + + +}; + +EXPORT_SYMBOL_GPL(ir_codes_encore_enltv); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 9d26ab48edd4..0403ae9e5b8a 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3189,8 +3189,10 @@ struct saa7134_board saa7134_boards[] = { }}, }, [SAA7134_BOARD_ENCORE_ENLTV] = { - /* Steven Walter */ + /* Steven Walter + Juan Pablo Sormani */ .name = "Encore ENLTV", + .audio_clock = 0x00200000, .tuner_type = TUNER_TNF_5335MF, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, @@ -3198,13 +3200,71 @@ struct saa7134_board saa7134_boards[] = { .inputs = {{ .name = name_tv, .vmux = 1, + .amux = 3, + .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, + },{ + .name = name_svideo, + .vmux = 0, + .amux = 2, + }}, + .radio = { + .name = name_radio, .amux = LINE2, +/* .gpio = 0x00300001,*/ + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, + }, + [SAA7134_BOARD_ENCORE_ENLTV_FM] = { + /* Juan Pablo Sormani */ + .name = "Encore ENLTV-FM", + .audio_clock = 0x00200000, + .tuner_type = TUNER_PHILIPS_ATSC, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = 3, .tv = 1, + },{ + .name = name_tv_mono, + .vmux = 7, + .amux = 4, + .tv = 1, + },{ + .name = name_comp1, + .vmux = 3, + .amux = 2, },{ .name = name_svideo, - .vmux = 6, - .amux = LINE1, + .vmux = 0, + .amux = 2, }}, + .radio = { + .name = name_radio, + .amux = LINE2, + .gpio = 0x20000, + + }, + .mute = { + .name = name_mute, + .amux = 0, + }, }, }; @@ -3850,6 +3910,24 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = PCI_VENDOR_ID_PHILIPS, .subdevice = 0x2342, .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x2341, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x3016, + .subdevice = 0x2344, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV, + },{ + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x1131, + .subdevice = 0x230f, + .driver_data = SAA7134_BOARD_ENCORE_ENLTV_FM, },{ /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -3959,6 +4037,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_FLYDVBTDUO: case SAA7134_BOARD_PROTEUS_2309: case SAA7134_BOARD_AVERMEDIA_A16AR: + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 08848ebc682f..1d0dd7e12521 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -325,6 +325,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keydown = 0x0040000; rc5_gpio = 1; break; + case SAA7134_BOARD_ENCORE_ENLTV: + case SAA7134_BOARD_ENCORE_ENLTV_FM: + ir_codes = ir_codes_encore_enltv; + mask_keycode = 0x00007f; + mask_keyup = 0x040000; + polling = 50; // ms + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 6e65fa6f2b3e..ec08412b1c66 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -233,6 +233,7 @@ struct saa7134_format { #define SAA7134_BOARD_HAUPPAUGE_HVR1110 104 #define SAA7134_BOARD_CINERGY_HT_PCMCIA 105 #define SAA7134_BOARD_ENCORE_ENLTV 106 +#define SAA7134_BOARD_ENCORE_ENLTV_FM 107 #define SAA7134_MAXBOARDS 8 #define SAA7134_INPUT_MAX 8 diff --git a/include/media/ir-common.h b/include/media/ir-common.h index c60a30997973..04201f77b85b 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -141,6 +141,7 @@ extern IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE]; #endif -- cgit v1.2.3-59-g8ed1b From c408a6f673e8fb0b67c81fc9cb29414265c1e6c1 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Thu, 28 Dec 2006 12:47:47 -0300 Subject: V4L/DVB (5017): DVB: fix compile error This patch fixes the following compile error: <-- snip --> ... LD drivers/media/video/built-in.o drivers/media/video/saa7134/built-in.o:(.data+0x85ec): multiple definition of `ir_rc5_remote_gap' drivers/media/video/bt8xx/built-in.o:(.data+0x734c): first defined here drivers/media/video/saa7134/built-in.o:(.data+0x85f0): multiple definition of `ir_rc5_key_timeout' drivers/media/video/bt8xx/built-in.o:(.data+0x7350): first defined here make[4]: *** [drivers/media/video/built-in.o] Error 1 <-- snip --> Since this variables were needlessly global, this patch implements the trivial fix of making them static. Signed-off-by: Adrian Bunk Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/bt8xx/bttv-input.c | 4 ++-- drivers/media/video/saa7134/saa7134-input.c | 4 ++-- include/media/ir-common.h | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index 14c07c606632..825161b9cb3f 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -36,9 +36,9 @@ module_param(repeat_delay, int, 0644); static int repeat_period = 33; module_param(repeat_period, int, 0644); -int ir_rc5_remote_gap = 885; +static int ir_rc5_remote_gap = 885; module_param(ir_rc5_remote_gap, int, 0644); -int ir_rc5_key_timeout = 200; +static int ir_rc5_key_timeout = 200; module_param(ir_rc5_key_timeout, int, 0644); #define DEVNAME "bttv-input" diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 1d0dd7e12521..46c583f1e788 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -40,9 +40,9 @@ static int pinnacle_remote = 0; module_param(pinnacle_remote, int, 0644); /* Choose Pinnacle PCTV remote */ MODULE_PARM_DESC(pinnacle_remote, "Specify Pinnacle PCTV remote: 0=coloured, 1=grey (defaults to 0)"); -int ir_rc5_remote_gap = 885; +static int ir_rc5_remote_gap = 885; module_param(ir_rc5_remote_gap, int, 0644); -int ir_rc5_key_timeout = 115; +static int ir_rc5_key_timeout = 115; module_param(ir_rc5_key_timeout, int, 0644); #define dprintk(fmt, arg...) if (ir_debug) \ diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 04201f77b85b..0a75c0fcfea2 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -36,9 +36,6 @@ #define IR_KEYCODE(tab,code) (((unsigned)code < IR_KEYTAB_SIZE) \ ? tab[code] : KEY_RESERVED) -extern int ir_rc5_remote_gap; -extern int ir_rc5_key_timeout; - #define RC5_START(x) (((x)>>12)&3) #define RC5_TOGGLE(x) (((x)>>11)&1) #define RC5_ADDR(x) (((x)>>6)&31) -- cgit v1.2.3-59-g8ed1b From ae1942c5712f700c9ccc8cc287c51db4daaa50d7 Mon Sep 17 00:00:00 2001 From: Ville-Pekka Vainio Date: Fri, 12 Jan 2007 14:06:21 -0300 Subject: V4L/DVB (5070): Budget-ci: add support for the Technotrend 1500 bundled remote The keymap is based on a previous patch by Jussi Kukkonen. This remote is identified by subsystem_device id 0x1010. Signed-off-by: Ville-Pekka Vainio Signed-off-by: Michael Krufky Signed-off-by: Mauro Carvalho Chehab --- drivers/media/common/ir-keymaps.c | 39 +++++++++++++++++++++++++++++++++++++ drivers/media/dvb/ttpci/budget-ci.c | 11 ++++++++++- include/media/ir-common.h | 1 + 3 files changed, 50 insertions(+), 1 deletion(-) (limited to 'include/media') diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 8b290204ff6c..03b47a262f27 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1738,3 +1738,42 @@ IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE] = { }; EXPORT_SYMBOL_GPL(ir_codes_encore_enltv); + +/* for the Technotrend 1500 bundled remote: */ +IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE] = { + [ 0x01 ] = KEY_POWER, + [ 0x02 ] = KEY_SHUFFLE, /* ? double-arrow key */ + [ 0x03 ] = KEY_1, + [ 0x04 ] = KEY_2, + [ 0x05 ] = KEY_3, + [ 0x06 ] = KEY_4, + [ 0x07 ] = KEY_5, + [ 0x08 ] = KEY_6, + [ 0x09 ] = KEY_7, + [ 0x0a ] = KEY_8, + [ 0x0b ] = KEY_9, + [ 0x0c ] = KEY_0, + [ 0x0d ] = KEY_UP, + [ 0x0e ] = KEY_LEFT, + [ 0x0f ] = KEY_OK, + [ 0x10 ] = KEY_RIGHT, + [ 0x11 ] = KEY_DOWN, + [ 0x12 ] = KEY_INFO, + [ 0x13 ] = KEY_EXIT, + [ 0x14 ] = KEY_RED, + [ 0x15 ] = KEY_GREEN, + [ 0x16 ] = KEY_YELLOW, + [ 0x17 ] = KEY_BLUE, + [ 0x18 ] = KEY_MUTE, + [ 0x19 ] = KEY_TEXT, + [ 0x1a ] = KEY_MODE, /* ? TV/Radio */ + [ 0x21 ] = KEY_OPTION, + [ 0x22 ] = KEY_EPG, + [ 0x23 ] = KEY_CHANNELUP, + [ 0x24 ] = KEY_CHANNELDOWN, + [ 0x25 ] = KEY_VOLUMEUP, + [ 0x26 ] = KEY_VOLUMEDOWN, + [ 0x27 ] = KEY_SETUP, +}; + +EXPORT_SYMBOL_GPL(ir_codes_tt_1500); diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index f2066b47baee..ea4257653318 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -223,7 +223,6 @@ static int msp430_ir_init(struct budget_ci *budget_ci) switch (budget_ci->budget.dev->pci->subsystem_device) { case 0x100c: case 0x100f: - case 0x1010: case 0x1011: case 0x1012: case 0x1017: @@ -236,6 +235,16 @@ static int msp430_ir_init(struct budget_ci *budget_ci) else budget_ci->ir.rc5_device = rc5_device; break; + case 0x1010: + /* for the Technotrend 1500 bundled remote */ + ir_input_init(input_dev, &budget_ci->ir.state, + IR_TYPE_RC5, ir_codes_tt_1500); + + if (rc5_device < 0) + budget_ci->ir.rc5_device = IR_DEVICE_ANY; + else + budget_ci->ir.rc5_device = rc5_device; + break; default: /* unknown remote */ ir_input_init(input_dev, &budget_ci->ir.state, diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 0a75c0fcfea2..9807a7c15830 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -139,6 +139,7 @@ extern IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_budget_ci_old[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_asus_pc39[IR_KEYTAB_SIZE]; extern IR_KEYTAB_TYPE ir_codes_encore_enltv[IR_KEYTAB_SIZE]; +extern IR_KEYTAB_TYPE ir_codes_tt_1500[IR_KEYTAB_SIZE]; #endif -- cgit v1.2.3-59-g8ed1b From 4cbca185e9adf3d48205b60bf196ebb9882af043 Mon Sep 17 00:00:00 2001 From: Marco Schluessler Date: Sun, 21 Jan 2007 19:43:38 -0300 Subject: V4L/DVB (5113): Adds video output routing Nexus CA needs to use a different routing on saa7115 module. Signed-off-by: Marco Schluessler Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7115.c | 24 ++++++++++++++++++------ include/media/saa7115.h | 3 +++ 2 files changed, 21 insertions(+), 6 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index c2374ed7ba9f..389e518bc3e5 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -71,6 +71,7 @@ I2C_CLIENT_INSMOD; struct saa711x_state { v4l2_std_id std; int input; + int output; int enable; int radio; int bright; @@ -1301,7 +1302,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar struct v4l2_routing *route = arg; route->input = state->input; - route->output = 0; + route->output = state->output; break; } @@ -1309,7 +1310,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar { struct v4l2_routing *route = arg; - v4l_dbg(1, debug, client, "decoder set input %d\n", route->input); + v4l_dbg(1, debug, client, "decoder set input %d output %d\n", route->input, route->output); /* saa7113 does not have these inputs */ if (state->ident == V4L2_IDENT_SAA7113 && (route->input == SAA7115_COMPOSITE4 || @@ -1318,10 +1319,12 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar } if (route->input > SAA7115_SVIDEO3) return -EINVAL; - if (state->input == route->input) + if (route->output > SAA7115_IPORT_ON) + return -EINVAL; + if (state->input == route->input && state->output == route->output) break; - v4l_dbg(1, debug, client, "now setting %s input\n", - (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite"); + v4l_dbg(1, debug, client, "now setting %s input %s output\n", + (route->input >= SAA7115_SVIDEO0) ? "S-Video" : "Composite", (route->output == SAA7115_IPORT_ON) ? "iport on" : "iport off"); state->input = route->input; /* select mode */ @@ -1333,6 +1336,14 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar saa711x_write(client, R_09_LUMA_CNTL, (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) | (state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0)); + + state->output = route->output; + if (state->ident == V4L2_IDENT_SAA7114 || + state->ident == V4L2_IDENT_SAA7115) { + saa711x_write(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK, + (saa711x_read(client, R_83_X_PORT_I_O_ENA_AND_OUT_CLK) & 0xfe) | + (state->output & 0x01)); + } break; } @@ -1492,6 +1503,7 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) return -ENOMEM; } state->input = -1; + state->output = SAA7115_IPORT_ON; state->enable = 1; state->radio = 0; state->bright = 128; @@ -1550,7 +1562,7 @@ static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind) static int saa711x_probe(struct i2c_adapter *adapter) { - if (adapter->class & I2C_CLASS_TV_ANALOG) + if (adapter->class & I2C_CLASS_TV_ANALOG || adapter->class & I2C_CLASS_TV_DIGITAL) return i2c_probe(adapter, &addr_data, &saa711x_attach); return 0; } diff --git a/include/media/saa7115.h b/include/media/saa7115.h index 9f0e2285a099..f677dfb9d373 100644 --- a/include/media/saa7115.h +++ b/include/media/saa7115.h @@ -42,5 +42,8 @@ #define SAA7115_FREQ_FL_CGCDIV (1 << 1) /* SA 3A[6], CGCDIV, SAA7115 only */ #define SAA7115_FREQ_FL_APLL (1 << 2) /* SA 3A[3], APLL, SAA7114/5 only */ +#define SAA7115_IPORT_ON 1 +#define SAA7115_IPORT_OFF 0 + #endif -- cgit v1.2.3-59-g8ed1b From dbbff48f39263f7e5c96a55624da87879de2bf71 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Mon, 22 Jan 2007 23:31:53 -0300 Subject: V4L/DVB (5144): Restore VIDIOC_INT_[SG]_REGISTER calls Add support for these ioctls to the video_ioctl2 system and the cx88 driver. Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx88/cx88-video.c | 32 +++++++++++++++++++++++++++++++- drivers/media/video/videodev.c | 16 ++++++++++++++++ include/media/v4l2-dev.h | 12 ++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) (limited to 'include/media') diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 360046e1d6be..ee8cbd33cfc1 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1385,6 +1385,32 @@ static int vidioc_s_frequency (struct file *file, void *priv, cx88_set_freq (core,f); } +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vidioc_g_register (struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; + + if (reg->i2c_id != 0) + return -EINVAL; + /* cx2388x has a 24-bit register space */ + reg->val = cx_read(reg->reg&0xffffff); + return 0; +} + +static int vidioc_s_register (struct file *file, void *fh, + struct v4l2_register *reg) +{ + struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; + + if (reg->i2c_id != 0) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx_write(reg->reg&0xffffff, reg->val); + return 0; +} +#endif /* ----------------------------------------------------------- */ /* RADIO ESPECIFIC IOCTLS */ @@ -1656,8 +1682,12 @@ static struct video_device cx8800_video_template = .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif .tvnorms = CX88_NORMS, - .current_norm = V4L2_STD_NTSC_M, + .current_norm = V4L2_STD_NTSC_M, }; static const struct file_operations radio_fops = diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index a786c1f5b960..4b5d5f771e4c 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1453,6 +1453,22 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_log_status(file, fh); break; } +#ifdef CONFIG_VIDEO_ADV_DEBUG + case VIDIOC_INT_G_REGISTER: + { + struct v4l2_register *p=arg; + if (vfd->vidioc_g_register) + ret=vfd->vidioc_g_register(file, fh, p); + break; + } + case VIDIOC_INT_S_REGISTER: + { + struct v4l2_register *p=arg; + if (vfd->vidioc_s_register) + ret=vfd->vidioc_s_register(file, fh, p); + break; + } +#endif } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index fb96472a1bd3..46eb71f5653c 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -77,6 +77,9 @@ int v4l_compat_translate_ioctl(struct inode *inode, struct file *file, extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); +/* Forward definition of v4l2-common.h defined structure */ +struct v4l2_register; + /* * Newer version of video_device, handled by videodev2.c * This version moves redundant code from video device code to @@ -296,6 +299,15 @@ struct video_device int (*vidioc_log_status) (struct file *file, void *fh); + /* Debugging ioctls */ +#ifdef CONFIG_VIDEO_ADV_DEBUG + int (*vidioc_g_register) (struct file *file, void *fh, + struct v4l2_register *reg); + int (*vidioc_s_register) (struct file *file, void *fh, + struct v4l2_register *reg); +#endif + + #ifdef OBSOLETE_OWNER /* to be removed soon */ /* obsolete -- fops->owner is used instead */ struct module *owner; -- cgit v1.2.3-59-g8ed1b From 52ebc763d8e0c9f2ab48af89a75e90e2318bac86 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Tue, 23 Jan 2007 22:38:13 -0300 Subject: V4L/DVB (5146): Make VIDIOC_INT_[SG]_REGISTER ioctls no longer internal only The direct register access ioctls were defined as kernel internal only, but they are very useful for debugging hardware from userspace and are used as such. Officially export them. VIDIOC_INT_[SG]_REGISTER is renamed to VIDIOC_DBG_[SG]_REGISTER Definition of ioctl and struct v4l2_register is moved from v4l2-common.h to videodev2.h. Types used in struct v4l2_register are changed to the userspace exportable versions (u32 -> __u32, etc). Use of VIDIOC_DBG_S_REGISTER requires CAP_SYS_ADMIN permission, so move the check into the video_ioctl2() dispatcher so it doesn't need to be duplicated in each driver's call-back function. CAP_SYS_ADMIN check is added to pvrusb2 (which doesn't use video_ioctl2). Signed-off-by: Trent Piepho Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 4 ++-- drivers/media/video/cx88/cx88-video.c | 2 -- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 6 +++--- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 2 +- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 14 +++++++++----- drivers/media/video/saa7115.c | 4 ++-- drivers/media/video/saa7127.c | 4 ++-- drivers/media/video/tvp5150.c | 4 ++-- drivers/media/video/upd64031a.c | 4 ++-- drivers/media/video/upd64083.c | 4 ++-- drivers/media/video/usbvision/usbvision-video.c | 12 ++++++------ drivers/media/video/v4l2-common.c | 11 ++++++----- drivers/media/video/videodev.c | 8 +++++--- include/linux/videodev2.h | 14 ++++++++++++++ include/media/v4l2-common.h | 11 +---------- include/media/v4l2-dev.h | 3 --- 16 files changed, 57 insertions(+), 50 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 7bb7589a07c3..6515b2a78417 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -628,7 +628,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, #ifdef CONFIG_VIDEO_ADV_DEBUG /* ioctls to allow direct access to the * cx25840 registers for testing */ - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; @@ -638,7 +638,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index ee8cbd33cfc1..f6736eb786bc 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1405,8 +1405,6 @@ static int vidioc_s_register (struct file *file, void *fh, if (reg->i2c_id != 0) return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; cx_write(reg->reg&0xffffff, reg->val); return 0; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 5e166ed19fc5..2a350755bd30 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3277,7 +3277,7 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) int pvr2_hdw_register_access(struct pvr2_hdw *hdw, - u32 chip_id,unsigned long reg_id, + u32 chip_id, u32 reg_id, int setFl,u32 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -3295,8 +3295,8 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, cp = list_entry(item,struct pvr2_i2c_client,list); if (cp->client->driver->id != chip_id) continue; stat = pvr2_i2c_client_cmd( - cp,(setFl ? VIDIOC_INT_S_REGISTER : - VIDIOC_INT_G_REGISTER),&req); + cp,(setFl ? VIDIOC_DBG_S_REGISTER : + VIDIOC_DBG_G_REGISTER),&req); if (!setFl) *val_ptr = req.val; okFl = !0; break; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index dc7a3ba8dd18..e6df8e4a7fe0 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -222,7 +222,7 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, setFl - true to set the register, false to read it val_ptr - storage location for source / result. */ int pvr2_hdw_register_access(struct pvr2_hdw *, - u32 chip_id,unsigned long reg_id, + u32 chip_id,u32 reg_id, int setFl,u32 *val_ptr); /* The following entry points are all lower level things you normally don't diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 49f5d3c3614c..cde5f5f3e8f2 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -738,16 +738,20 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, break; } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: + if (!capable(CAP_SYS_ADMIN)) { + ret = -EPERM; + break; + } /* fall through */ + case VIDIOC_DBG_G_REGISTER: { u32 val; struct v4l2_register *req = (struct v4l2_register *)arg; - if (cmd == VIDIOC_INT_S_REGISTER) val = req->val; + if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; ret = pvr2_hdw_register_access( hdw,req->i2c_id,req->reg, - cmd == VIDIOC_INT_S_REGISTER,&val); - if (cmd == VIDIOC_INT_G_REGISTER) req->val = val; + cmd == VIDIOC_DBG_S_REGISTER,&val); + if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; break; } #endif diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 389e518bc3e5..bb6aa135002a 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1417,7 +1417,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; @@ -1427,7 +1427,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index ad401bdefeaf..304375ade4a9 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -614,7 +614,7 @@ static int saa7127_command(struct i2c_client *client, break; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; @@ -624,7 +624,7 @@ static int saa7127_command(struct i2c_client *client, break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index bc0a4fc27b24..65d4389690a0 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -950,7 +950,7 @@ static int tvp5150_command(struct i2c_client *c, } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; @@ -960,7 +960,7 @@ static int tvp5150_command(struct i2c_client *c, break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index fc52201d607e..0eee82ba52bc 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -162,7 +162,7 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void * break; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; @@ -172,7 +172,7 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void * break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; u8 addr = reg->reg & 0xff; diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index c3a7ffe5c267..3f0eec0cdb45 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -139,7 +139,7 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a break; #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; @@ -149,7 +149,7 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; u8 addr = reg->reg & 0xff; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 82c39767ba34..b6fabeeb8ab8 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -519,7 +519,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, #ifdef CONFIG_VIDEO_ADV_DEBUG /* ioctls to allow direct acces to the NT100x registers */ - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *reg = arg; int errCode; @@ -529,17 +529,17 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, /* NT100x has a 8-bit register space */ errCode = usbvision_read_reg(usbvision, reg->reg&0xff); if (errCode < 0) { - err("%s: VIDIOC_INT_G_REGISTER failed: error %d", __FUNCTION__, errCode); + err("%s: VIDIOC_DBG_G_REGISTER failed: error %d", __FUNCTION__, errCode); } else { reg->val=(unsigned char)errCode; - PDEBUG(DBG_IOCTL, "VIDIOC_INT_G_REGISTER reg=0x%02X, value=0x%02X", + PDEBUG(DBG_IOCTL, "VIDIOC_DBG_G_REGISTER reg=0x%02X, value=0x%02X", (unsigned int)reg->reg, reg->val); errCode = 0; // No error } return errCode; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *reg = arg; int errCode; @@ -550,10 +550,10 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, return -EPERM; errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val); if (errCode < 0) { - err("%s: VIDIOC_INT_S_REGISTER failed: error %d", __FUNCTION__, errCode); + err("%s: VIDIOC_DBG_S_REGISTER failed: error %d", __FUNCTION__, errCode); } else { - PDEBUG(DBG_IOCTL, "VIDIOC_INT_S_REGISTER reg=0x%02X, value=0x%02X", + PDEBUG(DBG_IOCTL, "VIDIOC_DBG_S_REGISTER reg=0x%02X, value=0x%02X", (unsigned int)reg->reg, reg->val); errCode = 0; } diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index dab87512b9bd..d20d4ca5d8a3 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -400,9 +400,10 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(TUNER_SET_STANDBY)] = "TUNER_SET_STANDBY", [_IOC_NR(TDA9887_SET_CONFIG)] = "TDA9887_SET_CONFIG", + [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", + [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", + [_IOC_NR(VIDIOC_INT_S_TUNER_MODE)] = "VIDIOC_INT_S_TUNER_MODE", - [_IOC_NR(VIDIOC_INT_S_REGISTER)] = "VIDIOC_INT_S_REGISTER", - [_IOC_NR(VIDIOC_INT_G_REGISTER)] = "VIDIOC_INT_G_REGISTER", [_IOC_NR(VIDIOC_INT_RESET)] = "VIDIOC_INT_RESET", [_IOC_NR(VIDIOC_INT_AUDIO_CLOCK_FREQ)] = "VIDIOC_INT_AUDIO_CLOCK_FREQ", [_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE", @@ -753,11 +754,11 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) p->id,p->index,p->name); break; } - case VIDIOC_INT_G_REGISTER: - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *p=arg; - printk ("%s: i2c_id=%d, reg=%lu, val=%d\n", s, + printk ("%s: i2c_id=%d, reg=%d, val=%d\n", s, p->i2c_id,p->reg,p->val); break; diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 4b5d5f771e4c..764a53b70db2 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1454,17 +1454,19 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, break; } #ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_INT_G_REGISTER: + case VIDIOC_DBG_G_REGISTER: { struct v4l2_register *p=arg; if (vfd->vidioc_g_register) ret=vfd->vidioc_g_register(file, fh, p); break; } - case VIDIOC_INT_S_REGISTER: + case VIDIOC_DBG_S_REGISTER: { struct v4l2_register *p=arg; - if (vfd->vidioc_s_register) + if (!capable(CAP_SYS_ADMIN)) + ret=-EPERM; + else if (vfd->vidioc_s_register) ret=vfd->vidioc_s_register(file, fh, p); break; } diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 112b28c1f63a..fe6ccdfa9d48 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1270,6 +1270,17 @@ struct v4l2_streamparm } parm; }; +/* + * A D V A N C E D D E B U G G I N G + */ + +/* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */ +struct v4l2_register { + __u32 i2c_id; /* I2C driver ID of the I2C chip, or 0 for the host */ + __u32 reg; + __u32 val; +}; + /* * I O C T L C O D E S F O R V I D E O D E V I C E S * @@ -1339,6 +1350,9 @@ struct v4l2_streamparm #define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) #endif +/* only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ +#define VIDIOC_DBG_S_REGISTER _IOW ('d', 100, struct v4l2_register) +#define VIDIOC_DBG_G_REGISTER _IOWR('d', 101, struct v4l2_register) #ifdef __OLD_VIDIOC_ /* for compatibility, will go away some day */ diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 91b19921f958..959e6f6a4eff 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -99,13 +99,6 @@ u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id); /* Internal ioctls */ -/* VIDIOC_INT_G_REGISTER and VIDIOC_INT_S_REGISTER */ -struct v4l2_register { - u32 i2c_id; /* I2C driver ID of the I2C chip. 0 for the I2C adapter. */ - unsigned long reg; - u32 val; -}; - /* VIDIOC_INT_DECODE_VBI_LINE */ struct v4l2_decode_vbi_line { u32 is_second_field; /* Set to 0 for the first (odd) field, @@ -175,9 +168,7 @@ enum v4l2_chip_ident { Replacement of TUNER_SET_STANDBY. */ #define VIDIOC_INT_S_STANDBY _IOW('d', 94, u32) -/* only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ -#define VIDIOC_INT_S_REGISTER _IOW ('d', 100, struct v4l2_register) -#define VIDIOC_INT_G_REGISTER _IOWR('d', 101, struct v4l2_register) +/* 100, 101 used by VIDIOC_DBG_[SG]_REGISTER */ /* Generic reset command. The argument selects which subsystems to reset. Passing 0 will always reset the whole chip. */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 46eb71f5653c..aeec56992ef5 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -77,9 +77,6 @@ int v4l_compat_translate_ioctl(struct inode *inode, struct file *file, extern long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg); -/* Forward definition of v4l2-common.h defined structure */ -struct v4l2_register; - /* * Newer version of video_device, handled by videodev2.c * This version moves redundant code from video device code to -- cgit v1.2.3-59-g8ed1b From b50e7fe99317c05b0bb8ba6338bc6aa7da3b918e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 25 Jan 2007 05:00:01 -0300 Subject: V4L/DVB (5147): Make vivi driver to use vmalloced pointers Before this patch, vivi were simulating a scatter gather DMA transfer. While this is academic, showing how stuff really works on a real PCI device, this means a non-optimized code. There are only two memory models that vivi implements: 1) kernel alloced memory. This is also used by read() method. On this case, a vmalloc32 buffer is allocated at kernel; 2) userspace allocated memory. This is used by most userspace apps. video-buf will store this pointer. a simple copy_to_user is enough to transfer data. The third memory model scenario supported by video-buf is overlay mode. This model is not implemented on vivi and unlikely to be implemented on newer drivers, since now, most userspace apps do some post-processing (like de-interlacing). After this patch, some cleanups may be done at video-buf.c to avoid allocating pages, when the driver doesn't need a PCI buffer. This is the case of vivi and usb drivers. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/video-buf.c | 3 ++ drivers/media/video/vivi.c | 83 +++++++++++++++++++++++++++++++++++------ include/media/video-buf.h | 3 ++ 3 files changed, 78 insertions(+), 11 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c index 6504a5866849..459786ff459a 100644 --- a/drivers/media/video/video-buf.c +++ b/drivers/media/video/video-buf.c @@ -148,6 +148,8 @@ int videobuf_dma_init_user(struct videobuf_dmabuf *dma, int direction, dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n", data,size,dma->nr_pages); + dma->varea = (void *) data; + down_read(¤t->mm->mmap_sem); err = get_user_pages(current,current->mm, data & PAGE_MASK, dma->nr_pages, @@ -285,6 +287,7 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) vfree(dma->vmalloc); dma->vmalloc = NULL; + dma->varea = NULL; if (dma->bus_addr) { dma->bus_addr = 0; diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 178441922738..0fb6d520d1d0 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -145,7 +145,9 @@ struct vivi_buffer { struct vivi_fmt *fmt; +#ifdef CONFIG_VIVI_SCATTER struct sg_to_addr *to_addr; +#endif }; struct vivi_dmaqueue { @@ -230,6 +232,7 @@ static u8 bars[8][3] = { #define TSTAMP_MAX_Y TSTAMP_MIN_Y+15 #define TSTAMP_MIN_X 64 +#ifdef CONFIG_VIVI_SCATTER static void prep_to_addr(struct sg_to_addr to_addr[], struct videobuf_buffer *vb) { @@ -262,14 +265,24 @@ static int get_addr_pos(int pos, int pages, struct sg_to_addr to_addr[]) return (p1); } +#endif +#ifdef CONFIG_VIVI_SCATTER static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, int hmax, int line, char *timestr) +#else +static void gen_line(char *basep,int inipos,int wmax, + int hmax, int line, char *timestr) +#endif { - int w,i,j,pos=inipos,pgpos,oldpg,y; - char *p,*s,*basep; - struct page *pg; + int w,i,j,pos=inipos,y; + char *p,*s; u8 chr,r,g,b,color; +#ifdef CONFIG_VIVI_SCATTER + int pgpos,oldpg; + char *basep; + struct page *pg; + unsigned long flags; spinlock_t spinlock; @@ -280,6 +293,7 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, pg=pfn_to_page(sg_dma_address(to_addr[oldpg].sg) >> PAGE_SHIFT); spin_lock_irqsave(&spinlock,flags); basep = kmap_atomic(pg, KM_BOUNCE_READ)+to_addr[oldpg].sg->offset; +#endif /* We will just duplicate the second pixel at the packet */ wmax/=2; @@ -291,6 +305,7 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, b=bars[w*7/wmax][2]; for (color=0;color<4;color++) { +#ifdef CONFIG_VIVI_SCATTER pgpos=get_addr_pos(pos,pages,to_addr); if (pgpos!=oldpg) { pg=pfn_to_page(sg_dma_address(to_addr[pgpos].sg) >> PAGE_SHIFT); @@ -299,6 +314,9 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, oldpg=pgpos; } p=basep+pos-to_addr[pgpos].pos; +#else + p=basep+pos; +#endif switch (color) { case 0: @@ -343,6 +361,7 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, pos=inipos+j*2; for (color=0;color<4;color++) { +#ifdef CONFIG_VIVI_SCATTER pgpos=get_addr_pos(pos,pages,to_addr); if (pgpos!=oldpg) { pg=pfn_to_page(sg_dma_address( @@ -356,6 +375,9 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, oldpg=pgpos; } p=basep+pos-to_addr[pgpos].pos; +#else + p=basep+pos; +#endif y=TO_Y(r,g,b); @@ -380,19 +402,27 @@ static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax, end: +#ifdef CONFIG_VIVI_SCATTER kunmap_atomic(basep, KM_BOUNCE_READ); spin_unlock_irqrestore(&spinlock,flags); - +#else + return; +#endif } static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) { int h,pos=0; int hmax = buf->vb.height; int wmax = buf->vb.width; - struct videobuf_buffer *vb=&buf->vb; - struct sg_to_addr *to_addr=buf->to_addr; struct timeval ts; +#ifdef CONFIG_VIVI_SCATTER + struct sg_to_addr *to_addr=buf->to_addr; + struct videobuf_buffer *vb=&buf->vb; +#else + char *tmpbuf; +#endif +#ifdef CONFIG_VIVI_SCATTER /* Test if DMA mapping is ready */ if (!sg_dma_address(&vb->dma.sglist[0])) return; @@ -401,9 +431,27 @@ static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) /* Check if there is enough memory */ BUG_ON(buf->vb.dma.nr_pages << PAGE_SHIFT < (buf->vb.width*buf->vb.height)*2); +#else + if (buf->vb.dma.varea) { + tmpbuf=kmalloc (wmax*2, GFP_KERNEL); + } else { + tmpbuf=buf->vb.dma.vmalloc; + } + +#endif for (h=0;hdma.nr_pages,wmax,hmax,h,dev->timestr); +#else + if (buf->vb.dma.varea) { + gen_line(tmpbuf,0,wmax,hmax,h,dev->timestr); + /* FIXME: replacing to __copy_to_user */ + copy_to_user(buf->vb.dma.varea+pos,tmpbuf,wmax*2); + } else { + gen_line(tmpbuf,pos,wmax,hmax,h,dev->timestr); + } +#endif pos += wmax*2; } @@ -429,7 +477,7 @@ static void vivi_fillbuff(struct vivi_dev *dev,struct vivi_buffer *buf) dev->h,dev->m,dev->s,(dev->us+500)/1000); dprintk(2,"vivifill at %s: Buffer 0x%08lx size= %d\n",dev->timestr, - (unsigned long)buf->vb.dma.vmalloc,pos); + (unsigned long)buf->vb.dma.varea,pos); /* Advice that buffer was filled */ buf->vb.state = STATE_DONE; @@ -668,9 +716,11 @@ static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf) if (in_interrupt()) BUG(); +#ifdef CONFIG_VIVI_SCATTER /*FIXME: Maybe a spinlock is required here */ kfree(buf->to_addr); buf->to_addr=NULL; +#endif videobuf_waiton(&buf->vb,0,0); videobuf_dma_unmap(vq, &buf->vb.dma); @@ -716,11 +766,12 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, buf->vb.state = STATE_PREPARED; +#ifdef CONFIG_VIVI_SCATTER if (NULL == (buf->to_addr = kmalloc(sizeof(*buf->to_addr) * vb->dma.nr_pages,GFP_KERNEL))) { rc=-ENOMEM; goto fail; } - +#endif return 0; fail: @@ -785,6 +836,7 @@ static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb free_buffer(vq,buf); } +#ifdef CONFIG_VIVI_SCATTER static int vivi_map_sg(void *dev, struct scatterlist *sg, int nents, int direction) { @@ -817,6 +869,7 @@ static int vivi_dma_sync_sg(void *dev,struct scatterlist *sglist, int nr_pages, // flush_write_buffers(); return 0; } +#endif static struct videobuf_queue_ops vivi_video_qops = { .buf_setup = buffer_setup, @@ -825,9 +878,9 @@ static struct videobuf_queue_ops vivi_video_qops = { .buf_release = buffer_release, /* Non-pci handling routines */ - .vb_map_sg = vivi_map_sg, - .vb_dma_sync_sg = vivi_dma_sync_sg, - .vb_unmap_sg = vivi_unmap_sg, +// .vb_map_sg = vivi_map_sg, +// .vb_dma_sync_sg = vivi_dma_sync_sg, +// .vb_unmap_sg = vivi_unmap_sg, }; /* ------------------------------------------------------------------ @@ -1205,11 +1258,19 @@ static int vivi_open(struct inode *inode, struct file *file) sprintf(dev->timestr,"%02d:%02d:%02d:%03d", dev->h,dev->m,dev->s,(dev->us+500)/1000); +#ifdef CONFIG_VIVI_SCATTER + videobuf_queue_init(&fh->vb_vidq,VIDEOBUF_DMA_SCATTER, &vivi_video_qops, + NULL, NULL, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct vivi_buffer),fh); +#else videobuf_queue_init(&fh->vb_vidq, &vivi_video_qops, NULL, NULL, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct vivi_buffer),fh); +#endif return 0; } diff --git a/include/media/video-buf.h b/include/media/video-buf.h index 1115a256969f..d6f079476db3 100644 --- a/include/media/video-buf.h +++ b/include/media/video-buf.h @@ -78,6 +78,9 @@ struct videobuf_dmabuf { /* for kernel buffers */ void *vmalloc; + /* Stores the userspace pointer to vmalloc area */ + void *varea; + /* for overlay buffers (pci-pci dma) */ dma_addr_t bus_addr; -- cgit v1.2.3-59-g8ed1b From c65eeaab1f04d1113b5c4fb21f7f61ded795d2bb Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 6 Feb 2007 21:53:31 -0300 Subject: V4L/DVB (5200): V4l_printk_ioctl_arg() is no longer used. Signed-off-by: Adrian Bunk Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 586 -------------------------------------- include/media/v4l2-common.h | 3 - 2 files changed, 589 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 1f359252c879..ddfd80c5618b 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -271,11 +271,6 @@ char *v4l2_type_names[] = { [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out", }; -static char *v4l2_memory_names[] = { - [V4L2_MEMORY_MMAP] = "mmap", - [V4L2_MEMORY_USERPTR] = "userptr", - [V4L2_MEMORY_OVERLAY] = "overlay", -}; #define prt_names(a,arr) (((a)>=0)&&((a)width,fmt->height,fmt->pixelformat, - prt_names(fmt->field,v4l2_field_names), - fmt->bytesperline,fmt->sizeimage,fmt->colorspace); -}; /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ @@ -467,578 +454,6 @@ void v4l_printk_ioctl(unsigned int cmd) } } -/* Common ioctl debug function. This function can be used by - external ioctl messages as well as internal V4L ioctl and its - arguments */ -void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg) -{ - printk(s); - printk(": "); - v4l_printk_ioctl(cmd); - switch (cmd) { - case VIDIOC_INT_G_CHIP_IDENT: - { - enum v4l2_chip_ident *p=arg; - printk ("%s: chip ident=%d\n", s, *p); - break; - } - case VIDIOC_G_PRIORITY: - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *p=arg; - printk ("%s: priority=%d\n", s, *p); - break; - } - case VIDIOC_INT_S_TUNER_MODE: - { - enum v4l2_tuner_type *p=arg; - printk ("%s: tuner type=%d\n", s, *p); - break; - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case DECODER_SET_VBI_BYPASS: - case DECODER_ENABLE_OUTPUT: - case DECODER_GET_STATUS: - case DECODER_SET_OUTPUT: - case DECODER_SET_INPUT: - case DECODER_SET_GPIO: - case DECODER_SET_NORM: - case VIDIOCCAPTURE: - case VIDIOCSYNC: - case VIDIOCSWRITEMODE: -#endif - case TUNER_SET_TYPE_ADDR: - case TUNER_SET_STANDBY: - case TDA9887_SET_CONFIG: -#ifdef __OLD_VIDIOC_ - case VIDIOC_OVERLAY_OLD: -#endif - case VIDIOC_STREAMOFF: - case VIDIOC_G_OUTPUT: - case VIDIOC_S_OUTPUT: - case VIDIOC_STREAMON: - case VIDIOC_G_INPUT: - case VIDIOC_OVERLAY: - case VIDIOC_S_INPUT: - { - int *p=arg; - printk ("%s: value=%d\n", s, *p); - break; - } - case VIDIOC_G_AUDIO: - case VIDIOC_S_AUDIO: - case VIDIOC_ENUMAUDIO: -#ifdef __OLD_VIDIOC_ - case VIDIOC_G_AUDIO_OLD: -#endif - { - struct v4l2_audio *p=arg; - - printk ("%s: index=%d, name=%s, capability=%d, mode=%d\n", - s,p->index, p->name,p->capability, p->mode); - break; - } - case VIDIOC_G_AUDOUT: - case VIDIOC_S_AUDOUT: - case VIDIOC_ENUMAUDOUT: -#ifdef __OLD_VIDIOC_ - case VIDIOC_G_AUDOUT_OLD: -#endif - { - struct v4l2_audioout *p=arg; - printk ("%s: index=%d, name=%s, capability=%d, mode=%d\n", s, - p->index, p->name, p->capability,p->mode); - break; - } - case VIDIOC_QBUF: - case VIDIOC_DQBUF: - case VIDIOC_QUERYBUF: - { - struct v4l2_buffer *p=arg; - struct v4l2_timecode *tc=&p->timecode; - printk ("%s: %02ld:%02d:%02d.%08ld index=%d, type=%s, " - "bytesused=%d, flags=0x%08x, " - "field=%0d, sequence=%d, memory=%s, offset/userptr=0x%08lx\n", - s, - (p->timestamp.tv_sec/3600), - (int)(p->timestamp.tv_sec/60)%60, - (int)(p->timestamp.tv_sec%60), - p->timestamp.tv_usec, - p->index, - prt_names(p->type,v4l2_type_names), - p->bytesused,p->flags, - p->field,p->sequence, - prt_names(p->memory,v4l2_memory_names), - p->m.userptr); - printk ("%s: timecode= %02d:%02d:%02d type=%d, " - "flags=0x%08x, frames=%d, userbits=0x%08x\n", - s,tc->hours,tc->minutes,tc->seconds, - tc->type, tc->flags, tc->frames, *(__u32 *) tc->userbits); - break; - } - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *p=arg; - printk ("%s: driver=%s, card=%s, bus=%s, version=0x%08x, " - "capabilities=0x%08x\n", s, - p->driver,p->card,p->bus_info, - p->version, - p->capabilities); - break; - } - case VIDIOC_G_CTRL: - case VIDIOC_S_CTRL: -#ifdef __OLD_VIDIOC_ - case VIDIOC_S_CTRL_OLD: -#endif - { - struct v4l2_control *p=arg; - printk ("%s: id=%d, value=%d\n", s, p->id, p->value); - break; - } - case VIDIOC_G_EXT_CTRLS: - case VIDIOC_S_EXT_CTRLS: - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *p = arg; - int i; - - printk("%s: ctrl_class=%d, count=%d\n", s, p->ctrl_class, p->count); - for (i = 0; i < p->count; i++) { - struct v4l2_ext_control *c = &p->controls[i]; - if (cmd == VIDIOC_G_EXT_CTRLS) - printk("%s: id=%d\n", s, c->id); - else - printk("%s: id=%d, value=%d\n", s, c->id, c->value); - } - break; - } - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: - { - struct v4l2_crop *p=arg; - /*FIXME: Should also show rect structs */ - printk ("%s: type=%d\n", s, p->type); - break; - } - case VIDIOC_CROPCAP: -#ifdef __OLD_VIDIOC_ - case VIDIOC_CROPCAP_OLD: -#endif - { - struct v4l2_cropcap *p=arg; - /*FIXME: Should also show rect structs */ - printk ("%s: type=%d\n", s, p->type); - break; - } - case VIDIOC_INT_DECODE_VBI_LINE: - { - struct v4l2_decode_vbi_line *p=arg; - printk ("%s: is_second_field=%d, ptr=0x%08lx, line=%d, " - "type=%d\n", s, - p->is_second_field,(unsigned long)p->p,p->line,p->type); - break; - } - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *p=arg; - printk ("%s: index=%d, type=%d, flags=%d, description=%s," - " pixelformat=%d\n", s, - p->index, p->type, p->flags,p->description, - p->pixelformat); - - break; - } - case VIDIOC_G_FMT: - case VIDIOC_S_FMT: - case VIDIOC_TRY_FMT: - { - struct v4l2_format *p=arg; - printk ("%s: type=%s\n", s, - prt_names(p->type,v4l2_type_names)); - switch (p->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - v4l_print_pix_fmt (s, &p->fmt.pix); - break; - default: - break; - } - } - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: - { - struct v4l2_framebuffer *p=arg; - printk ("%s: capability=%d, flags=%d, base=0x%08lx\n", s, - p->capability,p->flags, (unsigned long)p->base); - v4l_print_pix_fmt (s, &p->fmt); - break; - } - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *p=arg; - printk ("%s: tuner=%d, type=%d, frequency=%d\n", s, - p->tuner,p->type,p->frequency); - break; - } - case VIDIOC_ENUMINPUT: - { - struct v4l2_input *p=arg; - printk ("%s: index=%d, name=%s, type=%d, audioset=%d, " - "tuner=%d, std=%Ld, status=%d\n", s, - p->index,p->name,p->type,p->audioset, - p->tuner, - (unsigned long long)p->std, - p->status); - break; - } - case VIDIOC_G_JPEGCOMP: - case VIDIOC_S_JPEGCOMP: - { - struct v4l2_jpegcompression *p=arg; - printk ("%s: quality=%d, APPn=%d, APP_len=%d, COM_len=%d," - " jpeg_markers=%d\n", s, - p->quality,p->APPn,p->APP_len, - p->COM_len,p->jpeg_markers); - break; - } - case VIDIOC_G_MODULATOR: - case VIDIOC_S_MODULATOR: - { - struct v4l2_modulator *p=arg; - printk ("%s: index=%d, name=%s, capability=%d, rangelow=%d," - " rangehigh=%d, txsubchans=%d\n", s, - p->index, p->name,p->capability,p->rangelow, - p->rangehigh,p->txsubchans); - break; - } - case VIDIOC_G_MPEGCOMP: - case VIDIOC_S_MPEGCOMP: - { - struct v4l2_mpeg_compression *p=arg; - /*FIXME: Several fields not shown */ - printk ("%s: ts_pid_pmt=%d, ts_pid_audio=%d, ts_pid_video=%d, " - "ts_pid_pcr=%d, ps_size=%d, au_sample_rate=%d, " - "au_pesid=%c, vi_frame_rate=%d, vi_frames_per_gop=%d, " - "vi_bframes_count=%d, vi_pesid=%c\n", s, - p->ts_pid_pmt,p->ts_pid_audio, p->ts_pid_video, - p->ts_pid_pcr, p->ps_size, p->au_sample_rate, - p->au_pesid, p->vi_frame_rate, - p->vi_frames_per_gop, p->vi_bframes_count, - p->vi_pesid); - break; - } - case VIDIOC_ENUMOUTPUT: - { - struct v4l2_output *p=arg; - printk ("%s: index=%d, name=%s,type=%d, audioset=%d, " - "modulator=%d, std=%Ld\n", - s,p->index,p->name,p->type,p->audioset, - p->modulator, - (unsigned long long)p->std); - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *p=arg; - printk ("%s: id=%d, type=%d, name=%s, min/max=%d/%d," - " step=%d, default=%d, flags=0x%08x\n", s, - p->id,p->type,p->name,p->minimum,p->maximum, - p->step,p->default_value,p->flags); - break; - } - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *p=arg; - printk ("%s: id=%d, index=%d, name=%s\n", s, - p->id,p->index,p->name); - break; - } -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_G_REGISTER: - case VIDIOC_DBG_S_REGISTER: - { - struct v4l2_register *p=arg; - printk ("%s: i2c_id=%d, reg=%llu, val=%u\n", s, - p->i2c_id,(unsigned long long)p->reg,p->val); - - break; - } -#endif - case VIDIOC_REQBUFS: - { - struct v4l2_requestbuffers *p=arg; - printk ("%s: count=%d, type=%s, memory=%s\n", s, - p->count, - prt_names(p->type,v4l2_type_names), - prt_names(p->memory,v4l2_memory_names)); - break; - } - case VIDIOC_INT_S_AUDIO_ROUTING: - case VIDIOC_INT_S_VIDEO_ROUTING: - case VIDIOC_INT_G_AUDIO_ROUTING: - case VIDIOC_INT_G_VIDEO_ROUTING: - { - struct v4l2_routing *p=arg; - printk ("%s: input=0x%x, output=0x%x\n", s, p->input, p->output); - break; - } - case VIDIOC_INT_S_CRYSTAL_FREQ: - { - struct v4l2_crystal_freq *p=arg; - printk ("%s: freq=%u, flags=0x%x\n", s, p->freq, p->flags); - break; - } - case VIDIOC_G_SLICED_VBI_CAP: - { - struct v4l2_sliced_vbi_cap *p=arg; - printk ("%s: service_set=%d\n", s, - p->service_set); - break; - } - case VIDIOC_INT_S_VBI_DATA: - case VIDIOC_INT_G_VBI_DATA: - { - struct v4l2_sliced_vbi_data *p=arg; - printk ("%s: id=%d, field=%d, line=%d\n", s, - p->id, p->field, p->line); - break; - } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *p=arg; - printk ("%s: index=%d, id=%Ld, name=%s, fps=%d/%d, " - "framelines=%d\n", s, p->index, - (unsigned long long)p->id, p->name, - p->frameperiod.numerator, - p->frameperiod.denominator, - p->framelines); - - break; - } - case VIDIOC_G_PARM: - case VIDIOC_S_PARM: -#ifdef __OLD_VIDIOC_ - case VIDIOC_S_PARM_OLD: -#endif - { - struct v4l2_streamparm *p=arg; - printk ("%s: type=%d\n", s, p->type); - - break; - } - case VIDIOC_G_TUNER: - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *p=arg; - printk ("%s: index=%d, name=%s, type=%d, capability=%d, " - "rangelow=%d, rangehigh=%d, signal=%d, afc=%d, " - "rxsubchans=%d, audmode=%d\n", s, - p->index, p->name, p->type, - p->capability, p->rangelow,p->rangehigh, - p->rxsubchans, p->audmode, p->signal, - p->afc); - break; - } -#ifdef CONFIG_VIDEO_V4L1_COMPAT - case VIDIOCGVBIFMT: - case VIDIOCSVBIFMT: - { - struct vbi_format *p=arg; - printk ("%s: sampling_rate=%d, samples_per_line=%d, " - "sample_format=%d, start=%d/%d, count=%d/%d, flags=%d\n", s, - p->sampling_rate,p->samples_per_line, - p->sample_format,p->start[0],p->start[1], - p->count[0],p->count[1],p->flags); - break; - } - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - { - struct video_audio *p=arg; - printk ("%s: audio=%d, volume=%d, bass=%d, treble=%d, " - "flags=%d, name=%s, mode=%d, balance=%d, step=%d\n", - s,p->audio,p->volume,p->bass, p->treble, - p->flags,p->name,p->mode,p->balance,p->step); - break; - } - case VIDIOCGFBUF: - case VIDIOCSFBUF: - { - struct video_buffer *p=arg; - printk ("%s: base=%08lx, height=%d, width=%d, depth=%d, " - "bytesperline=%d\n", s, - (unsigned long) p->base, p->height, p->width, - p->depth,p->bytesperline); - break; - } - case VIDIOCGCAP: - { - struct video_capability *p=arg; - printk ("%s: name=%s, type=%d, channels=%d, audios=%d, " - "maxwidth=%d, maxheight=%d, minwidth=%d, minheight=%d\n", - s,p->name,p->type,p->channels,p->audios, - p->maxwidth,p->maxheight,p->minwidth, - p->minheight); - - break; - } - case VIDIOCGCAPTURE: - case VIDIOCSCAPTURE: - { - struct video_capture *p=arg; - printk ("%s: x=%d, y=%d, width=%d, height=%d, decimation=%d," - " flags=%d\n", s, - p->x, p->y,p->width, p->height, - p->decimation,p->flags); - break; - } - case VIDIOCGCHAN: - case VIDIOCSCHAN: - { - struct video_channel *p=arg; - printk ("%s: channel=%d, name=%s, tuners=%d, flags=%d, " - "type=%d, norm=%d\n", s, - p->channel,p->name,p->tuners, - p->flags,p->type,p->norm); - - break; - } - case VIDIOCSMICROCODE: - { - struct video_code *p=arg; - printk ("%s: loadwhat=%s, datasize=%d\n", s, - p->loadwhat,p->datasize); - break; - } - case DECODER_GET_CAPABILITIES: - { - struct video_decoder_capability *p=arg; - printk ("%s: flags=%d, inputs=%d, outputs=%d\n", s, - p->flags,p->inputs,p->outputs); - break; - } - case DECODER_INIT: - { - struct video_decoder_init *p=arg; - printk ("%s: len=%c\n", s, p->len); - break; - } - case VIDIOCGPLAYINFO: - { - struct video_info *p=arg; - printk ("%s: frame_count=%d, h_size=%d, v_size=%d, " - "smpte_timecode=%d, picture_type=%d, " - "temporal_reference=%d, user_data=%s\n", s, - p->frame_count, p->h_size, - p->v_size, p->smpte_timecode, - p->picture_type, p->temporal_reference, - p->user_data); - break; - } - case VIDIOCKEY: - { - struct video_key *p=arg; - printk ("%s: key=%s, flags=%d\n", s, - p->key, p->flags); - break; - } - case VIDIOCGMBUF: - { - struct video_mbuf *p=arg; - printk ("%s: size=%d, frames=%d, offsets=0x%08lx\n", s, - p->size, - p->frames, - (unsigned long)p->offsets); - break; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *p=arg; - printk ("%s: frame=%d, height=%d, width=%d, format=%d\n", s, - p->frame, - p->height, p->width, - p->format); - break; - } - case VIDIOCGPICT: - case VIDIOCSPICT: - case DECODER_SET_PICTURE: - { - struct video_picture *p=arg; - - printk ("%s: brightness=%d, hue=%d, colour=%d, contrast=%d," - " whiteness=%d, depth=%d, palette=%d\n", s, - p->brightness, p->hue, p->colour, - p->contrast, p->whiteness, p->depth, - p->palette); - break; - } - case VIDIOCSPLAYMODE: - { - struct video_play_mode *p=arg; - printk ("%s: mode=%d, p1=%d, p2=%d\n", s, - p->mode,p->p1,p->p2); - break; - } - case VIDIOCGTUNER: - case VIDIOCSTUNER: - { - struct video_tuner *p=arg; - printk ("%s: tuner=%d, name=%s, rangelow=%ld, rangehigh=%ld, " - "flags=%d, mode=%d, signal=%d\n", s, - p->tuner, p->name,p->rangelow, p->rangehigh, - p->flags,p->mode, p->signal); - break; - } - case VIDIOCGUNIT: - { - struct video_unit *p=arg; - printk ("%s: video=%d, vbi=%d, radio=%d, audio=%d, " - "teletext=%d\n", s, - p->video,p->vbi,p->radio,p->audio,p->teletext); - break; - } - case VIDIOCGWIN: - case VIDIOCSWIN: - { - struct video_window *p=arg; - printk ("%s: x=%d, y=%d, width=%d, height=%d, chromakey=%d," - " flags=%d, clipcount=%d\n", s, - p->x, p->y,p->width, p->height, - p->chromakey,p->flags, - p->clipcount); - break; - } - case VIDIOCGFREQ: - case VIDIOCSFREQ: - { - unsigned long *p=arg; - printk ("%s: value=%lu\n", s, *p); - break; - } -#endif - case VIDIOC_INT_AUDIO_CLOCK_FREQ: - case VIDIOC_INT_I2S_CLOCK_FREQ: - case VIDIOC_INT_S_STANDBY: - case VIDIOC_INT_RESET: - { - u32 *p=arg; - - printk ("%s: value=%d\n", s, *p); - break; - } - case VIDIOC_G_STD: - case VIDIOC_S_STD: - case VIDIOC_QUERYSTD: - { - v4l2_std_id *p=arg; - - printk ("%s: value=%Lu\n", s, (unsigned long long)*p); - break; - } - } -} /* ----------------------------------------------------------------- */ @@ -1547,7 +962,6 @@ EXPORT_SYMBOL(v4l2_prio_check); EXPORT_SYMBOL(v4l2_field_names); EXPORT_SYMBOL(v4l2_type_names); EXPORT_SYMBOL(v4l_printk_ioctl); -EXPORT_SYMBOL(v4l_printk_ioctl_arg); EXPORT_SYMBOL(v4l2_ctrl_next); EXPORT_SYMBOL(v4l2_ctrl_check); diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 959e6f6a4eff..244e440edb53 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -64,9 +64,6 @@ /* Prints the ioctl in a human-readable format */ extern void v4l_printk_ioctl(unsigned int cmd); -/* Prints the ioctl and arg in a human-readable format */ -extern void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg); - /* Use this macro for non-I2C drivers. Pass the driver name as the first arg. */ #define v4l_print_ioctl(name, cmd) \ do { \ -- cgit v1.2.3-59-g8ed1b From f3d092b84a855c44914fea0648695bef7d751266 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 23 Feb 2007 20:55:14 -0300 Subject: V4L/DVB (5304): Improve chip matching in v4l2_register The chip matching in struct v4l2_register for VIDIOC_DBG_G/S_REGISTER was rather primitive. It could not be extended to other busses besides i2c and it lacked a way to. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx25840/cx25840-core.c | 2 +- drivers/media/video/cx88/cx88-video.c | 4 ++-- drivers/media/video/pvrusb2/pvrusb2-hdw.c | 11 +++++---- drivers/media/video/pvrusb2/pvrusb2-hdw.h | 7 +++--- drivers/media/video/pvrusb2/pvrusb2-v4l2.c | 4 ++-- drivers/media/video/saa7115.c | 2 +- drivers/media/video/saa7127.c | 2 +- drivers/media/video/tvp5150.c | 2 +- drivers/media/video/upd64031a.c | 2 +- drivers/media/video/upd64083.c | 2 +- drivers/media/video/usbvision/usbvision-video.c | 4 ++-- drivers/media/video/v4l2-common.c | 30 +++++++++++++++++++++++++ include/linux/videodev2.h | 11 +++++++-- include/media/v4l2-common.h | 8 +++++++ 14 files changed, 70 insertions(+), 21 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 3ff5fc00c09d..774d2536555b 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -633,7 +633,7 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_CX25840) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index a97be1bdc31d..bdfe2af70124 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1389,7 +1389,7 @@ static int vidioc_g_register (struct file *file, void *fh, { struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; - if (reg->i2c_id != 0) + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) return -EINVAL; /* cx2388x has a 24-bit register space */ reg->val = cx_read(reg->reg&0xffffff); @@ -1401,7 +1401,7 @@ static int vidioc_s_register (struct file *file, void *fh, { struct cx88_core *core = ((struct cx8800_fh*)fh)->dev->core; - if (reg->i2c_id != 0) + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) return -EINVAL; cx_write(reg->reg&0xffffff, reg->val); return 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index a1ca0f5007e0..1cd4bb3ae260 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -3256,8 +3256,8 @@ static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw) int pvr2_hdw_register_access(struct pvr2_hdw *hdw, - u32 chip_id, u64 reg_id, - int setFl,u32 *val_ptr) + u32 match_type, u32 match_chip, u64 reg_id, + int setFl,u64 *val_ptr) { #ifdef CONFIG_VIDEO_ADV_DEBUG struct list_head *item; @@ -3268,13 +3268,16 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw, if (!capable(CAP_SYS_ADMIN)) return -EPERM; - req.i2c_id = chip_id; + req.match_type = match_type; + req.match_chip = match_chip; req.reg = reg_id; if (setFl) req.val = *val_ptr; mutex_lock(&hdw->i2c_list_lock); do { list_for_each(item,&hdw->i2c_clients) { cp = list_entry(item,struct pvr2_i2c_client,list); - if (cp->client->driver->id != chip_id) continue; + if (!v4l2_chip_match_i2c_client(cp->client, req.match_type, req.match_chip)) { + continue; + } stat = pvr2_i2c_client_cmd( cp,(setFl ? VIDIOC_DBG_S_REGISTER : VIDIOC_DBG_G_REGISTER),&req); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 566a8ef7e121..0c9cca43ff85 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -217,13 +217,14 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *, enum pvr2_v4l_type index,int); /* Direct read/write access to chip's registers: - chip_id - unique id of chip (e.g. I2C_DRIVERD_xxxx) + match_type - how to interpret match_chip (e.g. driver ID, i2c address) + match_chip - chip match value (e.g. I2C_DRIVERD_xxxx) reg_id - register number to access setFl - true to set the register, false to read it val_ptr - storage location for source / result. */ int pvr2_hdw_register_access(struct pvr2_hdw *, - u32 chip_id,u64 reg_id, - int setFl,u32 *val_ptr); + u32 match_type, u32 match_chip,u64 reg_id, + int setFl,u64 *val_ptr); /* The following entry points are all lower level things you normally don't want to worry about. */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 4fe4136204c7..5313d342666e 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -740,11 +740,11 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file, case VIDIOC_DBG_S_REGISTER: case VIDIOC_DBG_G_REGISTER: { - u32 val; + u64 val; struct v4l2_register *req = (struct v4l2_register *)arg; if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; ret = pvr2_hdw_register_access( - hdw,req->i2c_id,req->reg, + hdw,req->match_type,req->match_chip,req->reg, cmd == VIDIOC_DBG_S_REGISTER,&val); if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; break; diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index c4f066d6668e..7735b6758921 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -1425,7 +1425,7 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_SAA711X) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index bd9c4f3ad02e..654863db1591 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -619,7 +619,7 @@ static int saa7127_command(struct i2c_client *client, { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_SAA7127) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 886b5df7c9d1..d5ec05f56adf 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -955,7 +955,7 @@ static int tvp5150_command(struct i2c_client *c, { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_TVP5150) + if (!v4l2_chip_match_i2c_client(c, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index b3b5fd536dc3..28d1133a3b7a 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -167,7 +167,7 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void * { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_UPD64031A) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index 8852903e7a92..fe38224150d8 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -144,7 +144,7 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a { struct v4l2_register *reg = arg; - if (reg->i2c_id != I2C_DRIVERID_UPD64083) + if (!v4l2_chip_match_i2c_client(client, reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index ae5f42562c0c..6fc14557d623 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -521,7 +521,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, struct v4l2_register *reg = arg; int errCode; - if (reg->i2c_id != 0) + if (!v4l2_chip_match_host(reg->match_type, reg->match_chip)) return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EPERM; @@ -540,7 +540,7 @@ static int usbvision_v4l2_do_ioctl(struct inode *inode, struct file *file, PDEBUG(DBG_IOCTL, "VIDIOC_DBG_%c_REGISTER reg=0x%02X, value=0x%02X", cmd == VIDIOC_DBG_G_REGISTER ? 'G' : 'S', - (unsigned int)reg->reg, reg->val); + (unsigned int)reg->reg, (unsigned int)reg->val); return 0; } #endif diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index ddfd80c5618b..3506cb667111 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -947,6 +948,32 @@ u32 v4l2_ctrl_next(const u32 * const * ctrl_classes, u32 id) return **ctrl_classes; } +int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 match_type, u32 match_chip) +{ + switch (match_type) { + case V4L2_CHIP_MATCH_ALWAYS: + return 1; + case V4L2_CHIP_MATCH_I2C_DRIVER: + return (c != NULL && c->driver != NULL && c->driver->id == match_chip); + case V4L2_CHIP_MATCH_I2C_ADDR: + return (c != NULL && c->addr == match_chip); + default: + return 0; + } +} + +int v4l2_chip_match_host(u32 match_type, u32 match_chip) +{ + switch (match_type) { + case V4L2_CHIP_MATCH_ALWAYS: + return 1; + case V4L2_CHIP_MATCH_HOST: + return match_chip == 0; + default: + return 0; + } +} + /* ----------------------------------------------------------------- */ EXPORT_SYMBOL(v4l2_norm_to_name); @@ -970,6 +997,9 @@ EXPORT_SYMBOL(v4l2_ctrl_query_menu); EXPORT_SYMBOL(v4l2_ctrl_query_fill); EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); +EXPORT_SYMBOL(v4l2_chip_match_i2c_client); +EXPORT_SYMBOL(v4l2_chip_match_host); + /* * Local variables: * c-basic-offset: 8 diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 65a165f918c9..e3ab047f90ef 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1306,10 +1306,17 @@ struct v4l2_streamparm */ /* VIDIOC_DBG_G_REGISTER and VIDIOC_DBG_S_REGISTER */ + +#define V4L2_CHIP_MATCH_ALWAYS 0 /* Match always (match_chip is not used) */ +#define V4L2_CHIP_MATCH_HOST 1 /* Match against chip ID on host (0 for the host) */ +#define V4L2_CHIP_MATCH_I2C_DRIVER 2 /* Match against I2C driver ID */ +#define V4L2_CHIP_MATCH_I2C_ADDR 3 /* Match against I2C 7-bit address */ + struct v4l2_register { + __u32 match_type; /* Match type */ + __u32 match_chip; /* Match this chip, meaning determined by match_type */ __u64 reg; - __u32 i2c_id; /* I2C driver ID of the I2C chip, or 0 for the host */ - __u32 val; + __u64 val; }; /* diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 244e440edb53..6eaeec98ed89 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -94,6 +94,14 @@ u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id); /* ------------------------------------------------------------------------- */ +/* Register/chip ident helper function */ + +struct i2c_client; /* forward reference */ +int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 id_type, u32 chip_id); +int v4l2_chip_match_host(u32 id_type, u32 chip_id); + +/* ------------------------------------------------------------------------- */ + /* Internal ioctls */ /* VIDIOC_INT_DECODE_VBI_LINE */ -- cgit v1.2.3-59-g8ed1b From db6eb5b334a0f095290b99096f68ff24ab6df1aa Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 18 Feb 2007 14:05:02 -0300 Subject: V4L/DVB (5270): Add VIDIOC_G_ENC_INDEX ioctl The VIDIOC_G_ENC_INDEX ioctl can obtain the MPEG index from an MPEG encoder. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 9 ++++++--- drivers/media/video/videodev.c | 12 ++++++++++++ include/linux/videodev2.h | 30 ++++++++++++++++++++++++++++++ include/media/v4l2-dev.h | 2 ++ 4 files changed, 50 insertions(+), 3 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 3506cb667111..2fe97c4cb35c 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -366,13 +366,16 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", -#if 1 [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", -#endif [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS", [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", - [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS" + [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", +#if 1 + [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", + [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", + [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX" +#endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index dc9b1ef678aa..cd13e6108e5e 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1342,6 +1342,18 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_s_jpegcomp(file, fh, p); break; } + case VIDIOC_G_ENC_INDEX: + { + struct v4l2_enc_idx *p=arg; + + if (!vfd->vidioc_g_enc_index) + break; + ret=vfd->vidioc_g_enc_index(file, fh, p); + if (!ret) + dbgarg (cmd, "entries=%d, entries_cap=%d\n", + p->entries,p->entries_cap); + break; + } case VIDIOC_G_PARM: { struct v4l2_streamparm *p=arg; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e3ab047f90ef..42cfdd2d50db 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1192,6 +1192,35 @@ struct v4l2_audioout __u32 reserved[2]; }; +/* + * M P E G I N D E X + * + * NOTE: EXPERIMENTAL API + */ +#if 1 +#define V4L2_ENC_IDX_FRAME_I (0) +#define V4L2_ENC_IDX_FRAME_P (1) +#define V4L2_ENC_IDX_FRAME_B (2) +#define V4L2_ENC_IDX_FRAME_MASK (0xf) + +struct v4l2_enc_idx_entry { + __u64 offset; + __u64 pts; + __u32 length; + __u32 flags; + __u32 reserved[2]; +}; + +#define V4L2_ENC_IDX_ENTRIES (64) +struct v4l2_enc_idx { + __u32 entries; + __u32 entries_cap; + __u32 reserved[4]; + struct v4l2_enc_idx_entry entry[V4L2_ENC_IDX_ENTRIES]; +}; +#endif + + /* * D A T A S E R V I C E S ( V B I ) * @@ -1385,6 +1414,7 @@ struct v4l2_register { #if 1 #define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) +#define VIDIOC_G_ENC_INDEX _IOR ('V', 76, struct v4l2_enc_idx) #endif /* only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ #define VIDIOC_DBG_S_REGISTER _IOW ('d', 100, struct v4l2_register) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index aeec56992ef5..e5e87e44c585 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -271,6 +271,8 @@ struct video_device struct v4l2_jpegcompression *a); int (*vidioc_s_jpegcomp) (struct file *file, void *fh, struct v4l2_jpegcompression *a); + int (*vidioc_g_enc_index) (struct file *file, void *fh, + struct v4l2_enc_idx *a); /* Stream type-dependent parameter ioctls */ int (*vidioc_g_parm) (struct file *file, void *fh, -- cgit v1.2.3-59-g8ed1b From ada6ecd2bf1d97e1f3bac6d5af4c7c4390a0a5af Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 18 Feb 2007 14:56:22 -0300 Subject: V4L/DVB (5271): Add VIDIOC_TRY_ENCODER_CMD and VIDIOC_ENCODER_CMD ioctls. Add support for starting, stopping, pausing and resuming an MPEG (or similar compressed stream) encoder. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 4 +++- drivers/media/video/videodev.c | 24 ++++++++++++++++++++++++ include/linux/videodev2.h | 24 +++++++++++++++++++++++- include/media/v4l2-dev.h | 4 ++++ 4 files changed, 54 insertions(+), 2 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 2fe97c4cb35c..57aaed3239d6 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -374,7 +374,9 @@ static const char *v4l2_ioctls[] = { #if 1 [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", - [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX" + [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX", + [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", + [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD" #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index cd13e6108e5e..011938fb7e0e 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1354,6 +1354,30 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, p->entries,p->entries_cap); break; } + case VIDIOC_ENCODER_CMD: + { + struct v4l2_encoder_cmd *p=arg; + + if (!vfd->vidioc_encoder_cmd) + break; + ret=vfd->vidioc_encoder_cmd(file, fh, p); + if (!ret) + dbgarg (cmd, "cmd=%d, flags=%d\n", + p->cmd,p->flags); + break; + } + case VIDIOC_TRY_ENCODER_CMD: + { + struct v4l2_encoder_cmd *p=arg; + + if (!vfd->vidioc_try_encoder_cmd) + break; + ret=vfd->vidioc_try_encoder_cmd(file, fh, p); + if (!ret) + dbgarg (cmd, "cmd=%d, flags=%d\n", + p->cmd,p->flags); + break; + } case VIDIOC_G_PARM: { struct v4l2_streamparm *p=arg; diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 42cfdd2d50db..854875869dbd 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1193,7 +1193,7 @@ struct v4l2_audioout }; /* - * M P E G I N D E X + * M P E G S E R V I C E S * * NOTE: EXPERIMENTAL API */ @@ -1218,6 +1218,26 @@ struct v4l2_enc_idx { __u32 reserved[4]; struct v4l2_enc_idx_entry entry[V4L2_ENC_IDX_ENTRIES]; }; + + +#define V4L2_ENC_CMD_START (0) +#define V4L2_ENC_CMD_STOP (1) +#define V4L2_ENC_CMD_PAUSE (2) +#define V4L2_ENC_CMD_RESUME (3) + +/* Flags for V4L2_ENC_CMD_STOP */ +#define V4L2_ENC_CMD_STOP_AT_GOP_END (1 << 0) + +struct v4l2_encoder_cmd { + __u32 cmd; + __u32 flags; + union { + struct { + __u32 data[8]; + } raw; + }; +}; + #endif @@ -1415,6 +1435,8 @@ struct v4l2_register { #define VIDIOC_ENUM_FRAMESIZES _IOWR ('V', 74, struct v4l2_frmsizeenum) #define VIDIOC_ENUM_FRAMEINTERVALS _IOWR ('V', 75, struct v4l2_frmivalenum) #define VIDIOC_G_ENC_INDEX _IOR ('V', 76, struct v4l2_enc_idx) +#define VIDIOC_ENCODER_CMD _IOWR ('V', 77, struct v4l2_encoder_cmd) +#define VIDIOC_TRY_ENCODER_CMD _IOWR ('V', 78, struct v4l2_encoder_cmd) #endif /* only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ #define VIDIOC_DBG_S_REGISTER _IOW ('d', 100, struct v4l2_register) diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index e5e87e44c585..1dd3d3239ecf 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -273,6 +273,10 @@ struct video_device struct v4l2_jpegcompression *a); int (*vidioc_g_enc_index) (struct file *file, void *fh, struct v4l2_enc_idx *a); + int (*vidioc_encoder_cmd) (struct file *file, void *fh, + struct v4l2_encoder_cmd *a); + int (*vidioc_try_encoder_cmd) (struct file *file, void *fh, + struct v4l2_encoder_cmd *a); /* Stream type-dependent parameter ioctls */ int (*vidioc_g_parm) (struct file *file, void *fh, -- cgit v1.2.3-59-g8ed1b From 7a7cd1920969dd9da4e0d99aab573b3eba24c799 Mon Sep 17 00:00:00 2001 From: Oliver Endriss Date: Tue, 13 Mar 2007 23:44:57 -0300 Subject: V4L/DVB (5441): Saa7146: Fix allocation of clipping memory Olaf Hering pointed out that SAA7146_CLIPPING_MEM would become very large for PAGE_SIZE > 4K. In fact, the number of clipping windows is limited to 16, and calculate_clipping_registers_rect() does not use more than 256 bytes. SAA7146_CLIPPING_MEM adjusted accordingly. Thanks-to: Olaf Hering Acked-by: Michael Hunold Signed-off-by: Oliver Endriss Signed-off-by: Mauro Carvalho Chehab --- include/media/saa7146_vv.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/media') diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 83fe2e3d1e25..50e33b0e9349 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -239,7 +239,8 @@ void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits); #define SAA7146_HPS_SYNC_PORT_B 0x01 /* some memory sizes */ -#define SAA7146_CLIPPING_MEM (14*PAGE_SIZE) +/* max. 16 clipping rectangles */ +#define SAA7146_CLIPPING_MEM (16 * 4 * sizeof(u32)) /* some defines for the various clipping-modes */ #define SAA7146_CLIPPING_RECT 0x4 -- cgit v1.2.3-59-g8ed1b From 5eee72e88416ef11f55791626440ac3c9018c4c0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:00 -0300 Subject: V4L/DVB (5268): Add support for three new MPEG controls. Added V4L2_CID_MPEG_AUDIO_MUTE, V4L2_CID_MPEG_VIDEO_MUTE and V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS controls together with their implementation in the cx2341x module. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cx2341x.c | 72 ++++++++++++++++++++++++++++++++++++--- drivers/media/video/v4l2-common.c | 10 ++++++ include/linux/videodev2.h | 4 +++ include/media/cx2341x.h | 4 +++ 4 files changed, 85 insertions(+), 5 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index d60cd5ecf821..88dbdddeec42 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -51,6 +51,7 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_AUDIO_MODE_EXTENSION, V4L2_CID_MPEG_AUDIO_EMPHASIS, V4L2_CID_MPEG_AUDIO_CRC, + V4L2_CID_MPEG_AUDIO_MUTE, V4L2_CID_MPEG_VIDEO_ENCODING, V4L2_CID_MPEG_VIDEO_ASPECT, V4L2_CID_MPEG_VIDEO_B_FRAMES, @@ -60,6 +61,8 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_VIDEO_BITRATE, V4L2_CID_MPEG_VIDEO_BITRATE_PEAK, V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION, + V4L2_CID_MPEG_VIDEO_MUTE, + V4L2_CID_MPEG_VIDEO_MUTE_YUV, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE, @@ -71,6 +74,7 @@ const u32 cx2341x_mpeg_ctrls[] = { V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP, + V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS, 0 }; @@ -102,6 +106,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_AUDIO_CRC: ctrl->value = params->audio_crc; break; + case V4L2_CID_MPEG_AUDIO_MUTE: + ctrl->value = params->audio_mute; + break; case V4L2_CID_MPEG_VIDEO_ENCODING: ctrl->value = params->video_encoding; break; @@ -129,6 +136,12 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: ctrl->value = params->video_temporal_decimation; break; + case V4L2_CID_MPEG_VIDEO_MUTE: + ctrl->value = params->video_mute; + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + ctrl->value = params->video_mute_yuv; + break; case V4L2_CID_MPEG_STREAM_TYPE: ctrl->value = params->stream_type; break; @@ -168,6 +181,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: ctrl->value = params->video_chroma_median_filter_bottom; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + ctrl->value = params->stream_insert_nav_packets; + break; default: return -EINVAL; } @@ -201,6 +217,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_AUDIO_CRC: params->audio_crc = ctrl->value; break; + case V4L2_CID_MPEG_AUDIO_MUTE: + params->audio_mute = ctrl->value; + break; case V4L2_CID_MPEG_VIDEO_ASPECT: params->video_aspect = ctrl->value; break; @@ -243,6 +262,12 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: params->video_temporal_decimation = ctrl->value; break; + case V4L2_CID_MPEG_VIDEO_MUTE: + params->video_mute = (ctrl->value != 0); + break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: + params->video_mute_yuv = ctrl->value; + break; case V4L2_CID_MPEG_STREAM_TYPE: params->stream_type = ctrl->value; params->video_encoding = @@ -290,6 +315,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: params->video_chroma_median_filter_bottom = ctrl->value; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + params->stream_insert_nav_packets = ctrl->value; + break; default: return -EINVAL; } @@ -336,6 +364,9 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma case V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM: name = "Median Chroma Filter Minimum"; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + name = "Insert Navigation Packets"; + break; default: return v4l2_ctrl_query_fill(qctrl, min, max, step, def); @@ -350,6 +381,12 @@ static int cx2341x_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 ma min = 0; step = 1; break; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; + min = 0; + max = 1; + step = 1; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; @@ -505,6 +542,9 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; + case V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS: + return cx2341x_ctrl_query_fill(qctrl, 0, 1, 1, 0); + default: return v4l2_ctrl_query_fill_std(qctrl); @@ -656,6 +696,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) /* stream */ .stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS, .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE, + .stream_insert_nav_packets = 0, /* audio */ .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000, @@ -665,6 +706,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4, .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE, .audio_crc = V4L2_MPEG_AUDIO_CRC_NONE, + .audio_mute = 0, /* video */ .video_encoding = V4L2_MPEG_VIDEO_ENCODING_MPEG_2, @@ -676,6 +718,8 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p) .video_bitrate = 6000000, .video_bitrate_peak = 8000000, .video_temporal_decimation = 0, + .video_mute = 0, + .video_mute_yuv = 0x008080, /* YCbCr value for black */ /* encoding filters */ .video_spatial_filter_mode = V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_MANUAL, @@ -779,6 +823,10 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, err = cx2341x_api(priv, func, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new->audio_properties); if (err) return err; } + if (old == NULL || old->audio_mute != new->audio_mute) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_AUDIO, 1, new->audio_mute); + if (err) return err; + } if (old == NULL || old->video_bitrate_mode != new->video_bitrate_mode || old->video_bitrate != new->video_bitrate || old->video_bitrate_peak != new->video_bitrate_peak) { @@ -826,6 +874,15 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func, new->video_temporal_decimation); if (err) return err; } + if (old == NULL || old->video_mute != new->video_mute || + (new->video_mute && old->video_mute_yuv != new->video_mute_yuv)) { + err = cx2341x_api(priv, func, CX2341X_ENC_MUTE_VIDEO, 1, new->video_mute | (new->video_mute_yuv << 8)); + if (err) return err; + } + if (old == NULL || old->stream_insert_nav_packets != new->stream_insert_nav_packets) { + err = cx2341x_api(priv, func, CX2341X_ENC_MISC, 2, 7, new->stream_insert_nav_packets); + if (err) return err; + } return 0; } @@ -854,18 +911,22 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) int temporal = p->video_temporal_filter; /* Stream */ - printk(KERN_INFO "%s: Stream: %s\n", + printk(KERN_INFO "%s: Stream: %s", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE)); + if (p->stream_insert_nav_packets) + printk(" (with navigation packets)"); + printk("\n"); printk(KERN_INFO "%s: VBI Format: %s\n", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT)); /* Video */ - printk(KERN_INFO "%s: Video: %dx%d, %d fps\n", + printk(KERN_INFO "%s: Video: %dx%d, %d fps%s\n", prefix, p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1), - p->is_50hz ? 25 : 30); + p->is_50hz ? 25 : 30, + (p->video_mute) ? " (muted)" : ""); printk(KERN_INFO "%s: Video: %s, %s, %s, %d", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING), @@ -886,12 +947,13 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix) } /* Audio */ - printk(KERN_INFO "%s: Audio: %s, %s, %s, %s", + printk(KERN_INFO "%s: Audio: %s, %s, %s, %s%s", prefix, cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING), cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE), - cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE)); + cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE), + p->audio_mute ? " (muted)" : ""); if (p->audio_mode == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO) { printk(", %s", cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_MODE_EXTENSION)); diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 54747606eae1..43a8467f0209 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -680,6 +680,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION: name = "Audio Stereo Mode Extension"; break; case V4L2_CID_MPEG_AUDIO_EMPHASIS: name = "Audio Emphasis"; break; case V4L2_CID_MPEG_AUDIO_CRC: name = "Audio CRC"; break; + case V4L2_CID_MPEG_AUDIO_MUTE: name = "Audio Mute"; break; case V4L2_CID_MPEG_VIDEO_ENCODING: name = "Video Encoding"; break; case V4L2_CID_MPEG_VIDEO_ASPECT: name = "Video Aspect"; break; case V4L2_CID_MPEG_VIDEO_B_FRAMES: name = "Video B Frames"; break; @@ -690,6 +691,8 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste case V4L2_CID_MPEG_VIDEO_BITRATE: name = "Video Bitrate"; break; case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: name = "Video Peak Bitrate"; break; case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: name = "Video Temporal Decimation"; break; + case V4L2_CID_MPEG_VIDEO_MUTE: name = "Video Mute"; break; + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: name = "Video Mute YUV"; break; case V4L2_CID_MPEG_STREAM_TYPE: name = "Stream Type"; break; case V4L2_CID_MPEG_STREAM_PID_PMT: name = "Stream PMT Program ID"; break; case V4L2_CID_MPEG_STREAM_PID_AUDIO: name = "Stream Audio Program ID"; break; @@ -705,6 +708,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste switch (qctrl->id) { case V4L2_CID_AUDIO_MUTE: case V4L2_CID_AUDIO_LOUDNESS: + case V4L2_CID_MPEG_AUDIO_MUTE: case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: case V4L2_CID_MPEG_VIDEO_PULLDOWN: qctrl->type = V4L2_CTRL_TYPE_BOOLEAN; @@ -838,6 +842,8 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) V4L2_MPEG_AUDIO_CRC_NONE, V4L2_MPEG_AUDIO_CRC_CRC16, 1, V4L2_MPEG_AUDIO_CRC_NONE); + case V4L2_CID_MPEG_AUDIO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); case V4L2_CID_MPEG_VIDEO_ENCODING: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, @@ -867,6 +873,10 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl) return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000); case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION: return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0); + case V4L2_CID_MPEG_VIDEO_MUTE: + return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0); + case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */ + return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_TYPE_MPEG2_PS, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index 441b877bf150..7b83d17c4cdd 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1037,6 +1037,7 @@ enum v4l2_mpeg_audio_crc { V4L2_MPEG_AUDIO_CRC_NONE = 0, V4L2_MPEG_AUDIO_CRC_CRC16 = 1, }; +#define V4L2_CID_MPEG_AUDIO_MUTE (V4L2_CID_MPEG_BASE+109) /* MPEG video */ #define V4L2_CID_MPEG_VIDEO_ENCODING (V4L2_CID_MPEG_BASE+200) @@ -1063,6 +1064,8 @@ enum v4l2_mpeg_video_bitrate_mode { #define V4L2_CID_MPEG_VIDEO_BITRATE (V4L2_CID_MPEG_BASE+207) #define V4L2_CID_MPEG_VIDEO_BITRATE_PEAK (V4L2_CID_MPEG_BASE+208) #define V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION (V4L2_CID_MPEG_BASE+209) +#define V4L2_CID_MPEG_VIDEO_MUTE (V4L2_CID_MPEG_BASE+210) +#define V4L2_CID_MPEG_VIDEO_MUTE_YUV (V4L2_CID_MPEG_BASE+211) /* MPEG-class control IDs specific to the CX2584x driver as defined by V4L2 */ #define V4L2_CID_MPEG_CX2341X_BASE (V4L2_CTRL_CLASS_MPEG | 0x1000) @@ -1103,6 +1106,7 @@ enum v4l2_mpeg_cx2341x_video_median_filter_type { #define V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+8) #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM (V4L2_CID_MPEG_CX2341X_BASE+9) #define V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP (V4L2_CID_MPEG_CX2341X_BASE+10) +#define V4L2_CID_MPEG_CX2341X_STREAM_INSERT_NAV_PACKETS (V4L2_CID_MPEG_CX2341X_BASE+11) /* * T U N I N G diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index d758a52cf556..583b0621ff43 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h @@ -40,6 +40,7 @@ struct cx2341x_mpeg_params { /* stream */ enum v4l2_mpeg_stream_type stream_type; enum v4l2_mpeg_stream_vbi_fmt stream_vbi_fmt; + u16 stream_insert_nav_packets; /* audio */ enum v4l2_mpeg_audio_sampling_freq audio_sampling_freq; @@ -50,6 +51,7 @@ struct cx2341x_mpeg_params { enum v4l2_mpeg_audio_emphasis audio_emphasis; enum v4l2_mpeg_audio_crc audio_crc; u16 audio_properties; + u16 audio_mute; /* video */ enum v4l2_mpeg_video_encoding video_encoding; @@ -61,6 +63,8 @@ struct cx2341x_mpeg_params { u32 video_bitrate; u32 video_bitrate_peak; u16 video_temporal_decimation; + u16 video_mute; + u32 video_mute_yuv; /* encoding filters */ enum v4l2_mpeg_cx2341x_video_spatial_filter_mode video_spatial_filter_mode; -- cgit v1.2.3-59-g8ed1b From b2787845fb91da18ebb079dc9297f92d990e9fe1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:02 -0300 Subject: V4L/DVB (5289): Add support for video output overlays. Add V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY support. Also add support for local and global alpha overlays. Add new field enums V4L2_FIELD_INTERLACED_TB and V4L2_FIELD_INTERLACED_BT. These changes are needed to support the ivtv On Screen Display features. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/v4l2-common.c | 5 ++- drivers/media/video/videodev.c | 23 ++++++++++++++ include/linux/videodev2.h | 66 ++++++++++++++++++++++++++------------- include/media/v4l2-dev.h | 8 +++++ 4 files changed, 79 insertions(+), 23 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 43a8467f0209..740ea5a9202f 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -260,6 +260,8 @@ char *v4l2_field_names[] = { [V4L2_FIELD_SEQ_TB] = "seq-tb", [V4L2_FIELD_SEQ_BT] = "seq-bt", [V4L2_FIELD_ALTERNATE] = "alternate", + [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", + [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", }; char *v4l2_type_names[] = { @@ -269,7 +271,8 @@ char *v4l2_type_names[] = { [V4L2_BUF_TYPE_VBI_CAPTURE] = "vbi-cap", [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap", - [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "slicec-vbi-out", + [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", }; diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 011938fb7e0e..5c9f2116d7bf 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -318,6 +318,7 @@ static char *v4l2_type_names_FIXME[] = { [V4L2_BUF_TYPE_VBI_OUTPUT] = "vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT] = "sliced-vbi-out", [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-capture", + [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "video-out-over", [V4L2_BUF_TYPE_PRIVATE] = "private", }; @@ -330,6 +331,8 @@ static char *v4l2_field_names_FIXME[] = { [V4L2_FIELD_SEQ_TB] = "seq-tb", [V4L2_FIELD_SEQ_BT] = "seq-bt", [V4L2_FIELD_ALTERNATE] = "alternate", + [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb", + [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt", }; #define prt_names(a,arr) (((a)>=0)&&((a)vidioc_try_fmt_vbi_output) return (0); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_output_overlay) + return (0); + break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_try_fmt_type_private) return (0); @@ -525,6 +532,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_enum_fmt_vbi_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_enum_fmt_output_overlay) + ret=vfd->vidioc_enum_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_PRIVATE: if (vfd->vidioc_enum_fmt_type_private) ret=vfd->vidioc_enum_fmt_type_private(file, @@ -582,6 +593,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_g_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_g_fmt_output_overlay) + ret=vfd->vidioc_g_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_g_fmt_vbi_output) ret=vfd->vidioc_g_fmt_vbi_output(file, fh, f); @@ -630,6 +645,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_s_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_s_fmt_output_overlay) + ret=vfd->vidioc_s_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_s_fmt_vbi_output) ret=vfd->vidioc_s_fmt_vbi_output(file, @@ -680,6 +699,10 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, ret=vfd->vidioc_try_fmt_video_output(file, fh, f); break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (vfd->vidioc_try_fmt_output_overlay) + ret=vfd->vidioc_try_fmt_output_overlay(file, fh, f); + break; case V4L2_BUF_TYPE_VBI_OUTPUT: if (vfd->vidioc_try_fmt_vbi_output) ret=vfd->vidioc_try_fmt_vbi_output(file, diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index e4ed5667a55d..a08ef2c16300 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -96,44 +96,60 @@ * E N U M S */ enum v4l2_field { - V4L2_FIELD_ANY = 0, /* driver can choose from none, - top, bottom, interlaced - depending on whatever it thinks - is approximate ... */ - V4L2_FIELD_NONE = 1, /* this device has no fields ... */ - V4L2_FIELD_TOP = 2, /* top field only */ - V4L2_FIELD_BOTTOM = 3, /* bottom field only */ - V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */ - V4L2_FIELD_SEQ_TB = 5, /* both fields sequential into one - buffer, top-bottom order */ - V4L2_FIELD_SEQ_BT = 6, /* same as above + bottom-top order */ - V4L2_FIELD_ALTERNATE = 7, /* both fields alternating into - separate buffers */ + V4L2_FIELD_ANY = 0, /* driver can choose from none, + top, bottom, interlaced + depending on whatever it thinks + is approximate ... */ + V4L2_FIELD_NONE = 1, /* this device has no fields ... */ + V4L2_FIELD_TOP = 2, /* top field only */ + V4L2_FIELD_BOTTOM = 3, /* bottom field only */ + V4L2_FIELD_INTERLACED = 4, /* both fields interlaced */ + V4L2_FIELD_SEQ_TB = 5, /* both fields sequential into one + buffer, top-bottom order */ + V4L2_FIELD_SEQ_BT = 6, /* same as above + bottom-top order */ + V4L2_FIELD_ALTERNATE = 7, /* both fields alternating into + separate buffers */ + V4L2_FIELD_INTERLACED_TB = 8, /* both fields interlaced, top field + first and the top field is + transmitted first */ + V4L2_FIELD_INTERLACED_BT = 9, /* both fields interlaced, top field + first and the bottom field is + transmitted first */ }; #define V4L2_FIELD_HAS_TOP(field) \ ((field) == V4L2_FIELD_TOP ||\ (field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT ||\ (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) #define V4L2_FIELD_HAS_BOTTOM(field) \ ((field) == V4L2_FIELD_BOTTOM ||\ (field) == V4L2_FIELD_INTERLACED ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT ||\ (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) #define V4L2_FIELD_HAS_BOTH(field) \ ((field) == V4L2_FIELD_INTERLACED ||\ - (field) == V4L2_FIELD_SEQ_TB ||\ + (field) == V4L2_FIELD_INTERLACED_TB ||\ + (field) == V4L2_FIELD_INTERLACED_BT ||\ + (field) == V4L2_FIELD_SEQ_TB ||\ (field) == V4L2_FIELD_SEQ_BT) enum v4l2_buf_type { - V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, - V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, - V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, - V4L2_BUF_TYPE_VBI_CAPTURE = 4, - V4L2_BUF_TYPE_VBI_OUTPUT = 5, - V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, - V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, - V4L2_BUF_TYPE_PRIVATE = 0x80, + V4L2_BUF_TYPE_VIDEO_CAPTURE = 1, + V4L2_BUF_TYPE_VIDEO_OUTPUT = 2, + V4L2_BUF_TYPE_VIDEO_OVERLAY = 3, + V4L2_BUF_TYPE_VBI_CAPTURE = 4, + V4L2_BUF_TYPE_VBI_OUTPUT = 5, + V4L2_BUF_TYPE_SLICED_VBI_CAPTURE = 6, + V4L2_BUF_TYPE_SLICED_VBI_OUTPUT = 7, +#if 1 + /* Experimental */ + V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY = 8, +#endif + V4L2_BUF_TYPE_PRIVATE = 0x80, }; enum v4l2_ctrl_type { @@ -228,6 +244,7 @@ struct v4l2_capability #define V4L2_CAP_SLICED_VBI_OUTPUT 0x00000080 /* Is a sliced VBI output device */ #define V4L2_CAP_RDS_CAPTURE 0x00000100 /* RDS data capture */ #define V4L2_CAP_VIDEO_OUTPUT_POS 0x00000200 /* Video output can have x,y coords */ +#define V4L2_CAP_VIDEO_OUTPUT_OVERLAY 0x00000400 /* Can do video output overlay */ #define V4L2_CAP_TUNER 0x00010000 /* has a tuner */ #define V4L2_CAP_AUDIO 0x00020000 /* has audio support */ @@ -599,10 +616,14 @@ struct v4l2_framebuffer #define V4L2_FBUF_CAP_CHROMAKEY 0x0002 #define V4L2_FBUF_CAP_LIST_CLIPPING 0x0004 #define V4L2_FBUF_CAP_BITMAP_CLIPPING 0x0008 +#define V4L2_FBUF_CAP_LOCAL_ALPHA 0x0010 +#define V4L2_FBUF_CAP_GLOBAL_ALPHA 0x0020 /* Flags for the 'flags' field. */ #define V4L2_FBUF_FLAG_PRIMARY 0x0001 #define V4L2_FBUF_FLAG_OVERLAY 0x0002 #define V4L2_FBUF_FLAG_CHROMAKEY 0x0004 +#define V4L2_FBUF_FLAG_LOCAL_ALPHA 0x0008 +#define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010 struct v4l2_clip { @@ -618,6 +639,7 @@ struct v4l2_window struct v4l2_clip __user *clips; __u32 clipcount; void __user *bitmap; + __u8 global_alpha; }; /* diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 1dd3d3239ecf..2693f3ae6ffb 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -127,6 +127,8 @@ struct video_device struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_video_output)(struct file *file, void *fh, struct v4l2_fmtdesc *f); + int (*vidioc_enum_fmt_output_overlay) (struct file *file, void *fh, + struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_vbi_output) (struct file *file, void *fh, struct v4l2_fmtdesc *f); int (*vidioc_enum_fmt_type_private)(struct file *file, void *fh, @@ -145,6 +147,8 @@ struct video_device struct v4l2_format *f); int (*vidioc_g_fmt_video_output)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_g_fmt_output_overlay) (struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_g_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -162,6 +166,8 @@ struct video_device struct v4l2_format *f); int (*vidioc_s_fmt_video_output)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_s_fmt_output_overlay) (struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_s_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); @@ -178,6 +184,8 @@ struct video_device struct v4l2_format *f); int (*vidioc_try_fmt_video_output)(struct file *file, void *fh, struct v4l2_format *f); + int (*vidioc_try_fmt_output_overlay)(struct file *file, void *fh, + struct v4l2_format *f); int (*vidioc_try_fmt_type_private)(struct file *file, void *fh, struct v4l2_format *f); -- cgit v1.2.3-59-g8ed1b From 045290b2a90ff1be60196a061aadecf70eb6bcc3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:04 -0300 Subject: V4L/DVB (5290): Add support for VIDIOC_INT_G/S_STD_OUTPUT Added VIDIOC_INT_G_STD_OUTPUT and VIDIOC_INT_S_STD_OUTPUT to allow drivers to set the TV standard for video output separately from the video capture. This is needed for cx23415 support where the decoder is separate from the encoder and can have a different TV standard. Modified the saa7127 module to listen to VIDIOC_INT_G/S_STD_OUTPUT instead of VIDIOC_G/S_STD. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7127.c | 4 ++-- drivers/media/video/v4l2-common.c | 5 ++++- include/media/v4l2-common.h | 8 ++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 654863db1591..50dbb76d4a7f 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -550,12 +550,12 @@ static int saa7127_command(struct i2c_client *client, struct v4l2_routing *route = arg; switch (cmd) { - case VIDIOC_S_STD: + case VIDIOC_INT_S_STD_OUTPUT: if (state->std == *(v4l2_std_id *)arg) break; return saa7127_set_std(client, *(v4l2_std_id *)arg); - case VIDIOC_G_STD: + case VIDIOC_INT_G_STD_OUTPUT: *(v4l2_std_id *)arg = state->std; break; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 740ea5a9202f..4a3635cd6f9e 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -420,7 +420,10 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(VIDIOC_INT_G_AUDIO_ROUTING)] = "VIDIOC_INT_G_AUDIO_ROUTING", [_IOC_NR(VIDIOC_INT_S_VIDEO_ROUTING)] = "VIDIOC_INT_S_VIDEO_ROUTING", [_IOC_NR(VIDIOC_INT_G_VIDEO_ROUTING)] = "VIDIOC_INT_G_VIDEO_ROUTING", - [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ" + [_IOC_NR(VIDIOC_INT_S_CRYSTAL_FREQ)] = "VIDIOC_INT_S_CRYSTAL_FREQ", + [_IOC_NR(VIDIOC_INT_INIT)] = "VIDIOC_INT_INIT", + [_IOC_NR(VIDIOC_INT_G_STD_OUTPUT)] = "VIDIOC_INT_G_STD_OUTPUT", + [_IOC_NR(VIDIOC_INT_S_STD_OUTPUT)] = "VIDIOC_INT_S_STD_OUTPUT", }; #define V4L2_INT_IOCTLS ARRAY_SIZE(v4l2_int_ioctls) diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index 6eaeec98ed89..abb9ce9b21b8 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -254,4 +254,12 @@ struct v4l2_crystal_freq { default values. */ #define VIDIOC_INT_INIT _IOW ('d', 114, u32) +/* Set v4l2_std_id for video OUTPUT devices. This is ignored by + video input devices. */ +#define VIDIOC_INT_S_STD_OUTPUT _IOW ('d', 115, v4l2_std_id) + +/* Get v4l2_std_id for video OUTPUT devices. This is ignored by + video input devices. */ +#define VIDIOC_INT_G_STD_OUTPUT _IOW ('d', 116, v4l2_std_id) + #endif /* V4L2_COMMON_H_ */ -- cgit v1.2.3-59-g8ed1b From 3434eb7e14d9587ee56f3462bcfa5726b62dadb9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:08 -0300 Subject: V4L/DVB (5306): Add support for VIDIOC_G_CHIP_IDENT VIDIOC_G_CHIP_IDENT improves debugging of card problems: it can be used to detect which chips are on the board and based on that information selected register dumps can be made, making it easy to debug complicated media chips containing tens or hundreds of registers. This ioctl replaces the internal VIDIOC_INT_G_CHIP_IDENT ioctl. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cafe_ccic.c | 8 +++- drivers/media/video/cx25840/cx25840-core.c | 9 +++-- drivers/media/video/cx25840/cx25840-core.h | 3 +- drivers/media/video/ov7670.c | 6 +-- drivers/media/video/saa7115.c | 9 ++--- drivers/media/video/saa7127.c | 8 ++-- drivers/media/video/v4l2-common.c | 21 +++++++++- drivers/media/video/videodev.c | 10 +++++ include/linux/videodev2.h | 10 +++++ include/media/v4l2-chip-ident.h | 62 ++++++++++++++++++++++++++++++ include/media/v4l2-common.h | 39 +------------------ include/media/v4l2-dev.h | 2 + 12 files changed, 130 insertions(+), 57 deletions(-) create mode 100644 include/media/v4l2-chip-ident.h (limited to 'include/media') diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c index 710c11a68296..4d4db7b2b611 100644 --- a/drivers/media/video/cafe_ccic.c +++ b/drivers/media/video/cafe_ccic.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -164,7 +165,7 @@ struct cafe_camera struct tasklet_struct s_tasklet; /* Current operating parameters */ - enum v4l2_chip_ident sensor_type; /* Currently ov7670 only */ + u32 sensor_type; /* Currently ov7670 only */ struct v4l2_pix_format pix_format; /* Locks */ @@ -818,6 +819,7 @@ static int __cafe_cam_reset(struct cafe_camera *cam) */ static int cafe_cam_init(struct cafe_camera *cam) { + struct v4l2_chip_ident chip = { V4L2_CHIP_MATCH_I2C_ADDR, 0, 0, 0 }; int ret; mutex_lock(&cam->s_mutex); @@ -827,9 +829,11 @@ static int cafe_cam_init(struct cafe_camera *cam) ret = __cafe_cam_reset(cam); if (ret) goto out; - ret = __cafe_cam_cmd(cam, VIDIOC_INT_G_CHIP_IDENT, &cam->sensor_type); + chip.match_chip = cam->sensor->addr; + ret = __cafe_cam_cmd(cam, VIDIOC_G_CHIP_IDENT, &chip); if (ret) goto out; + cam->sensor_type = chip.ident; // if (cam->sensor->addr != OV7xx0_SID) { if (cam->sensor_type != V4L2_IDENT_OV7670) { cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr); diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 774d2536555b..1757a588970f 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "cx25840-core.h" @@ -827,9 +828,8 @@ static int cx25840_command(struct i2c_client *client, unsigned int cmd, cx25840_initialize(client, 0); break; - case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = state->id; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->id, state->rev); default: return -EINVAL; @@ -847,7 +847,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, { struct i2c_client *client; struct cx25840_state *state; - enum v4l2_chip_ident id; + u32 id; u16 device_id; /* Check if the adapter supports the needed features @@ -902,6 +902,7 @@ static int cx25840_detect_client(struct i2c_adapter *adapter, int address, state->audmode = V4L2_TUNER_MODE_LANG1; state->vbi_line_offset = 8; state->id = id; + state->rev = device_id; i2c_attach_client(client); diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 28049064dd7d..f4b56d2fd6b6 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -43,7 +43,8 @@ struct cx25840_state { u32 audclk_freq; int audmode; int vbi_line_offset; - enum v4l2_chip_ident id; + u32 id; + u32 rev; int is_cx25836; }; diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c index 5ed0adc4ca26..5234762c5427 100644 --- a/drivers/media/video/ov7670.c +++ b/drivers/media/video/ov7670.c @@ -15,6 +15,7 @@ #include #include #include +#include #include @@ -1270,9 +1271,8 @@ static int ov7670_command(struct i2c_client *client, unsigned int cmd, void *arg) { switch (cmd) { - case VIDIOC_INT_G_CHIP_IDENT: - * (enum v4l2_chip_ident *) arg = V4L2_IDENT_OV7670; - return 0; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0); case VIDIOC_INT_RESET: ov7670_reset(client); diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c index 4d5bbd859de1..26c9b64c748c 100644 --- a/drivers/media/video/saa7115.c +++ b/drivers/media/video/saa7115.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -80,7 +81,7 @@ struct saa711x_state { int sat; int width; int height; - enum v4l2_chip_ident ident; + u32 ident; u32 audclk_freq; u32 crystal_freq; u8 ucgc; @@ -1232,7 +1233,6 @@ static void saa711x_decode_vbi_line(struct i2c_client *client, static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct saa711x_state *state = i2c_get_clientdata(client); - int *iarg = arg; /* ioctls to allow direct access to the saa7115 registers for testing */ switch (cmd) { @@ -1437,9 +1437,8 @@ static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *ar } #endif - case VIDIOC_INT_G_CHIP_IDENT: - *iarg = state->ident; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); default: return -EINVAL; diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c index 50dbb76d4a7f..9f986930490f 100644 --- a/drivers/media/video/saa7127.c +++ b/drivers/media/video/saa7127.c @@ -54,6 +54,7 @@ #include #include #include +#include #include static int debug = 0; @@ -234,7 +235,7 @@ static struct i2c_reg_value saa7127_init_config_50hz[] = { struct saa7127_state { v4l2_std_id std; - enum v4l2_chip_ident ident; + u32 ident; enum saa7127_input_type input_type; enum saa7127_output_type output_type; int video_enable; @@ -650,9 +651,8 @@ static int saa7127_command(struct i2c_client *client, break; } - case VIDIOC_INT_G_CHIP_IDENT: - *(enum v4l2_chip_ident *)arg = state->ident; - break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, 0); default: return -EINVAL; diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index 4a3635cd6f9e..49f1df74aa21 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -60,6 +60,7 @@ #include #define __OLD_VIDIOC_ /* To allow fixing old calls*/ #include +#include #ifdef CONFIG_KMOD #include @@ -383,6 +384,8 @@ static const char *v4l2_ioctls[] = { [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", + + [_IOC_NR(VIDIOC_G_CHIP_IDENT)] = "VIDIOC_G_CHIP_IDENT", #endif }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) @@ -413,7 +416,6 @@ static const char *v4l2_int_ioctls[] = { [_IOC_NR(VIDIOC_INT_DECODE_VBI_LINE)] = "VIDIOC_INT_DECODE_VBI_LINE", [_IOC_NR(VIDIOC_INT_S_VBI_DATA)] = "VIDIOC_INT_S_VBI_DATA", [_IOC_NR(VIDIOC_INT_G_VBI_DATA)] = "VIDIOC_INT_G_VBI_DATA", - [_IOC_NR(VIDIOC_INT_G_CHIP_IDENT)] = "VIDIOC_INT_G_CHIP_IDENT", [_IOC_NR(VIDIOC_INT_I2S_CLOCK_FREQ)] = "VIDIOC_INT_I2S_CLOCK_FREQ", [_IOC_NR(VIDIOC_INT_S_STANDBY)] = "VIDIOC_INT_S_STANDBY", [_IOC_NR(VIDIOC_INT_S_AUDIO_ROUTING)] = "VIDIOC_INT_S_AUDIO_ROUTING", @@ -981,6 +983,22 @@ int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 match_type, u32 match_c } } +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, + u32 ident, u32 revision) +{ + if (!v4l2_chip_match_i2c_client(c, chip->match_type, chip->match_chip)) + return 0; + if (chip->ident == V4L2_IDENT_NONE) { + chip->ident = ident; + chip->revision = revision; + } + else { + chip->ident = V4L2_IDENT_AMBIGUOUS; + chip->revision = 0; + } + return 0; +} + int v4l2_chip_match_host(u32 match_type, u32 match_chip) { switch (match_type) { @@ -1015,6 +1033,7 @@ EXPORT_SYMBOL(v4l2_ctrl_query_fill); EXPORT_SYMBOL(v4l2_ctrl_query_fill_std); EXPORT_SYMBOL(v4l2_chip_match_i2c_client); +EXPORT_SYMBOL(v4l2_chip_ident_i2c_client); EXPORT_SYMBOL(v4l2_chip_match_host); /* diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c index 5c9f2116d7bf..fdfef0b53315 100644 --- a/drivers/media/video/videodev.c +++ b/drivers/media/video/videodev.c @@ -1532,6 +1532,16 @@ static int __video_do_ioctl(struct inode *inode, struct file *file, break; } #endif + case VIDIOC_G_CHIP_IDENT: + { + struct v4l2_chip_ident *p=arg; + if (!vfd->vidioc_g_chip_ident) + break; + ret=vfd->vidioc_g_chip_ident(file, fh, p); + if (!ret) + dbgarg (cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision); + break; + } } /* switch */ if (vfd->debug & V4L2_DEBUG_IOCTL_ARG) { diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index a08ef2c16300..a25c2afa67e1 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -1398,6 +1398,14 @@ struct v4l2_register { __u64 val; }; +/* VIDIOC_G_CHIP_IDENT */ +struct v4l2_chip_ident { + __u32 match_type; /* Match type */ + __u32 match_chip; /* Match this chip, meaning determined by match_type */ + __u32 ident; /* chip identifier as specified in */ + __u32 revision; /* chip revision, chip specific */ +}; + /* * I O C T L C O D E S F O R V I D E O D E V I C E S * @@ -1471,6 +1479,8 @@ struct v4l2_register { /* Experimental, only implemented if CONFIG_VIDEO_ADV_DEBUG is defined */ #define VIDIOC_DBG_S_REGISTER _IOW ('V', 79, struct v4l2_register) #define VIDIOC_DBG_G_REGISTER _IOWR ('V', 80, struct v4l2_register) + +#define VIDIOC_G_CHIP_IDENT _IOWR ('V', 81, struct v4l2_chip_ident) #endif #ifdef __OLD_VIDIOC_ diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h new file mode 100644 index 000000000000..f6686ce133d8 --- /dev/null +++ b/include/media/v4l2-chip-ident.h @@ -0,0 +1,62 @@ +/* + v4l2 chip identifiers header + + This header provides a list of chip identifiers that can be returned + through the VIDIOC_G_CHIP_IDENT ioctl. + + Copyright (C) 2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef V4L2_CHIP_IDENT_H_ +#define V4L2_CHIP_IDENT_H_ + +/* VIDIOC_G_CHIP_IDENT: identifies the actual chip installed on the board */ +enum { + /* general idents: reserved range 0-49 */ + V4L2_IDENT_NONE = 0, /* No chip matched */ + V4L2_IDENT_AMBIGUOUS = 1, /* Match too general, multiple chips matched */ + V4L2_IDENT_UNKNOWN = 2, /* Chip found, but cannot identify */ + + /* module saa7110: just ident= 100 */ + V4L2_IDENT_SAA7110 = 100, + + /* module saa7111: just ident= 101 */ + V4L2_IDENT_SAA7111 = 101, + + /* module saa7115: reserved range 102-149 */ + V4L2_IDENT_SAA7113 = 103, + V4L2_IDENT_SAA7114 = 104, + V4L2_IDENT_SAA7115 = 105, + V4L2_IDENT_SAA7118 = 108, + + /* module saa7127: reserved range 150-199 */ + V4L2_IDENT_SAA7127 = 157, + V4L2_IDENT_SAA7129 = 159, + + /* module cx25840: reserved range 200-249 */ + V4L2_IDENT_CX25836 = 236, + V4L2_IDENT_CX25837 = 237, + V4L2_IDENT_CX25840 = 240, + V4L2_IDENT_CX25841 = 241, + V4L2_IDENT_CX25842 = 242, + V4L2_IDENT_CX25843 = 243, + + /* OmniVision sensors - range 250-299 */ + V4L2_IDENT_OV7670 = 250, +}; + +#endif diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h index abb9ce9b21b8..181a40c46a52 100644 --- a/include/media/v4l2-common.h +++ b/include/media/v4l2-common.h @@ -98,6 +98,8 @@ u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id); struct i2c_client; /* forward reference */ int v4l2_chip_match_i2c_client(struct i2c_client *c, u32 id_type, u32 chip_id); +int v4l2_chip_ident_i2c_client(struct i2c_client *c, struct v4l2_chip_ident *chip, + u32 ident, u32 revision); int v4l2_chip_match_host(u32 id_type, u32 chip_id); /* ------------------------------------------------------------------------- */ @@ -114,39 +116,6 @@ struct v4l2_decode_vbi_line { u32 type; /* VBI service type (V4L2_SLICED_*). 0 if no service found */ }; -/* VIDIOC_INT_G_CHIP_IDENT: identifies the actual chip installed on the board */ -enum v4l2_chip_ident { - /* general idents: reserved range 0-49 */ - V4L2_IDENT_UNKNOWN = 0, - - /* module saa7110: just ident= 100 */ - V4L2_IDENT_SAA7110 = 100, - - /* module saa7111: just ident= 101 */ - V4L2_IDENT_SAA7111 = 101, - - /* module saa7115: reserved range 102-149 */ - V4L2_IDENT_SAA7113 = 103, - V4L2_IDENT_SAA7114 = 104, - V4L2_IDENT_SAA7115 = 105, - V4L2_IDENT_SAA7118 = 108, - - /* module saa7127: reserved range 150-199 */ - V4L2_IDENT_SAA7127 = 157, - V4L2_IDENT_SAA7129 = 159, - - /* module cx25840: reserved range 200-249 */ - V4L2_IDENT_CX25836 = 236, - V4L2_IDENT_CX25837 = 237, - V4L2_IDENT_CX25840 = 240, - V4L2_IDENT_CX25841 = 241, - V4L2_IDENT_CX25842 = 242, - V4L2_IDENT_CX25843 = 243, - - /* OmniVision sensors - range 250-299 */ - V4L2_IDENT_OV7670 = 250, -}; - /* audio ioctls */ /* v4l device was opened in Radio mode, to be replaced by VIDIOC_INT_S_TUNER_MODE */ @@ -208,10 +177,6 @@ enum v4l2_chip_ident { whether CC data from the first or second field should be obtained). */ #define VIDIOC_INT_G_VBI_DATA _IOWR('d', 106, struct v4l2_sliced_vbi_data) -/* Returns the chip identifier or V4L2_IDENT_UNKNOWN if no identification can - be made. */ -#define VIDIOC_INT_G_CHIP_IDENT _IOR ('d', 107, enum v4l2_chip_ident) - /* Sets I2S speed in bps. This is used to provide a standard way to select I2S clock used by driving digital audio streams at some board designs. Usual values for the frequency are 1024000 and 2048000. diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 2693f3ae6ffb..d62847f846c2 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -317,6 +317,8 @@ struct video_device int (*vidioc_s_register) (struct file *file, void *fh, struct v4l2_register *reg); #endif + int (*vidioc_g_chip_ident) (struct file *file, void *fh, + struct v4l2_chip_ident *chip); #ifdef OBSOLETE_OWNER /* to be removed soon */ -- cgit v1.2.3-59-g8ed1b From de956c1e0f89413a3837b642d592e2dff3e3eb78 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Fri, 27 Apr 2007 12:31:12 -0300 Subject: V4L/DVB (5313): Added a config entry and a gpio function pointer to tuner struct These entries mainly are to support configurations of the tda827x silicon tuner with a preamplifier. The values can be set throgh the attach inform or through the extended TUNER_SET_TYPE_ADDR client call. The function pointer will only be updated if the parameter is not NULL. Since a typecast is necessary to set the pointer, i added a typedef for this pointer (tuner_gpio_func_t) in tuner.h Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-cards.c | 3 ++- drivers/media/video/saa7134/saa7134-i2c.c | 2 ++ drivers/media/video/saa7134/saa7134.h | 1 + drivers/media/video/tuner-core.c | 24 ++++++++++++++++-------- include/media/tuner.h | 7 +++++++ 5 files changed, 28 insertions(+), 9 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 89f32107f46b..4399d1371cc1 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -2543,11 +2543,12 @@ struct saa7134_board saa7134_boards[] = { .name = "Philips Tiger reference design", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, + .tuner_config = 0, .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, .mpeg = SAA7134_MPEG_DVB, - .gpiomask = 1 << 21, + .gpiomask = 0x0200000, .inputs = {{ .name = name_tv, .vmux = 1, diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index cce8da6a4f94..62c107e7759d 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -370,6 +370,8 @@ static int attach_inform(struct i2c_client *client) tun_setup.type = tuner; tun_setup.addr = saa7134_boards[dev->board].tuner_addr; + tun_setup.config = saa7134_boards[dev->board].tuner_config; + tun_setup.gpio_func = (tuner_gpio_func_t) saa7134_set_gpio; if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) { diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 7b5ae194bb11..303c0806df91 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -280,6 +280,7 @@ struct saa7134_board { unsigned char radio_addr; unsigned int tda9887_conf; + unsigned int tuner_config; /* peripheral I/O */ enum saa7134_video_out video_out; diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 15dbc6bf42a7..522ec1c35b8c 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -144,7 +144,8 @@ static void set_freq(struct i2c_client *c, unsigned long freq) } static void set_type(struct i2c_client *c, unsigned int type, - unsigned int new_mode_mask) + unsigned int new_mode_mask, unsigned int new_config, + tuner_gpio_func_t gpio_func) { struct tuner *t = i2c_get_clientdata(c); unsigned char buffer[4]; @@ -173,6 +174,11 @@ static void set_type(struct i2c_client *c, unsigned int type, microtune_init(c); break; case TUNER_PHILIPS_TDA8290: + t->config = new_config; + if (gpio_func != NULL) { + tuner_dbg("Defining GPIO function\n"); + t->gpio_func = gpio_func; + } tda8290_init(c); break; case TUNER_TEA5767: @@ -234,10 +240,11 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) tuner_dbg("set addr for type %i\n", t->type); - if ( t->type == UNSET && ((tun_setup->addr == ADDR_UNSET && - (t->mode_mask & tun_setup->mode_mask)) || - tun_setup->addr == c->addr)) { - set_type(c, tun_setup->type, tun_setup->mode_mask); + if ( (t->type == UNSET && ((tun_setup->addr == ADDR_UNSET) && + (t->mode_mask & tun_setup->mode_mask))) || + (tun_setup->addr == c->addr)) { + set_type(c, tun_setup->type, tun_setup->mode_mask, + tun_setup->config, tun_setup->gpio_func); } } @@ -496,7 +503,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) register_client: tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); i2c_attach_client (&t->i2c); - set_type (&t->i2c,t->type, t->mode_mask); + set_type (&t->i2c,t->type, t->mode_mask, t->config, t->gpio_func); return 0; } @@ -576,10 +583,11 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) switch (cmd) { /* --- configuration --- */ case TUNER_SET_TYPE_ADDR: - tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x\n", + tuner_dbg ("Calling set_type_addr for type=%d, addr=0x%02x, mode=0x%02x, config=0x%02x\n", ((struct tuner_setup *)arg)->type, ((struct tuner_setup *)arg)->addr, - ((struct tuner_setup *)arg)->mode_mask); + ((struct tuner_setup *)arg)->mode_mask, + ((struct tuner_setup *)arg)->config); set_addr(client, (struct tuner_setup *)arg); break; diff --git a/include/media/tuner.h b/include/media/tuner.h index 99acf847365c..fe567129b22b 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -173,10 +173,15 @@ enum tuner_mode { when the tuner is set to TV mode. */ +/* allows to access the GPIOs of the host (pci bridge) */ +typedef void (*tuner_gpio_func_t) (void *dev, int bit_no,int value); + struct tuner_setup { unsigned short addr; /* I2C address */ unsigned int type; /* Tuner type */ unsigned int mode_mask; /* Allowed tuner modes */ + unsigned int config; /* configuraion for more complex tuners */ + tuner_gpio_func_t gpio_func; }; struct tuner { @@ -210,6 +215,8 @@ struct tuner { unsigned char tda827x_addr; unsigned char tda827x_ver; unsigned int sgIF; + unsigned int config; + tuner_gpio_func_t gpio_func; /* function ptrs */ void (*set_tv_freq)(struct i2c_client *c, unsigned int freq); -- cgit v1.2.3-59-g8ed1b From cfeb88398f004a0e85ee011fd89a01f5d3bf3c81 Mon Sep 17 00:00:00 2001 From: Hartmut Hackmann Date: Fri, 27 Apr 2007 12:31:17 -0300 Subject: V4L/DVB (5323): Updated support for tuner callbacks This change supplies a more generic version of the tuner callback. The tuner struct now has a function pointer int (*tuner_callback) (void *dev, int command, int arg) additionally to a int config parameter. both can be set through the TUNER_SET_TYPE_ADDR client call. Note that the meaning of the parameters depend on the tuner type. Signed-off-by: Hartmut Hackmann Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/saa7134/saa7134-cards.c | 15 +++------------ drivers/media/video/saa7134/saa7134-core.c | 29 +++++++++++++++++++++++------ drivers/media/video/saa7134/saa7134-i2c.c | 2 +- drivers/media/video/saa7134/saa7134.h | 1 + drivers/media/video/tda8290.c | 12 ++++++------ drivers/media/video/tuner-core.c | 16 ++++++++-------- include/media/tuner.h | 8 +++----- 7 files changed, 45 insertions(+), 38 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index f44e7c7e18a5..87a8a68efc5c 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -4185,6 +4185,9 @@ int saa7134_board_init2(struct saa7134_dev *dev) { unsigned char buf; int board; + struct tuner_setup tun_setup; + tun_setup.config = 0; + tun_setup.tuner_callback = saa7134_tuner_callback; switch (dev->board) { case SAA7134_BOARD_BMK_MPEX_NOTUNER: @@ -4201,20 +4204,15 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->tuner_type = saa7134_boards[dev->board].tuner_type; if (TUNER_ABSENT != dev->tuner_type) { - struct tuner_setup tun_setup; - tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; - tun_setup.config = 0; - tun_setup.gpio_func = NULL; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR, &tun_setup); } break; case SAA7134_BOARD_MD7134: { - struct tuner_setup tun_setup; u8 subaddr; u8 data[3]; int ret, tuner_t; @@ -4275,8 +4273,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) tun_setup.mode_mask = T_RADIO | T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = ADDR_UNSET; - tun_setup.config = 0; - tun_setup.gpio_func = NULL; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); } @@ -4288,7 +4284,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) * the channel decoder. We have to make it transparent to find it */ { - struct tuner_setup tun_setup; u8 data[] = { 0x07, 0x02}; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; i2c_transfer(&dev->i2c_adap, &msg, 1); @@ -4296,8 +4291,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = dev->tuner_addr; - tun_setup.config = 0; - tun_setup.gpio_func = NULL; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); } @@ -4306,7 +4299,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) case SAA7134_BOARD_PHILIPS_TIGER_S: { u8 data[] = { 0x3c, 0x33, 0x60}; - struct tuner_setup tun_setup; struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)}; if(dev->autodetected && (dev->eedata[0x49] == 0x50)) { dev->board = SAA7134_BOARD_PHILIPS_TIGER_S; @@ -4318,7 +4310,6 @@ int saa7134_board_init2(struct saa7134_dev *dev) tun_setup.type = TUNER_PHILIPS_TDA8290; tun_setup.addr = 0x4b; tun_setup.config = 2; - tun_setup.gpio_func = (tuner_gpio_func_t) saa7134_set_gpio; saa7134_i2c_call_clients (dev, TUNER_SET_TYPE_ADDR,&tun_setup); data[2] = 0x68; diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index 8ed03d65a34e..5f74ea467d4e 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -120,7 +120,6 @@ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg) void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) { u32 index, bitval; - u8 sync_control; index = 1 << bit_no; switch (value) { @@ -140,22 +139,40 @@ void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value) dprintk("setting GPIO%d to tristate\n", bit_no); saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, index, 0); break; - case 4: /* sync output on GPIO 22 for tda8275a, 50Hz*/ - case 5: /* sync output on GPIO 22 for tda8275a, 60Hz*/ - if (bit_no == 22) { - dprintk("setting GPIO22 to vsync %d\n", value - 4); + } +} + +int saa7134_tuner_callback(void *ptr, int command, int arg) +{ + u8 sync_control; + struct saa7134_dev *dev = ptr; + + switch (dev->tuner_type) { + case TUNER_PHILIPS_TDA8290: + switch (command) { + case 0: /* switch LNA gain through GPIO 22*/ + saa7134_set_gpio(dev, 22, arg) ; + break; + case 1: /* vsync output at GPIO22. 50 / 60Hz */ + dprintk("setting GPIO22 to vsync %d\n", arg); saa_andorb(SAA7134_VIDEO_PORT_CTRL3, 0x80, 0x80); saa_andorb(SAA7134_VIDEO_PORT_CTRL6, 0x0f, 0x03); - if (value == 5) + if (arg == 1) sync_control = 11; else sync_control = 17; saa_writeb(SAA7134_VGATE_START, sync_control); saa_writeb(SAA7134_VGATE_STOP, sync_control + 1); saa_andorb(SAA7134_MISC_VGATE_MSB, 0x03, 0x00); + break; + default: + return -EINVAL; } break; + default: + return -ENODEV; } + return 0; } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c index 62c107e7759d..4e8d6c94ea60 100644 --- a/drivers/media/video/saa7134/saa7134-i2c.c +++ b/drivers/media/video/saa7134/saa7134-i2c.c @@ -371,7 +371,7 @@ static int attach_inform(struct i2c_client *client) tun_setup.type = tuner; tun_setup.addr = saa7134_boards[dev->board].tuner_addr; tun_setup.config = saa7134_boards[dev->board].tuner_config; - tun_setup.gpio_func = (tuner_gpio_func_t) saa7134_set_gpio; + tun_setup.tuner_callback = saa7134_tuner_callback; if ((tun_setup.addr == ADDR_UNSET)||(tun_setup.addr == client->addr)) { diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index b46265a97565..c365ec581a11 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -565,6 +565,7 @@ extern int saa7134_no_overlay; void saa7134_track_gpio(struct saa7134_dev *dev, char *msg); void saa7134_set_gpio(struct saa7134_dev *dev, int bit_no, int value); +int saa7134_tuner_callback(void *ptr, int command, int arg); #define SAA7134_PGTABLE_SIZE 4096 diff --git a/drivers/media/video/tda8290.c b/drivers/media/video/tda8290.c index e6c3e6167191..44348f95dd4c 100644 --- a/drivers/media/video/tda8290.c +++ b/drivers/media/video/tda8290.c @@ -211,19 +211,19 @@ static void tda827xa_lna_gain(struct i2c_client *c, int high) case 2: /* turn Vsync on */ if (t->std & V4L2_STD_MN) - arg = 5; + arg = 1; else - arg = 4; - if (t->gpio_func) - t->gpio_func(c->adapter->algo_data, 22, 5); + arg = 0; + if (t->tuner_callback) + t->tuner_callback(c->adapter->algo_data, 1, arg); buf[1] = high ? 0 : 1; if (t->config == 2) buf[1] = high ? 1 : 0; i2c_transfer(c->adapter, &msg, 1); break; case 3: /* switch with GPIO of saa713x */ - if (t->gpio_func) - t->gpio_func(c->adapter->algo_data, 22, high); + if (t->tuner_callback) + t->tuner_callback(c->adapter->algo_data, 0, high); break; } } diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index 522ec1c35b8c..b8c38a028841 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -145,7 +145,7 @@ static void set_freq(struct i2c_client *c, unsigned long freq) static void set_type(struct i2c_client *c, unsigned int type, unsigned int new_mode_mask, unsigned int new_config, - tuner_gpio_func_t gpio_func) + int (*tuner_callback) (void *dev, int command,int arg)) { struct tuner *t = i2c_get_clientdata(c); unsigned char buffer[4]; @@ -169,16 +169,16 @@ static void set_type(struct i2c_client *c, unsigned int type, } t->type = type; + t->config = new_config; + if (tuner_callback != NULL) { + tuner_dbg("defining GPIO callback\n"); + t->tuner_callback = tuner_callback; + } switch (t->type) { case TUNER_MT2032: microtune_init(c); break; case TUNER_PHILIPS_TDA8290: - t->config = new_config; - if (gpio_func != NULL) { - tuner_dbg("Defining GPIO function\n"); - t->gpio_func = gpio_func; - } tda8290_init(c); break; case TUNER_TEA5767: @@ -244,7 +244,7 @@ static void set_addr(struct i2c_client *c, struct tuner_setup *tun_setup) (t->mode_mask & tun_setup->mode_mask))) || (tun_setup->addr == c->addr)) { set_type(c, tun_setup->type, tun_setup->mode_mask, - tun_setup->config, tun_setup->gpio_func); + tun_setup->config, tun_setup->tuner_callback); } } @@ -503,7 +503,7 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind) register_client: tuner_info("chip found @ 0x%x (%s)\n", addr << 1, adap->name); i2c_attach_client (&t->i2c); - set_type (&t->i2c,t->type, t->mode_mask, t->config, t->gpio_func); + set_type (&t->i2c,t->type, t->mode_mask, t->config, t->tuner_callback); return 0; } diff --git a/include/media/tuner.h b/include/media/tuner.h index fe567129b22b..a41ac41113ac 100644 --- a/include/media/tuner.h +++ b/include/media/tuner.h @@ -173,15 +173,12 @@ enum tuner_mode { when the tuner is set to TV mode. */ -/* allows to access the GPIOs of the host (pci bridge) */ -typedef void (*tuner_gpio_func_t) (void *dev, int bit_no,int value); - struct tuner_setup { unsigned short addr; /* I2C address */ unsigned int type; /* Tuner type */ unsigned int mode_mask; /* Allowed tuner modes */ unsigned int config; /* configuraion for more complex tuners */ - tuner_gpio_func_t gpio_func; + int (*tuner_callback) (void *dev, int command,int arg); }; struct tuner { @@ -215,8 +212,9 @@ struct tuner { unsigned char tda827x_addr; unsigned char tda827x_ver; unsigned int sgIF; + unsigned int config; - tuner_gpio_func_t gpio_func; + int (*tuner_callback) (void *dev, int command,int arg); /* function ptrs */ void (*set_tv_freq)(struct i2c_client *c, unsigned int freq); -- cgit v1.2.3-59-g8ed1b From 0b20060f6c2cc69c5394cf9782513e7b526e87b9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:22 -0300 Subject: V4L/DVB (5336): Cx23416 doc updates + rename CX2341X_ENC_UNKNOWN The documentation of Several miscellaneous commands was updated. As a result of which the CX2341X_ENC_UNKNOWN command was renamed to CX2341X_ENC_SET_VERT_CROP_LINE. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- Documentation/video4linux/cx2341x/fw-encoder-api.txt | 19 ++++++++++--------- include/media/cx2341x.h | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) (limited to 'include/media') diff --git a/Documentation/video4linux/cx2341x/fw-encoder-api.txt b/Documentation/video4linux/cx2341x/fw-encoder-api.txt index 242104ce5b61..5dd3109a8b3f 100644 --- a/Documentation/video4linux/cx2341x/fw-encoder-api.txt +++ b/Documentation/video4linux/cx2341x/fw-encoder-api.txt @@ -663,12 +663,13 @@ Param[0] ------------------------------------------------------------------------------- -Name CX2341X_ENC_UNKNOWN +Name CX2341X_ENC_SET_VERT_CROP_LINE Enum 219/0xDB Description - Unknown API, it's used by Hauppauge though. + Something to do with 'Vertical Crop Line' Param[0] - 0 This is the value Hauppauge uses, Unknown what it means. + If saa7114 and raw VBI capture and 60 Hz, then set to 10001. + Else 0. ------------------------------------------------------------------------------- @@ -682,11 +683,9 @@ Param[0] Command number: 1=set initial SCR value when starting encoding (works). 2=set quality mode (apparently some test setting). - 3=setup advanced VIM protection handling (supposedly only for the cx23416 - for raw YUV). - Actually it looks like this should be 0 for saa7114/5 based card and 1 - for cx25840 based cards. - 4=generate artificial PTS timestamps + 3=setup advanced VIM protection handling. + Always 1 for the cx23416 and 0 for cx23415. + 4=generate DVD compatible PTS timestamps 5=USB flush mode 6=something to do with the quantization matrix 7=set navigation pack insertion for DVD: adds 0xbf (private stream 2) @@ -698,7 +697,9 @@ Param[0] 9=set history parameters of the video input module 10=set input field order of VIM 11=set quantization matrix - 12=reset audio interface + 12=reset audio interface after channel change or input switch (has no argument). + Needed for the cx2584x, not needed for the mspx4xx, but it doesn't seem to + do any harm calling it regardless. 13=set audio volume delay 14=set audio delay diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h index 583b0621ff43..38c12fed7535 100644 --- a/include/media/cx2341x.h +++ b/include/media/cx2341x.h @@ -166,7 +166,7 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix); #define CX2341X_ENC_SET_PLACEHOLDER 0xd7 #define CX2341X_ENC_MUTE_VIDEO 0xd9 #define CX2341X_ENC_MUTE_AUDIO 0xda -#define CX2341X_ENC_UNKNOWN 0xdb +#define CX2341X_ENC_SET_VERT_CROP_LINE 0xdb #define CX2341X_ENC_MISC 0xdc /* OSD API, specific to the cx23415 */ -- cgit v1.2.3-59-g8ed1b From 1b5888cea1d371239a130150222e63d476298d89 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:23 -0300 Subject: V4L/DVB (5341): Add cx23415/6 chip idents. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- include/media/v4l2-chip-ident.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include/media') diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index f6686ce133d8..7d0c65413981 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -57,6 +57,10 @@ enum { /* OmniVision sensors - range 250-299 */ V4L2_IDENT_OV7670 = 250, + + /* Conexant MPEG encoder/decoders: range 410-420 */ + V4L2_IDENT_CX23415 = 415, + V4L2_IDENT_CX23416 = 416, }; #endif -- cgit v1.2.3-59-g8ed1b From 1a0adaf37c30e89e44d1470ef604a930999a5826 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:25 -0300 Subject: V4L/DVB (5345): ivtv driver for Conexant cx23416/cx23415 MPEG encoder/decoder It took three core maintainers, over four years of work, eight new i2c modules, eleven new V4L2 ioctls, three new DVB video ioctls, a Sliced VBI API, a new MPEG encoder API, an enhanced DVB video MPEG decoding API, major YUV/OSD contributions from Ian and John, web/wiki/svn/trac support from Axel Thimm, (hardware) support from Hauppauge, support and assistance from the v4l-dvb people and the many, many users of ivtv to finally make it possible to merge this driver into the kernel. Thank you all! Signed-off-by: Kevin Thayer Signed-off-by: Chris Kennedy Signed-off-by: Hans Verkuil Signed-off-by: John P Harvey Signed-off-by: Ian Armstrong Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 2 + drivers/media/video/Makefile | 1 + drivers/media/video/ivtv/Kconfig | 26 + drivers/media/video/ivtv/Makefile | 7 + drivers/media/video/ivtv/ivtv-audio.c | 74 ++ drivers/media/video/ivtv/ivtv-audio.h | 23 + drivers/media/video/ivtv/ivtv-cards.c | 964 ++++++++++++++++++ drivers/media/video/ivtv/ivtv-cards.h | 207 ++++ drivers/media/video/ivtv/ivtv-controls.c | 303 ++++++ drivers/media/video/ivtv/ivtv-controls.h | 21 + drivers/media/video/ivtv/ivtv-driver.c | 1385 ++++++++++++++++++++++++++ drivers/media/video/ivtv/ivtv-driver.h | 866 +++++++++++++++++ drivers/media/video/ivtv/ivtv-fileops.c | 918 ++++++++++++++++++ drivers/media/video/ivtv/ivtv-fileops.h | 45 + drivers/media/video/ivtv/ivtv-firmware.c | 272 ++++++ drivers/media/video/ivtv/ivtv-firmware.h | 25 + drivers/media/video/ivtv/ivtv-gpio.c | 307 ++++++ drivers/media/video/ivtv/ivtv-gpio.h | 25 + drivers/media/video/ivtv/ivtv-i2c.c | 750 ++++++++++++++ drivers/media/video/ivtv/ivtv-i2c.h | 38 + drivers/media/video/ivtv/ivtv-ioctl.c | 1555 ++++++++++++++++++++++++++++++ drivers/media/video/ivtv/ivtv-ioctl.h | 28 + drivers/media/video/ivtv/ivtv-irq.c | 818 ++++++++++++++++ drivers/media/video/ivtv/ivtv-irq.h | 24 + drivers/media/video/ivtv/ivtv-mailbox.c | 360 +++++++ drivers/media/video/ivtv/ivtv-mailbox.h | 25 + drivers/media/video/ivtv/ivtv-queue.c | 262 +++++ drivers/media/video/ivtv/ivtv-queue.h | 64 ++ drivers/media/video/ivtv/ivtv-streams.c | 977 +++++++++++++++++++ drivers/media/video/ivtv/ivtv-streams.h | 31 + drivers/media/video/ivtv/ivtv-udma.c | 200 ++++ drivers/media/video/ivtv/ivtv-udma.h | 43 + drivers/media/video/ivtv/ivtv-vbi.c | 545 +++++++++++ drivers/media/video/ivtv/ivtv-vbi.h | 27 + drivers/media/video/ivtv/ivtv-version.h | 26 + drivers/media/video/ivtv/ivtv-video.c | 150 +++ drivers/media/video/ivtv/ivtv-video.h | 25 + drivers/media/video/ivtv/ivtv-yuv.c | 1129 ++++++++++++++++++++++ drivers/media/video/ivtv/ivtv-yuv.h | 24 + include/media/ivtv.h | 65 ++ 40 files changed, 12637 insertions(+) create mode 100644 drivers/media/video/ivtv/Kconfig create mode 100644 drivers/media/video/ivtv/Makefile create mode 100644 drivers/media/video/ivtv/ivtv-audio.c create mode 100644 drivers/media/video/ivtv/ivtv-audio.h create mode 100644 drivers/media/video/ivtv/ivtv-cards.c create mode 100644 drivers/media/video/ivtv/ivtv-cards.h create mode 100644 drivers/media/video/ivtv/ivtv-controls.c create mode 100644 drivers/media/video/ivtv/ivtv-controls.h create mode 100644 drivers/media/video/ivtv/ivtv-driver.c create mode 100644 drivers/media/video/ivtv/ivtv-driver.h create mode 100644 drivers/media/video/ivtv/ivtv-fileops.c create mode 100644 drivers/media/video/ivtv/ivtv-fileops.h create mode 100644 drivers/media/video/ivtv/ivtv-firmware.c create mode 100644 drivers/media/video/ivtv/ivtv-firmware.h create mode 100644 drivers/media/video/ivtv/ivtv-gpio.c create mode 100644 drivers/media/video/ivtv/ivtv-gpio.h create mode 100644 drivers/media/video/ivtv/ivtv-i2c.c create mode 100644 drivers/media/video/ivtv/ivtv-i2c.h create mode 100644 drivers/media/video/ivtv/ivtv-ioctl.c create mode 100644 drivers/media/video/ivtv/ivtv-ioctl.h create mode 100644 drivers/media/video/ivtv/ivtv-irq.c create mode 100644 drivers/media/video/ivtv/ivtv-irq.h create mode 100644 drivers/media/video/ivtv/ivtv-mailbox.c create mode 100644 drivers/media/video/ivtv/ivtv-mailbox.h create mode 100644 drivers/media/video/ivtv/ivtv-queue.c create mode 100644 drivers/media/video/ivtv/ivtv-queue.h create mode 100644 drivers/media/video/ivtv/ivtv-streams.c create mode 100644 drivers/media/video/ivtv/ivtv-streams.h create mode 100644 drivers/media/video/ivtv/ivtv-udma.c create mode 100644 drivers/media/video/ivtv/ivtv-udma.h create mode 100644 drivers/media/video/ivtv/ivtv-vbi.c create mode 100644 drivers/media/video/ivtv/ivtv-vbi.h create mode 100644 drivers/media/video/ivtv/ivtv-version.h create mode 100644 drivers/media/video/ivtv/ivtv-video.c create mode 100644 drivers/media/video/ivtv/ivtv-video.h create mode 100644 drivers/media/video/ivtv/ivtv-yuv.c create mode 100644 drivers/media/video/ivtv/ivtv-yuv.h create mode 100644 include/media/ivtv.h (limited to 'include/media') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index fa0a87679190..639e8b6c35b1 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -647,6 +647,8 @@ config VIDEO_HEXIUM_GEMINI source "drivers/media/video/cx88/Kconfig" +source "drivers/media/video/ivtv/Kconfig" + config VIDEO_M32R_AR tristate "AR devices" depends on M32R && VIDEO_V4L1 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 384f01c133c5..9c2de501612f 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -61,6 +61,7 @@ obj-$(CONFIG_VIDEO_CPIA_USB) += cpia_usb.o obj-$(CONFIG_VIDEO_MEYE) += meye.o obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/ obj-$(CONFIG_VIDEO_CX88) += cx88/ +obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig new file mode 100644 index 000000000000..88e510171347 --- /dev/null +++ b/drivers/media/video/ivtv/Kconfig @@ -0,0 +1,26 @@ +config VIDEO_IVTV + tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" + depends on VIDEO_V4L2 && USB && I2C && EXPERIMENTAL + select FW_LOADER + select VIDEO_TUNER + select VIDEO_TVEEPROM + select VIDEO_CX2341X + select VIDEO_MSP3400 + select VIDEO_SAA711X + select VIDEO_SAA7127 + select VIDEO_TVAUDIO + select VIDEO_CS53L32A + select VIDEO_TLV320AIC23B + select VIDEO_WM8775 + select VIDEO_WM8739 + select VIDEO_UPD64031A + select VIDEO_UPD64083 + ---help--- + This is a video4linux driver for Conexant cx23416 or cx23416 based + PCI personal video recorder devices. + + This is used in devices such as the Hauppauge PVR-150/250/350/500 + cards. + + To compile this driver as a module, choose M here: the + module will be called ivtv. diff --git a/drivers/media/video/ivtv/Makefile b/drivers/media/video/ivtv/Makefile new file mode 100644 index 000000000000..7e95148fbf4f --- /dev/null +++ b/drivers/media/video/ivtv/Makefile @@ -0,0 +1,7 @@ +ivtv-objs := ivtv-audio.o ivtv-cards.o ivtv-controls.o \ + ivtv-driver.o ivtv-fileops.o ivtv-firmware.o \ + ivtv-gpio.o ivtv-i2c.o ivtv-ioctl.o ivtv-irq.o \ + ivtv-mailbox.o ivtv-queue.o ivtv-streams.o ivtv-udma.o \ + ivtv-vbi.o ivtv-video.o ivtv-yuv.o + +obj-$(CONFIG_VIDEO_IVTV) += ivtv.o diff --git a/drivers/media/video/ivtv/ivtv-audio.c b/drivers/media/video/ivtv/ivtv-audio.c new file mode 100644 index 000000000000..d702b8b539a1 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-audio.c @@ -0,0 +1,74 @@ +/* + Audio-related ivtv functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" +#include "ivtv-i2c.h" +#include "ivtv-gpio.h" +#include "ivtv-cards.h" +#include "ivtv-audio.h" +#include +#include + +/* Selects the audio input and output according to the current + settings. */ +int ivtv_audio_set_io(struct ivtv *itv) +{ + struct v4l2_routing route; + u32 audio_input; + int mux_input; + + /* Determine which input to use */ + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + audio_input = itv->card->radio_input.audio_input; + mux_input = itv->card->radio_input.muxer_input; + } else { + audio_input = itv->card->audio_inputs[itv->audio_input].audio_input; + mux_input = itv->card->audio_inputs[itv->audio_input].muxer_input; + } + + /* handle muxer chips */ + route.input = mux_input; + route.output = 0; + ivtv_i2c_hw(itv, itv->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route); + + route.input = audio_input; + if (itv->card->hw_audio & IVTV_HW_MSP34XX) { + route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1); + } + return ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, &route); +} + +void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route) +{ + ivtv_i2c_hw(itv, itv->card->hw_audio, VIDIOC_INT_S_AUDIO_ROUTING, route); +} + +void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq) +{ + static u32 freqs[3] = { 44100, 48000, 32000 }; + + /* The audio clock of the digitizer must match the codec sample + rate otherwise you get some very strange effects. */ + if (freq > 2) + return; + ivtv_call_i2c_clients(itv, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]); +} + diff --git a/drivers/media/video/ivtv/ivtv-audio.h b/drivers/media/video/ivtv/ivtv-audio.h new file mode 100644 index 000000000000..9c42846d8124 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-audio.h @@ -0,0 +1,23 @@ +/* + Audio-related ivtv functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_audio_set_io(struct ivtv *itv); +void ivtv_audio_set_route(struct ivtv *itv, struct v4l2_routing *route); +void ivtv_audio_set_audio_clock_freq(struct ivtv *itv, u8 freq); diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c new file mode 100644 index 000000000000..8eab02083887 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -0,0 +1,964 @@ +/* + Functions to query card hardware + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-i2c.h" + +#include +#include +#include +#include +#include + +#define MSP_TUNER MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ + MSP_DSP_IN_TUNER, MSP_DSP_IN_TUNER) +#define MSP_SCART1 MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_SCART2 MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_SCART3 MSP_INPUT(MSP_IN_SCART3, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) +#define MSP_MONO MSP_INPUT(MSP_IN_MONO, MSP_IN_TUNER1, \ + MSP_DSP_IN_SCART, MSP_DSP_IN_SCART) + +/********************** card configuration *******************************/ + +/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii + This keeps the PCI ID database up to date. Note that the entries + must be added under vendor 0x4444 (Conexant) as subsystem IDs. + New vendor IDs should still be added to the vendor ID list. */ + +/* Hauppauge PVR-250 cards */ + +/* Note: for Hauppauge cards the tveeprom information is used instead of PCI IDs */ +static const struct ivtv_card ivtv_card_pvr250 = { + .type = IVTV_CARD_PVR_250, + .name = "Hauppauge WinTV PVR-250", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* Hauppauge PVR-350 cards */ + +/* Outputs for Hauppauge PVR350 cards */ +static struct ivtv_card_output ivtv_pvr350_outputs[] = { + { + .name = "S-Video + Composite", + .video_output = 0, + }, { + .name = "Composite", + .video_output = 1, + }, { + .name = "S-Video", + .video_output = 2, + }, { + .name = "RGB", + .video_output = 3, + }, { + .name = "YUV C", + .video_output = 4, + }, { + .name = "YUV V", + .video_output = 5, + } +}; + +static const struct ivtv_card ivtv_card_pvr350 = { + .type = IVTV_CARD_PVR_350, + .name = "Hauppauge WinTV PVR-350", + .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, + .video_outputs = ivtv_pvr350_outputs, + .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* PVR-350 V1 boards have a different audio tuner input and use a + saa7114 instead of a saa7115. + Note that the info below comes from a pre-production model so it may + not be correct. Especially the audio behaves strangely (mono only it seems) */ +static const struct ivtv_card ivtv_card_pvr350_v1 = { + .type = IVTV_CARD_PVR_350_V1, + .name = "Hauppauge WinTV PVR-350 (V1)", + .v4l2_capabilities = IVTV_CAP_ENCODER | IVTV_CAP_DECODER, + .video_outputs = ivtv_pvr350_outputs, + .nof_outputs = ARRAY_SIZE(ivtv_pvr350_outputs), + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7114 | + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, IVTV_SAA71XX_COMPOSITE1 }, + { IVTV_CARD_INPUT_COMPOSITE3, 1, IVTV_SAA71XX_COMPOSITE5 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_MONO }, + { IVTV_CARD_INPUT_LINE_IN1, MSP_SCART1 }, + { IVTV_CARD_INPUT_LINE_IN2, MSP_SCART3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, MSP_SCART2 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* Hauppauge PVR-150/PVR-500 cards */ + +static const struct ivtv_card ivtv_card_pvr150 = { + .type = IVTV_CARD_PVR_150, + .name = "Hauppauge WinTV PVR-150", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_WM8775, + .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | + IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE3 }, + { IVTV_CARD_INPUT_SVIDEO2, 2, CX25840_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE2, 2, CX25840_COMPOSITE4 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, + CX25840_AUDIO8, WM8775_AIN2 }, + { IVTV_CARD_INPUT_LINE_IN1, + CX25840_AUDIO_SERIAL, WM8775_AIN2 }, + { IVTV_CARD_INPUT_LINE_IN2, + CX25840_AUDIO_SERIAL, WM8775_AIN3 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, + CX25840_AUDIO_SERIAL, WM8775_AIN4 }, + /* apparently needed for the IR blaster */ + .gpio_init = { .direction = 0x1f01, .initial_value = 0x26f3 }, +}; + +/* ------------------------------------------------------------------------- */ + +/* AVerMedia M179 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_m179[] = { + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3cf }, + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_AVERMEDIA, 0xa3ce }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_m179 = { + .type = IVTV_CARD_M179, + .name = "AVerMedia M179", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xe380, .initial_value = 0x8290 }, + .gpio_audio_input = { .mask = 0x8040, .tuner = 0x8000, .linein = 0x0000 }, + .gpio_audio_mute = { .mask = 0x2000, .mute = 0x2000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0200, .lang2 = 0x0100, .both = 0x0000 }, + .gpio_audio_freq = { .mask = 0x0018, .f32000 = 0x0000, + .f44100 = 0x0008, .f48000 = 0x0010 }, + .gpio_audio_detect = { .mask = 0x4000, .stereo = 0x0000 }, + .tuners = { + /* As far as we know all M179 cards use this tuner */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_NTSC }, + }, + .pci_list = ivtv_pci_m179, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG600/Kuroutoshikou ITVC16-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_mpg600[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xfff3 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0xffff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_mpg600 = { + .type = IVTV_CARD_MPG600, + .name = "Yuan MPG600, Kuroutoshikou ITVC16-STVLP", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x3080, .initial_value = 0x0004 }, + .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, + .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, + .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, + .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, + .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, + .tuners = { + /* The PAL tuner is confirmed */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_mpg600, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG160/Kuroutoshikou ITVC15-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_mpg160[] = { + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_YUAN1, 0 }, + { PCI_DEVICE_ID_IVTV15, IVTV_PCI_ID_IODATA, 0x40a0 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_mpg160 = { + .type = IVTV_CARD_MPG160, + .name = "YUAN MPG160, Kuroutoshikou ITVC15-STVLP, I/O Data GV-M2TV/PCI", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0x7080, .initial_value = 0x400c }, + .gpio_audio_input = { .mask = 0x3000, .tuner = 0x0000, .linein = 0x2000 }, + .gpio_audio_mute = { .mask = 0x0001, .mute = 0x0001 }, + .gpio_audio_mode = { .mask = 0x000e, .mono = 0x0006, .stereo = 0x0004, + .lang1 = 0x0004, .lang2 = 0x0000, .both = 0x0008 }, + .gpio_audio_detect = { .mask = 0x0900, .stereo = 0x0100 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_mpg160, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan PG600/Diamond PVR-550 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_pg600[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_DIAMONDMM, 0x0070 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_pg600 = { + .type = IVTV_CARD_PG600, + .name = "Yuan PG600, Diamond PVR-550", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FQ1216ME }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_pg600, +}; + +/* ------------------------------------------------------------------------- */ + +/* Adaptec VideOh! AVC-2410 card */ + +static const struct ivtv_card_pci_info ivtv_pci_avc2410[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0093 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_avc2410 = { + .type = IVTV_CARD_AVC2410, + .name = "Adaptec VideOh! AVC-2410", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_muxer = IVTV_HW_CS53L32A, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_CS53L32A | + IVTV_HW_SAA7115 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, + MSP_TUNER, CS53L32A_IN0 }, + { IVTV_CARD_INPUT_LINE_IN1, + MSP_SCART1, CS53L32A_IN2 }, + }, + /* This card has no eeprom and in fact the Windows driver relies + on the country/region setting of the user to decide which tuner + is available. */ + .tuners = { + /* This tuner has been verified for the AVC2410 */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + /* This is a good guess, but I'm not totally sure this is + the correct tuner for NTSC. */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_avc2410, +}; + +/* ------------------------------------------------------------------------- */ + +/* Adaptec VideOh! AVC-2010 card */ + +static const struct ivtv_card_pci_info ivtv_pci_avc2010[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_ADAPTEC, 0x0092 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_avc2010 = { + .type = IVTV_CARD_AVC2010, + .name = "Adaptec VideOh! AVC-2010", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_CS53L32A, + .hw_audio_ctrl = IVTV_HW_CS53L32A, + .hw_all = IVTV_HW_CS53L32A | IVTV_HW_SAA7115, + .video_inputs = { + { IVTV_CARD_INPUT_SVIDEO1, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 0, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_LINE_IN1, CS53L32A_IN2 }, + }, + /* Does not have a tuner */ + .pci_list = ivtv_pci_avc2010, +}; + +/* ------------------------------------------------------------------------- */ + +/* Nagase Transgear 5000TV card */ + +static const struct ivtv_card_pci_info ivtv_pci_tg5000tv[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_tg5000tv = { + .type = IVTV_CARD_TG5000TV, + .name = "Nagase Transgear 5000TV", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7114 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | + IVTV_HW_GPIO, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7114 | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gr_config = UPD64031A_VERTICAL_EXTERNAL, + .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, + .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, + .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, + .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, + .composite = 0x0010, .svideo = 0x0020 }, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_tg5000tv, +}; + +/* ------------------------------------------------------------------------- */ + +/* AOpen VA2000MAX-SNT6 card */ + +static const struct ivtv_card_pci_info ivtv_pci_va2000[] = { + { PCI_DEVICE_ID_IVTV16, 0, 0xff5f }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_va2000 = { + .type = IVTV_CARD_VA2000MAX_SNT6, + .name = "AOpen VA2000MAX-SNT6", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_MSP34XX, + .hw_audio_ctrl = IVTV_HW_MSP34XX, + .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | + IVTV_HW_UPD6408X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, MSP_TUNER }, + }, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_va2000, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPG600GR/Kuroutoshikou CX23416GYC-STVLP cards */ + +static const struct ivtv_card_pci_info ivtv_pci_cx23416gyc[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN4, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_MELCO, 0x0523 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_cx23416gyc = { + .type = IVTV_CARD_CX23416GYC, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO3 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gr_config = UPD64031A_VERTICAL_EXTERNAL, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, + .pci_list = ivtv_pci_cx23416gyc, +}; + +static const struct ivtv_card ivtv_card_cx23416gyc_nogr = { + .type = IVTV_CARD_CX23416GYC_NOGR, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER | + IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, +}; + +static const struct ivtv_card ivtv_card_cx23416gyc_nogrycs = { + .type = IVTV_CARD_CX23416GYC_NOGRYCS, + .name = "Yuan MPG600GR, Kuroutoshikou CX23416GYC-STVLP (no GR/YCS)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X | IVTV_HW_GPIO, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA717X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 | + IVTV_SAA717X_TUNER_FLAG }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE0 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN2 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN0 }, + }, + .gpio_init = { .direction = 0xf880, .initial_value = 0x8800 }, + .gpio_video_input = { .mask = 0x0020, .tuner = 0x0000, + .composite = 0x0020, .svideo = 0x0020 }, + .gpio_audio_freq = { .mask = 0xc000, .f32000 = 0x0000, + .f44100 = 0x4000, .f48000 = 0x8000 }, + .tuners = { + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + }, +}; + +/* ------------------------------------------------------------------------- */ + +/* I/O Data GV-MVP/RX & GV-MVP/RX2W (dual tuner) cards */ + +static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd01e }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd038 }, /* 2W unit #1 */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd039 }, /* 2W unit #2 */ + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gv_mvprx = { + .type = IVTV_CARD_GV_MVPRX, + .name = "I/O Data GV-MVP/RX, GV-MVP/RX2W (dual tuner)", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_WM8739, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TVAUDIO | + IVTV_HW_TUNER | IVTV_HW_WM8739 | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO1 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, + .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, + .tuners = { + /* This card has the Panasonic VP27 tuner */ + { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + }, + .pci_list = ivtv_pci_gv_mvprx, +}; + +/* ------------------------------------------------------------------------- */ + +/* I/O Data GV-MVP/RX2E card */ + +static const struct ivtv_card_pci_info ivtv_pci_gv_mvprx2e[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_IODATA, 0xd025 }, + {0, 0, 0} +}; + +static const struct ivtv_card ivtv_card_gv_mvprx2e = { + .type = IVTV_CARD_GV_MVPRX2E, + .name = "I/O Data GV-MVP/RX2E", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_WM8739, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | + IVTV_HW_TVAUDIO | IVTV_HW_WM8739, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xc301, .initial_value = 0x0200 }, + .gpio_audio_input = { .mask = 0xffff, .tuner = 0x0200, .linein = 0x0300 }, + .tuners = { + /* This card has the Panasonic VP27 tuner */ + { .std = V4L2_STD_525_60, .tuner = TUNER_PANASONIC_VP27 }, + }, + .pci_list = ivtv_pci_gv_mvprx2e, +}; + +/* ------------------------------------------------------------------------- */ + +/* GotVIEW PCI DVD card */ + +static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN1, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gotview_pci_dvd = { + .type = IVTV_CARD_GOTVIEW_PCI_DVD, + .name = "GotView PCI DVD", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA717X, + .hw_audio = IVTV_HW_SAA717X, + .hw_audio_ctrl = IVTV_HW_SAA717X, + .hw_all = IVTV_HW_SAA717X | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE1 }, /* pin 116 */ + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, /* pin 114/109 */ + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_COMPOSITE3 }, /* pin 118 */ + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_SAA717X_IN0 }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_SAA717X_IN2 }, + }, + .gpio_init = { .direction = 0xf000, .initial_value = 0xA000 }, + .tuners = { + /* This card has a Philips FQ1216ME MK3 tuner */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .pci_list = ivtv_pci_gotview_pci_dvd, +}; + +/* ------------------------------------------------------------------------- */ + +/* GotVIEW PCI DVD2 Deluxe card */ + +static const struct ivtv_card_pci_info ivtv_pci_gotview_pci_dvd2[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW1, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_gotview_pci_dvd2 = { + .type = IVTV_CARD_GOTVIEW_PCI_DVD2, + .name = "GotView PCI DVD2 Deluxe", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_muxer = IVTV_HW_GPIO, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5, 0 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO_SERIAL, 2 }, + .gpio_init = { .direction = 0x0800, .initial_value = 0 }, + .gpio_audio_input = { .mask = 0x0800, .tuner = 0, .linein = 0, .radio = 0x0800 }, + .tuners = { + /* This card has a Philips FQ1216ME MK5 tuner */ + { .std = V4L2_STD_625_50, .tuner = TUNER_PHILIPS_FM1216ME_MK3 }, + }, + .pci_list = ivtv_pci_gotview_pci_dvd2, +}; + +/* ------------------------------------------------------------------------- */ + +/* Yuan MPC622 miniPCI card */ + +static const struct ivtv_card_pci_info ivtv_pci_yuan_mpc622[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN2, 0xd998 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_yuan_mpc622 = { + .type = IVTV_CARD_YUAN_MPC622, + .name = "Yuan MPC622", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, CX25840_COMPOSITE1 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .gpio_init = { .direction = 0x00ff, .initial_value = 0x0002 }, + .tuners = { + /* This card has the TDA8290/TDA8275 tuner chips */ + { .std = V4L2_STD_ALL, .tuner = TUNER_PHILIPS_TDA8290 }, + }, + .pci_list = ivtv_pci_yuan_mpc622, +}; + +/* ------------------------------------------------------------------------- */ + +/* DIGITAL COWBOY DCT-MTVP1 card */ + +static const struct ivtv_card_pci_info ivtv_pci_dctmvtvp1[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xbfff }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_dctmvtvp1 = { + .type = IVTV_CARD_DCTMTVP1, + .name = "Digital Cowboy DCT-MTVP1", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_SAA7115 | IVTV_HW_UPD64031A | IVTV_HW_UPD6408X | + IVTV_HW_GPIO, + .hw_audio = IVTV_HW_GPIO, + .hw_audio_ctrl = IVTV_HW_GPIO, + .hw_all = IVTV_HW_GPIO | IVTV_HW_SAA7115 | IVTV_HW_TUNER | + IVTV_HW_UPD64031A | IVTV_HW_UPD6408X, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_SVIDEO0 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO2 }, + { IVTV_CARD_INPUT_COMPOSITE1, 1, IVTV_SAA71XX_SVIDEO2 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, IVTV_GPIO_TUNER }, + { IVTV_CARD_INPUT_LINE_IN1, IVTV_GPIO_LINE_IN }, + }, + .gpio_init = { .direction = 0xe080, .initial_value = 0x8000 }, + .gpio_audio_input = { .mask = 0x8080, .tuner = 0x8000, .linein = 0x0080 }, + .gpio_audio_mute = { .mask = 0x6000, .mute = 0x6000 }, + .gpio_audio_mode = { .mask = 0x4300, .mono = 0x4000, .stereo = 0x0200, + .lang1 = 0x0300, .lang2 = 0x0000, .both = 0x0200 }, + .gpio_video_input = { .mask = 0x0030, .tuner = 0x0000, + .composite = 0x0010, .svideo = 0x0020}, + .tuners = { + { .std = V4L2_STD_525_60, .tuner = TUNER_PHILIPS_FQ1286 }, + }, + .pci_list = ivtv_pci_dctmvtvp1, +}; + +/* ------------------------------------------------------------------------- */ + +#ifdef HAVE_XC3028 + +/* Yuan PG600-2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 cards */ + +static const struct ivtv_card_pci_info ivtv_pci_pg600v2[] = { + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_YUAN3, 0x0600 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_GOTVIEW2, 0x0600 }, + { 0, 0, 0 } +}; + +static const struct ivtv_card ivtv_card_pg600v2 = { + .type = IVTV_CARD_PG600V2, + .name = "Yuan PG600-2, GotView PCI DVD Lite, Club3D ZAP-TV1x01", + .v4l2_capabilities = IVTV_CAP_ENCODER, + .hw_video = IVTV_HW_CX25840, + .hw_audio = IVTV_HW_CX25840, + .hw_audio_ctrl = IVTV_HW_CX25840, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER, + .video_inputs = { + { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, + { IVTV_CARD_INPUT_SVIDEO1, 1, + CX25840_SVIDEO_LUMA3 | CX25840_SVIDEO_CHROMA4 }, + }, + .audio_inputs = { + { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL }, + }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, + .tuners = { + { .std = V4L2_STD_ALL, .tuner = TUNER_XCEIVE_XC3028 }, + }, + .gpio_init = { .direction = 0x1000, .initial_value = 0x1000 }, /* tuner reset */ + .pci_list = ivtv_pci_pg600v2, +}; +#endif + +static const struct ivtv_card *ivtv_card_list[] = { + &ivtv_card_pvr250, + &ivtv_card_pvr350, + &ivtv_card_pvr150, + &ivtv_card_m179, + &ivtv_card_mpg600, + &ivtv_card_mpg160, + &ivtv_card_pg600, + &ivtv_card_avc2410, + &ivtv_card_avc2010, + &ivtv_card_tg5000tv, + &ivtv_card_va2000, + &ivtv_card_cx23416gyc, + &ivtv_card_gv_mvprx, + &ivtv_card_gv_mvprx2e, + &ivtv_card_gotview_pci_dvd, + &ivtv_card_gotview_pci_dvd2, + &ivtv_card_yuan_mpc622, + &ivtv_card_dctmvtvp1, +#ifdef HAVE_XC3028 + &ivtv_card_pg600v2, +#endif + + /* Variations of standard cards but with the same PCI IDs. + These cards must come last in this list. */ + &ivtv_card_pvr350_v1, + &ivtv_card_cx23416gyc_nogr, + &ivtv_card_cx23416gyc_nogrycs, +}; + +const struct ivtv_card *ivtv_get_card(u16 index) +{ + if (index >= ARRAY_SIZE(ivtv_card_list)) + return NULL; + return ivtv_card_list[index]; +} + +int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input) +{ + const struct ivtv_card_video_input *card_input = itv->card->video_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "S-Video 1", + "S-Video 2", + "Composite 1", + "Composite 2", + "Composite 3" + }; + + memset(input, 0, sizeof(*input)); + if (index >= itv->nof_inputs) + return -EINVAL; + input->index = index; + strcpy(input->name, input_strs[card_input->video_type - 1]); + input->type = (card_input->video_type == IVTV_CARD_INPUT_VID_TUNER ? + V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA); + input->audioset = (1 << itv->nof_audio_inputs) - 1; + input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ? + itv->tuner_std : V4L2_STD_ALL; + return 0; +} + +int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output) +{ + const struct ivtv_card_output *card_output = itv->card->video_outputs + index; + + memset(output, 0, sizeof(*output)); + if (index >= itv->card->nof_outputs) + return -EINVAL; + output->index = index; + strcpy(output->name, card_output->name); + output->type = V4L2_OUTPUT_TYPE_ANALOG; + output->audioset = 1; + output->std = V4L2_STD_ALL; + return 0; +} + +int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *audio) +{ + const struct ivtv_card_audio_input *aud_input = itv->card->audio_inputs + index; + static const char * const input_strs[] = { + "Tuner 1", + "Line In 1", + "Line In 2" + }; + + memset(audio, 0, sizeof(*audio)); + if (index >= itv->nof_audio_inputs) + return -EINVAL; + strcpy(audio->name, input_strs[aud_input->audio_type - 1]); + audio->index = index; + audio->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *aud_output) +{ + memset(aud_output, 0, sizeof(*aud_output)); + if (itv->card->video_outputs == NULL || index != 0) + return -EINVAL; + strcpy(aud_output->name, "A/V Audio Out"); + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h new file mode 100644 index 000000000000..15012f88b802 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -0,0 +1,207 @@ +/* + Functions to query card hardware + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* hardware flags */ +#define IVTV_HW_CX25840 (1 << 0) +#define IVTV_HW_SAA7115 (1 << 1) +#define IVTV_HW_SAA7127 (1 << 2) +#define IVTV_HW_MSP34XX (1 << 3) +#define IVTV_HW_TUNER (1 << 4) +#define IVTV_HW_WM8775 (1 << 5) +#define IVTV_HW_CS53L32A (1 << 6) +#define IVTV_HW_TVEEPROM (1 << 7) +#define IVTV_HW_SAA7114 (1 << 8) +#define IVTV_HW_TVAUDIO (1 << 9) +#define IVTV_HW_UPD64031A (1 << 10) +#define IVTV_HW_UPD6408X (1 << 11) +#define IVTV_HW_SAA717X (1 << 12) +#define IVTV_HW_WM8739 (1 << 13) +#define IVTV_HW_GPIO (1 << 14) + +#define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) + +/* video inputs */ +#define IVTV_CARD_INPUT_VID_TUNER 1 +#define IVTV_CARD_INPUT_SVIDEO1 2 +#define IVTV_CARD_INPUT_SVIDEO2 3 +#define IVTV_CARD_INPUT_COMPOSITE1 4 +#define IVTV_CARD_INPUT_COMPOSITE2 5 +#define IVTV_CARD_INPUT_COMPOSITE3 6 + +/* audio inputs */ +#define IVTV_CARD_INPUT_AUD_TUNER 1 +#define IVTV_CARD_INPUT_LINE_IN1 2 +#define IVTV_CARD_INPUT_LINE_IN2 3 + +#define IVTV_CARD_MAX_VIDEO_INPUTS 6 +#define IVTV_CARD_MAX_AUDIO_INPUTS 3 +#define IVTV_CARD_MAX_TUNERS 2 + +/* SAA71XX HW inputs */ +#define IVTV_SAA71XX_COMPOSITE0 0 +#define IVTV_SAA71XX_COMPOSITE1 1 +#define IVTV_SAA71XX_COMPOSITE2 2 +#define IVTV_SAA71XX_COMPOSITE3 3 +#define IVTV_SAA71XX_COMPOSITE4 4 +#define IVTV_SAA71XX_COMPOSITE5 5 +#define IVTV_SAA71XX_SVIDEO0 6 +#define IVTV_SAA71XX_SVIDEO1 7 +#define IVTV_SAA71XX_SVIDEO2 8 +#define IVTV_SAA71XX_SVIDEO3 9 + +/* SAA717X needs to mark the tuner input by ORing with this flag */ +#define IVTV_SAA717X_TUNER_FLAG 0x80 + +/* Dummy HW input */ +#define IVTV_DUMMY_AUDIO 0 + +/* GPIO HW inputs */ +#define IVTV_GPIO_TUNER 0 +#define IVTV_GPIO_LINE_IN 1 + +/* SAA717X HW inputs */ +#define IVTV_SAA717X_IN0 0 +#define IVTV_SAA717X_IN1 1 +#define IVTV_SAA717X_IN2 2 + +/* V4L2 capability aliases */ +#define IVTV_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \ + V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE | \ + V4L2_CAP_SLICED_VBI_CAPTURE) +#define IVTV_CAP_DECODER (V4L2_CAP_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT | \ + V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_OVERLAY | V4L2_CAP_VIDEO_OUTPUT_POS) + +struct ivtv_card_video_input { + u8 video_type; /* video input type */ + u8 audio_index; /* index in ivtv_card_audio_input array */ + u16 video_input; /* hardware video input */ +}; + +struct ivtv_card_audio_input { + u8 audio_type; /* audio input type */ + u32 audio_input; /* hardware audio input */ + u16 muxer_input; /* hardware muxer input for boards with a + multiplexer chip */ +}; + +struct ivtv_card_output { + u8 name[32]; + u16 video_output; /* hardware video output */ +}; + +struct ivtv_card_pci_info { + u16 device; + u16 subsystem_vendor; + u16 subsystem_device; +}; + +/* GPIO definitions */ + +/* The mask is the set of bits used by the operation */ + +struct ivtv_gpio_init { /* set initial GPIO DIR and OUT values */ + u16 direction; /* DIR setting. Leave to 0 if no init is needed */ + u16 initial_value; +}; + +struct ivtv_gpio_video_input { /* select tuner/line in input */ + u16 mask; /* leave to 0 if not supported */ + u16 tuner; + u16 composite; + u16 svideo; +}; + +struct ivtv_gpio_audio_input { /* select tuner/line in input */ + u16 mask; /* leave to 0 if not supported */ + u16 tuner; + u16 linein; + u16 radio; +}; + +struct ivtv_gpio_audio_mute { + u16 mask; /* leave to 0 if not supported */ + u16 mute; /* set this value to mute, 0 to unmute */ +}; + +struct ivtv_gpio_audio_mode { + u16 mask; /* leave to 0 if not supported */ + u16 mono; /* set audio to mono */ + u16 stereo; /* set audio to stereo */ + u16 lang1; /* set audio to the first language */ + u16 lang2; /* set audio to the second language */ + u16 both; /* both languages are output */ +}; + +struct ivtv_gpio_audio_freq { + u16 mask; /* leave to 0 if not supported */ + u16 f32000; + u16 f44100; + u16 f48000; +}; + +struct ivtv_gpio_audio_detect { + u16 mask; /* leave to 0 if not supported */ + u16 stereo; /* if the input matches this value then + stereo is detected */ +}; + +struct ivtv_card_tuner { + v4l2_std_id std; /* standard for which the tuner is suitable */ + int tuner; /* tuner ID (from tuner.h) */ +}; + +/* for card information/parameters */ +struct ivtv_card { + int type; + char *name; + u32 v4l2_capabilities; + u32 hw_video; /* hardware used to process video */ + u32 hw_audio; /* hardware used to process audio */ + u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only 1 dev allowed) */ + u32 hw_muxer; /* hardware used to multiplex audio input */ + u32 hw_all; /* all hardware used by the board */ + struct ivtv_card_video_input video_inputs[IVTV_CARD_MAX_VIDEO_INPUTS]; + struct ivtv_card_audio_input audio_inputs[IVTV_CARD_MAX_AUDIO_INPUTS]; + struct ivtv_card_audio_input radio_input; + int nof_outputs; + const struct ivtv_card_output *video_outputs; + u8 gr_config; /* config byte for the ghost reduction device */ + + /* GPIO card-specific settings */ + struct ivtv_gpio_init gpio_init; + struct ivtv_gpio_video_input gpio_video_input; + struct ivtv_gpio_audio_input gpio_audio_input; + struct ivtv_gpio_audio_mute gpio_audio_mute; + struct ivtv_gpio_audio_mode gpio_audio_mode; + struct ivtv_gpio_audio_freq gpio_audio_freq; + struct ivtv_gpio_audio_detect gpio_audio_detect; + + struct ivtv_card_tuner tuners[IVTV_CARD_MAX_TUNERS]; + + /* list of device and subsystem vendor/devices that + correspond to this card type. */ + const struct ivtv_card_pci_info *pci_list; +}; + +int ivtv_get_input(struct ivtv *itv, u16 index, struct v4l2_input *input); +int ivtv_get_output(struct ivtv *itv, u16 index, struct v4l2_output *output); +int ivtv_get_audio_input(struct ivtv *itv, u16 index, struct v4l2_audio *input); +int ivtv_get_audio_output(struct ivtv *itv, u16 index, struct v4l2_audioout *output); +const struct ivtv_card *ivtv_get_card(u16 index); diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c new file mode 100644 index 000000000000..7a876c3e5b19 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -0,0 +1,303 @@ +/* + ioctl control functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-ioctl.h" +#include "ivtv-audio.h" +#include "ivtv-i2c.h" +#include "ivtv-mailbox.h" +#include "ivtv-controls.h" + +static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_AUDIO_VOLUME, + V4L2_CID_AUDIO_BALANCE, + V4L2_CID_AUDIO_BASS, + V4L2_CID_AUDIO_TREBLE, + V4L2_CID_AUDIO_MUTE, + V4L2_CID_AUDIO_LOUDNESS, + 0 +}; + +static const u32 *ctrl_classes[] = { + user_ctrls, + cx2341x_mpeg_ctrls, + NULL +}; + +static int ivtv_queryctrl(struct ivtv *itv, struct v4l2_queryctrl *qctrl) +{ + const char *name; + + IVTV_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id); + + qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id); + if (qctrl->id == 0) + return -EINVAL; + + switch (qctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + if (itv->video_dec_func(itv, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + if (ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + + default: + if (cx2341x_ctrl_query(&itv->params, qctrl)) + qctrl->flags |= V4L2_CTRL_FLAG_DISABLED; + return 0; + } + strncpy(qctrl->name, name, sizeof(qctrl->name) - 1); + qctrl->name[sizeof(qctrl->name) - 1] = 0; + return 0; +} + +static int ivtv_querymenu(struct ivtv *itv, struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + + qctrl.id = qmenu->id; + ivtv_queryctrl(itv, &qctrl); + return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id)); +} + +static int ivtv_s_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ + s32 v = vctrl->value; + + IVTV_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return itv->video_dec_func(itv, VIDIOC_S_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl); + + default: + IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int ivtv_g_ctrl(struct ivtv *itv, struct v4l2_control *vctrl) +{ + IVTV_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id); + + switch (vctrl->id) { + /* Standard V4L2 controls */ + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_HUE: + case V4L2_CID_SATURATION: + case V4L2_CID_CONTRAST: + return itv->video_dec_func(itv, VIDIOC_G_CTRL, vctrl); + + case V4L2_CID_AUDIO_VOLUME: + case V4L2_CID_AUDIO_MUTE: + case V4L2_CID_AUDIO_BALANCE: + case V4L2_CID_AUDIO_BASS: + case V4L2_CID_AUDIO_TREBLE: + case V4L2_CID_AUDIO_LOUDNESS: + return ivtv_i2c_hw(itv, itv->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl); + default: + IVTV_DEBUG_IOCTL("invalid control %x\n", vctrl->id); + return -EINVAL; + } + return 0; +} + +static int ivtv_setup_vbi_fmt(struct ivtv *itv, enum v4l2_mpeg_stream_vbi_fmt fmt) +{ + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE)) + return -EINVAL; + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + /* First try to allocate sliced VBI buffers if needed. */ + if (fmt && itv->vbi.sliced_mpeg_data[0] == NULL) { + int i; + + for (i = 0; i < IVTV_VBI_FRAMES; i++) { + /* Yuck, hardcoded. Needs to be a define */ + itv->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL); + if (itv->vbi.sliced_mpeg_data[i] == NULL) { + while (--i >= 0) { + kfree(itv->vbi.sliced_mpeg_data[i]); + itv->vbi.sliced_mpeg_data[i] = NULL; + } + return -ENOMEM; + } + } + } + + itv->vbi.insert_mpeg = fmt; + + if (itv->vbi.insert_mpeg == 0) { + return 0; + } + /* Need sliced data for mpeg insertion */ + if (get_service_set(itv->vbi.sliced_in) == 0) { + if (itv->is_60hz) + itv->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525; + else + itv->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625; + expand_service_set(itv->vbi.sliced_in, itv->is_50hz); + } + return 0; +} + +int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_control ctrl; + + switch (cmd) { + case VIDIOC_QUERYMENU: + IVTV_DEBUG_IOCTL("VIDIOC_QUERYMENU\n"); + return ivtv_querymenu(itv, arg); + + case VIDIOC_QUERYCTRL: + return ivtv_queryctrl(itv, arg); + + case VIDIOC_S_CTRL: + return ivtv_s_ctrl(itv, arg); + + case VIDIOC_G_CTRL: + return ivtv_g_ctrl(itv, arg); + + case VIDIOC_S_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_s_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + IVTV_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) { + struct cx2341x_mpeg_params p = itv->params; + int err = cx2341x_ext_ctrls(&p, arg, cmd); + + if (err) + return err; + + if (p.video_encoding != itv->params.video_encoding) { + int is_mpeg1 = p.video_encoding == + V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + struct v4l2_format fmt; + + /* fix videodecoder resolution */ + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt.fmt.pix.width = itv->params.width / (is_mpeg1 ? 2 : 1); + fmt.fmt.pix.height = itv->params.height; + itv->video_dec_func(itv, VIDIOC_S_FMT, &fmt); + } + err = cx2341x_update(itv, ivtv_api_func, &itv->params, &p); + if (!err && itv->params.stream_vbi_fmt != p.stream_vbi_fmt) { + err = ivtv_setup_vbi_fmt(itv, p.stream_vbi_fmt); + } + itv->params = p; + itv->dualwatch_stereo_mode = p.audio_properties & 0x0300; + ivtv_audio_set_audio_clock_freq(itv, p.audio_properties & 0x03); + return err; + } + return -EINVAL; + } + + case VIDIOC_G_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + if (c->ctrl_class == V4L2_CTRL_CLASS_USER) { + int i; + int err = 0; + + for (i = 0; i < c->count; i++) { + ctrl.id = c->controls[i].id; + ctrl.value = c->controls[i].value; + err = ivtv_g_ctrl(itv, &ctrl); + c->controls[i].value = ctrl.value; + if (err) { + c->error_idx = i; + break; + } + } + return err; + } + IVTV_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, arg, cmd); + return -EINVAL; + } + + case VIDIOC_TRY_EXT_CTRLS: + { + struct v4l2_ext_controls *c = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n"); + if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) + return cx2341x_ext_ctrls(&itv->params, arg, cmd); + return -EINVAL; + } + + default: + return -EINVAL; + } + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-controls.h b/drivers/media/video/ivtv/ivtv-controls.h new file mode 100644 index 000000000000..5a11149725ad --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-controls.h @@ -0,0 +1,21 @@ +/* + ioctl control functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_control_ioctls(struct ivtv *itv, unsigned int cmd, void *arg); diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c new file mode 100644 index 000000000000..8d3876588b88 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -0,0 +1,1385 @@ +/* + ivtv driver initialization and card probing + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Main Driver file for the ivtv project: + * Driver for the Conexant CX23415/CX23416 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + * + * Kurouto Sikou CX23416GYC-STVLP tested by K.Ohta + * using information from T.Adachi,Takeru KOMORIYA and others :-) + * + * Nagase TRANSGEAR 5000TV, Aopen VA2000MAX-STN6 and I/O data GV-MVP/RX + * version by T.Adachi. Special thanks Mr.Suzuki + */ + +#include "ivtv-driver.h" +#include "ivtv-version.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-firmware.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-mailbox.h" +#include "ivtv-streams.h" +#include "ivtv-ioctl.h" +#include "ivtv-cards.h" +#include "ivtv-vbi.h" +#include "ivtv-audio.h" +#include "ivtv-gpio.h" +#include "ivtv-yuv.h" + +#include +#include +#include + +/* var to keep track of the number of array elements in use */ +int ivtv_cards_active = 0; + +/* If you have already X v4l cards, then set this to X. This way + the device numbers stay matched. Example: you have a WinTV card + without radio and a PVR-350 with. Normally this would give a + video1 device together with a radio0 device for the PVR. By + setting this to 1 you ensure that radio0 is now also radio1. */ +int ivtv_first_minor = 0; + +/* Master variable for all ivtv info */ +struct ivtv *ivtv_cards[IVTV_MAX_CARDS]; + +/* Protects ivtv_cards_active */ +spinlock_t ivtv_cards_lock = 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,} +}; + +MODULE_DEVICE_TABLE(pci,ivtv_pci_tbl); + +const u32 yuv_offset[4] = { + IVTV_YUV_BUFFER_OFFSET, + IVTV_YUV_BUFFER_OFFSET_1, + IVTV_YUV_BUFFER_OFFSET_2, + IVTV_YUV_BUFFER_OFFSET_3 +}; + +/* Parameter declarations */ +static int cardtype[IVTV_MAX_CARDS]; +static int tuner[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; + +static int cardtype_c = 1; +static int tuner_c = 1; +static int radio_c = 1; +static char pal[] = "--"; +static char secam[] = "--"; +static char ntsc[] = "-"; + +/* Buffers */ +static int enc_mpg_buffers = IVTV_DEFAULT_ENC_MPG_BUFFERS; +static int enc_yuv_buffers = IVTV_DEFAULT_ENC_YUV_BUFFERS; +static int enc_vbi_buffers = IVTV_DEFAULT_ENC_VBI_BUFFERS; +static int enc_pcm_buffers = IVTV_DEFAULT_ENC_PCM_BUFFERS; +static int dec_mpg_buffers = IVTV_DEFAULT_DEC_MPG_BUFFERS; +static int dec_yuv_buffers = IVTV_DEFAULT_DEC_YUV_BUFFERS; +static int dec_vbi_buffers = IVTV_DEFAULT_DEC_VBI_BUFFERS; + +static int ivtv_yuv_mode = 0; +static int ivtv_yuv_threshold=480; +static int ivtv_pci_latency = 1; + +int ivtv_debug = 0; + +int newi2c = -1; + +module_param_array(tuner, int, &tuner_c, 0644); +module_param_array(radio, bool, &radio_c, 0644); +module_param_array(cardtype, int, &cardtype_c, 0644); +module_param_string(pal, pal, sizeof(pal), 0644); +module_param_string(secam, secam, sizeof(secam), 0644); +module_param_string(ntsc, ntsc, sizeof(ntsc), 0644); +module_param_named(debug,ivtv_debug, int, 0644); +module_param(ivtv_pci_latency, int, 0644); +module_param(ivtv_yuv_mode, int, 0644); +module_param(ivtv_yuv_threshold, int, 0644); +module_param(ivtv_first_minor, int, 0644); + +module_param(enc_mpg_buffers, int, 0644); +module_param(enc_yuv_buffers, int, 0644); +module_param(enc_vbi_buffers, int, 0644); +module_param(enc_pcm_buffers, int, 0644); +module_param(dec_mpg_buffers, int, 0644); +module_param(dec_yuv_buffers, int, 0644); +module_param(dec_vbi_buffers, int, 0644); + +module_param(newi2c, int, 0644); + +MODULE_PARM_DESC(tuner, "Tuner type selection,\n" + "\t\t\tsee tuner.h for values"); +MODULE_PARM_DESC(radio, + "Enable or disable the radio. Use only if autodetection\n" + "\t\t\tfails. 0 = disable, 1 = enable"); +MODULE_PARM_DESC(cardtype, + "Only use this option if your card is not detected properly.\n" + "\t\tSpecify card type:\n" + "\t\t\t 1 = WinTV PVR 250\n" + "\t\t\t 2 = WinTV PVR 350\n" + "\t\t\t 3 = WinTV PVR-150 or PVR-500\n" + "\t\t\t 4 = AVerMedia M179\n" + "\t\t\t 5 = YUAN MPG600/Kuroutoshikou iTVC16-STVLP\n" + "\t\t\t 6 = YUAN MPG160/Kuroutoshikou iTVC15-STVLP\n" + "\t\t\t 7 = YUAN PG600/DIAMONDMM PVR-550 (CX Falcon 2)\n" + "\t\t\t 8 = Adaptec AVC-2410\n" + "\t\t\t 9 = Adaptec AVC-2010\n" + "\t\t\t10 = NAGASE TRANSGEAR 5000TV\n" + "\t\t\t11 = AOpen VA2000MAX-STN6\n" + "\t\t\t12 = YUAN MPG600GR/Kuroutoshikou CX23416GYC-STVLP\n" + "\t\t\t13 = I/O Data GV-MVP/RX\n" + "\t\t\t14 = I/O Data GV-MVP/RX2E\n" + "\t\t\t15 = GOTVIEW PCI DVD\n" + "\t\t\t16 = GOTVIEW PCI DVD2 Deluxe\n" + "\t\t\t17 = Yuan MPC622\n" + "\t\t\t18 = Digital Cowboy DCT-MTVP1\n" +#ifdef HAVE_XC3028 + "\t\t\t19 = Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01\n" +#endif + "\t\t\t 0 = Autodetect (default)\n" + "\t\t\t-1 = Ignore this card\n\t\t"); +MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60"); +MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC"); +MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K"); +MODULE_PARM_DESC(debug, + "Debug level (bitmask). Default: errors only\n" + "\t\t\t(debug = 511 gives full debugging)"); +MODULE_PARM_DESC(ivtv_pci_latency, + "Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n" + "\t\t\tDefault: Yes"); +MODULE_PARM_DESC(ivtv_yuv_mode, + "Specify the yuv playback mode:\n" + "\t\t\t0 = interlaced\n\t\t\t1 = progressive\n\t\t\t2 = auto\n" + "\t\t\tDefault: 0 (interlaced)"); +MODULE_PARM_DESC(ivtv_yuv_threshold, + "If ivtv_yuv_mode is 2 (auto) then playback content as\n\t\tprogressive if src height <= ivtv_yuvthreshold\n" + "\t\t\tDefault: 480");; +MODULE_PARM_DESC(enc_mpg_buffers, + "Encoder MPG Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_MPG_BUFFERS)); +MODULE_PARM_DESC(enc_yuv_buffers, + "Encoder YUV Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_YUV_BUFFERS)); +MODULE_PARM_DESC(enc_vbi_buffers, + "Encoder VBI Buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_VBI_BUFFERS)); +MODULE_PARM_DESC(enc_pcm_buffers, + "Encoder PCM buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_ENC_PCM_BUFFERS)); +MODULE_PARM_DESC(dec_mpg_buffers, + "Decoder MPG buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_MPG_BUFFERS)); +MODULE_PARM_DESC(dec_yuv_buffers, + "Decoder YUV buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_YUV_BUFFERS)); +MODULE_PARM_DESC(dec_vbi_buffers, + "Decoder VBI buffers (in MB)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_DEC_VBI_BUFFERS)); +MODULE_PARM_DESC(newi2c, + "Use new I2C implementation\n" + "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" + "\t\t\tDefault is autodetect"); + +MODULE_PARM_DESC(ivtv_first_minor, "Set minor assigned to first card"); + +MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); +MODULE_DESCRIPTION("CX23415/CX23416 driver"); +MODULE_SUPPORTED_DEVICE + ("CX23415/CX23416 MPEG2 encoder (WinTV PVR-150/250/350/500,\n" + "\t\t\tYuan MPG series and similar)"); +MODULE_LICENSE("GPL"); + +MODULE_VERSION(IVTV_VERSION); + +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask) +{ + itv->irqmask &= ~mask; + write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); +} + +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask) +{ + itv->irqmask |= mask; + write_reg_sync(itv->irqmask, IVTV_REG_IRQMASK); +} + +int ivtv_set_output_mode(struct ivtv *itv, int mode) +{ + int old_mode; + + spin_lock(&itv->lock); + old_mode = itv->output_mode; + if (old_mode == 0) + itv->output_mode = old_mode = mode; + spin_unlock(&itv->lock); + return old_mode; +} + +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv) +{ + switch (itv->output_mode) { + case OUT_MPG: + return &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + case OUT_YUV: + return &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + default: + return NULL; + } +} + +int ivtv_waitq(wait_queue_head_t *waitq) +{ + DEFINE_WAIT(wait); + + prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE); + schedule(); + finish_wait(waitq, &wait); + return signal_pending(current) ? -EINTR : 0; +} + +/* Generic utility functions */ +int ivtv_sleep_timeout(int timeout, int intr) +{ + int ret; + + do { + set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + timeout = schedule_timeout(timeout); + if (intr && (ret = signal_pending(current))) + return ret; + } while (timeout); + return 0; +} + +/* Release ioremapped memory */ +static void ivtv_iounmap(struct ivtv *itv) +{ + if (itv == NULL) + return; + + /* Release registers memory */ + if (itv->reg_mem != NULL) { + IVTV_DEBUG_INFO("releasing reg_mem\n"); + iounmap(itv->reg_mem); + itv->reg_mem = NULL; + } + /* Release io memory */ + if (itv->has_cx23415 && itv->dec_mem != NULL) { + IVTV_DEBUG_INFO("releasing dec_mem\n"); + iounmap(itv->dec_mem); + } + itv->dec_mem = NULL; + + /* Release io memory */ + if (itv->enc_mem != NULL) { + IVTV_DEBUG_INFO("releasing enc_mem\n"); + iounmap(itv->enc_mem); + itv->enc_mem = NULL; + } +} + +/* Hauppauge card? get values from tveeprom */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv) +{ + u8 eedata[256]; + + itv->i2c_client.addr = 0xA0 >> 1; + tveeprom_read(&itv->i2c_client, eedata, sizeof(eedata)); + tveeprom_hauppauge_analog(&itv->i2c_client, tv, eedata); +} + +static void ivtv_process_eeprom(struct ivtv *itv) +{ + struct tveeprom tv; + int pci_slot = PCI_SLOT(itv->dev->devfn); + + ivtv_read_eeprom(itv, &tv); + + /* Many thanks to Steven Toth from Hauppauge for providing the + model numbers */ + switch (tv.model) { + /* In a few cases the PCI subsystem IDs do not correctly + identify the card. A better method is to check the + model number from the eeprom instead. */ + case 32000 ... 32999: + case 48000 ... 48099: /* 48??? range are PVR250s with a cx23415 */ + case 48400 ... 48599: + itv->card = ivtv_get_card(IVTV_CARD_PVR_250); + break; + case 48100 ... 48399: + case 48600 ... 48999: + itv->card = ivtv_get_card(IVTV_CARD_PVR_350); + break; + case 23000 ... 23999: /* PVR500 */ + case 25000 ... 25999: /* Low profile PVR150 */ + case 26000 ... 26999: /* Regular PVR150 */ + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + break; + case 0: + IVTV_ERR("Invalid EEPROM\n"); + return; + default: + IVTV_ERR("Unknown model %d, defaulting to PVR-150\n", tv.model); + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + break; + } + + switch (tv.model) { + /* Old style PVR350 (with an saa7114) uses this input for + the tuner. */ + case 48254: + itv->card = ivtv_get_card(IVTV_CARD_PVR_350_V1); + break; + default: + break; + } + + itv->v4l2_cap = itv->card->v4l2_capabilities; + itv->card_name = itv->card->name; + + /* If this is a PVR500 then it should be possible to detect whether it is the + first or second unit by looking at the subsystem device ID: is bit 4 is + set, then it is the second unit (according to info from Hauppauge). + + However, while this works for most cards, I have seen a few PVR500 cards + where both units have the same subsystem ID. + + So instead I look at the reported 'PCI slot' (which is the slot on the PVR500 + PCI bridge) and if it is 8, then it is assumed to be the first unit, otherwise + it is the second unit. It is possible that it is a different slot when ivtv is + used in Xen, in that case I ignore this card here. The worst that can happen + is that the card presents itself with a non-working radio device. + + This detection is needed since the eeprom reports incorrectly that a radio is + present on the second unit. */ + if (tv.model / 1000 == 23) { + itv->card_name = "WinTV PVR 500"; + if (pci_slot == 8 || pci_slot == 9) { + int is_first = (pci_slot & 1) == 0; + + itv->card_name = is_first ? "WinTV PVR 500 (unit #1)" : + "WinTV PVR 500 (unit #2)"; + if (!is_first) { + IVTV_INFO("Correcting tveeprom data: no radio present on second unit\n"); + tv.has_radio = 0; + } + } + } + IVTV_INFO("Autodetected %s\n", itv->card_name); + + switch (tv.tuner_hauppauge_model) { + case 85: + case 99: + case 112: + itv->pvr150_workaround = 1; + break; + default: + break; + } + if (tv.tuner_type == TUNER_ABSENT) + IVTV_ERR("tveeprom cannot autodetect tuner!"); + + if (itv->options.tuner == -1) + itv->options.tuner = tv.tuner_type; + if (itv->options.radio == -1) + itv->options.radio = (tv.has_radio != 0); + /* only enable newi2c if an IR blaster is present */ + /* FIXME: for 2.6.20 the test against 2 should be removed */ + if (itv->options.newi2c == -1 && tv.has_ir != -1 && tv.has_ir != 2) { + itv->options.newi2c = (tv.has_ir & 2) ? 1 : 0; + if (itv->options.newi2c) { + IVTV_INFO("reopen i2c bus for IR-blaster support\n"); + exit_ivtv_i2c(itv); + init_ivtv_i2c(itv); + } + } + + if (itv->std != 0) + /* user specified tuner standard */ + return; + + /* autodetect tuner standard */ + if (tv.tuner_formats & V4L2_STD_PAL) { + IVTV_DEBUG_INFO("PAL tuner detected\n"); + itv->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H; + } else if (tv.tuner_formats & V4L2_STD_NTSC) { + IVTV_DEBUG_INFO("NTSC tuner detected\n"); + itv->std |= V4L2_STD_NTSC_M; + } else if (tv.tuner_formats & V4L2_STD_SECAM) { + IVTV_DEBUG_INFO("SECAM tuner detected\n"); + itv->std |= V4L2_STD_SECAM_L; + } else { + IVTV_INFO("No tuner detected, default to NTSC-M\n"); + itv->std |= V4L2_STD_NTSC_M; + } +} + +static v4l2_std_id ivtv_parse_std(struct ivtv *itv) +{ + switch (pal[0]) { + case '6': + return V4L2_STD_PAL_60; + case 'b': + case 'B': + case 'g': + case 'G': + return V4L2_STD_PAL_BG; + case 'h': + case 'H': + return V4L2_STD_PAL_H; + case 'n': + case 'N': + if (pal[1] == 'c' || pal[1] == 'C') + return V4L2_STD_PAL_Nc; + return V4L2_STD_PAL_N; + case 'i': + case 'I': + return V4L2_STD_PAL_I; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_PAL_DK; + case 'M': + case 'm': + return V4L2_STD_PAL_M; + case '-': + break; + default: + IVTV_WARN("pal= argument not recognised\n"); + return 0; + } + + switch (secam[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + case 'h': + case 'H': + return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H; + case 'd': + case 'D': + case 'k': + case 'K': + return V4L2_STD_SECAM_DK; + case 'l': + case 'L': + if (secam[1] == 'C' || secam[1] == 'c') + return V4L2_STD_SECAM_LC; + return V4L2_STD_SECAM_L; + case '-': + break; + default: + IVTV_WARN("secam= argument not recognised\n"); + return 0; + } + + switch (ntsc[0]) { + case 'm': + case 'M': + return V4L2_STD_NTSC_M; + case 'j': + case 'J': + return V4L2_STD_NTSC_M_JP; + case 'k': + case 'K': + return V4L2_STD_NTSC_M_KR; + case '-': + break; + default: + IVTV_WARN("ntsc= argument not recognised\n"); + return 0; + } + + /* no match found */ + return 0; +} + +static void ivtv_process_options(struct ivtv *itv) +{ + const char *chipname; + int i, j; + + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers; + itv->options.megabytes[IVTV_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_MPG] = dec_mpg_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_YUV] = dec_yuv_buffers; + itv->options.megabytes[IVTV_DEC_STREAM_TYPE_VBI] = dec_vbi_buffers; + itv->options.cardtype = cardtype[itv->num]; + itv->options.tuner = tuner[itv->num]; + itv->options.radio = radio[itv->num]; + itv->options.newi2c = newi2c; + + itv->std = ivtv_parse_std(itv); + itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15); + chipname = itv->has_cx23415 ? "cx23415" : "cx23416"; + if (itv->options.cardtype == -1) { + IVTV_INFO("Ignore card (detected %s based chip)\n", chipname); + return; + } + if ((itv->card = ivtv_get_card(itv->options.cardtype - 1))) { + IVTV_INFO("User specified %s card (detected %s based chip)\n", + itv->card->name, chipname); + } else if (itv->options.cardtype != 0) { + IVTV_ERR("Unknown user specified type, trying to autodetect card\n"); + } + if (itv->card == NULL) { + if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE || + itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 || + itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) { + itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150); + IVTV_INFO("Autodetected Hauppauge card (%s based)\n", + chipname); + } + } + if (itv->card == NULL) { + for (i = 0; (itv->card = ivtv_get_card(i)); i++) { + if (itv->card->pci_list == NULL) + continue; + for (j = 0; itv->card->pci_list[j].device; j++) { + if (itv->dev->device != + itv->card->pci_list[j].device) + continue; + if (itv->dev->subsystem_vendor != + itv->card->pci_list[j].subsystem_vendor) + continue; + if (itv->dev->subsystem_device != + itv->card->pci_list[j].subsystem_device) + continue; + IVTV_INFO("Autodetected %s card (%s based)\n", + itv->card->name, chipname); + goto done; + } + } + } +done: + + if (itv->card == NULL) { + itv->card = ivtv_get_card(IVTV_CARD_PVR_150); + IVTV_ERR("Unknown card: vendor/device: %04x/%04x\n", + itv->dev->vendor, itv->dev->device); + IVTV_ERR(" subsystem vendor/device: %04x/%04x\n", + itv->dev->subsystem_vendor, itv->dev->subsystem_device); + IVTV_ERR(" %s based\n", chipname); + IVTV_ERR("Defaulting to %s card\n", itv->card->name); + IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n"); + IVTV_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n"); + IVTV_ERR("Prefix your subject line with [UNKNOWN CARD].\n"); + } + itv->v4l2_cap = itv->card->v4l2_capabilities; + itv->card_name = itv->card->name; +} + +/* Precondition: the ivtv structure has been memset to 0. Only + the dev and num fields have been filled in. + No assumptions on the card type may be made here (see ivtv_init_struct2 + for that). + */ +static int __devinit ivtv_init_struct1(struct ivtv *itv) +{ + itv->base_addr = pci_resource_start(itv->dev, 0); + itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */ + itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */ + + mutex_init(&itv->i2c_bus_lock); + mutex_init(&itv->udma.lock); + + itv->lock = SPIN_LOCK_UNLOCKED; + itv->dma_reg_lock = SPIN_LOCK_UNLOCKED; + + itv->vbi.work_queues = create_workqueue("ivtv_vbi"); + if (itv->vbi.work_queues == NULL) { + IVTV_ERR("Could not create VBI workqueue\n"); + return -1; + } + + itv->yuv_info.work_queues = create_workqueue("ivtv_yuv"); + if (itv->yuv_info.work_queues == NULL) { + IVTV_ERR("Could not create YUV workqueue\n"); + destroy_workqueue(itv->vbi.work_queues); + return -1; + } + + INIT_WORK(&itv->vbi.work_queue, vbi_work_handler); + INIT_WORK(&itv->yuv_info.work_queue, ivtv_yuv_work_handler); + + /* start counting open_id at 1 */ + itv->open_id = 1; + + /* Initial settings */ + cx2341x_fill_defaults(&itv->params); + itv->params.port = CX2341X_PORT_MEMORY; + itv->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI; + init_waitqueue_head(&itv->cap_w); + init_waitqueue_head(&itv->event_waitq); + init_waitqueue_head(&itv->vsync_waitq); + init_waitqueue_head(&itv->dma_waitq); + init_timer(&itv->dma_timer); + itv->dma_timer.function = ivtv_unfinished_dma; + itv->dma_timer.data = (unsigned long)itv; + + itv->cur_dma_stream = -1; + itv->audio_stereo_mode = AUDIO_STEREO; + itv->audio_bilingual_mode = AUDIO_MONO_LEFT; + + /* Ctrls */ + itv->speed = 1000; + + /* VBI */ + itv->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + itv->vbi.sliced_in = &itv->vbi.in.fmt.sliced; + + /* OSD */ + itv->osd_global_alpha_state = 1; + itv->osd_global_alpha = 255; + + /* YUV */ + atomic_set(&itv->yuv_info.next_dma_frame, -1); + itv->yuv_info.lace_mode = ivtv_yuv_mode; + itv->yuv_info.lace_threshold = ivtv_yuv_threshold; + return 0; +} + +/* Second initialization part. Here the card type has been + autodetected. */ +static void __devinit ivtv_init_struct2(struct ivtv *itv) +{ + int i; + + for (i = 0; i < IVTV_CARD_MAX_VIDEO_INPUTS; i++) + if (itv->card->video_inputs[i].video_type == 0) + break; + itv->nof_inputs = i; + for (i = 0; i < IVTV_CARD_MAX_AUDIO_INPUTS; i++) + if (itv->card->audio_inputs[i].audio_type == 0) + break; + itv->nof_audio_inputs = i; + + /* 0x00EF = saa7114(239) 0x00F0 = saa7115(240) 0x0106 = micro */ + if (itv->card->hw_all & (IVTV_HW_SAA7115 | IVTV_HW_SAA717X)) + itv->digitizer = 0xF1; + else if (itv->card->hw_all & IVTV_HW_SAA7114) + itv->digitizer = 0xEF; + else /* cx25840 */ + itv->digitizer = 0x140; + + if (itv->card->hw_all & IVTV_HW_CX25840) { + itv->vbi.sliced_size = 288; /* multiple of 16, real size = 284 */ + } else { + itv->vbi.sliced_size = 64; /* multiple of 16, real size = 52 */ + } + + /* Find tuner input */ + for (i = 0; i < itv->nof_inputs; i++) { + if (itv->card->video_inputs[i].video_type == + IVTV_CARD_INPUT_VID_TUNER) + break; + } + if (i == itv->nof_inputs) + i = 0; + itv->active_input = i; + itv->audio_input = itv->card->video_inputs[i].audio_index; + if (itv->card->hw_all & IVTV_HW_CX25840) + itv->video_dec_func = ivtv_cx25840; + else if (itv->card->hw_all & IVTV_HW_SAA717X) + itv->video_dec_func = ivtv_saa717x; + else + itv->video_dec_func = ivtv_saa7115; +} + +static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + u16 cmd; + unsigned char pci_latency; + + IVTV_DEBUG_INFO("Enabling pci device\n"); + + if (pci_enable_device(dev)) { + IVTV_ERR("Can't enable device %d!\n", itv->num); + return -EIO; + } + if (pci_set_dma_mask(dev, 0xffffffff)) { + IVTV_ERR("No suitable DMA available on card %d.\n", itv->num); + return -EIO; + } + if (!request_mem_region(itv->base_addr, IVTV_ENCODER_SIZE, "ivtv encoder")) { + IVTV_ERR("Cannot request encoder memory region on card %d.\n", itv->num); + return -EIO; + } + + if (!request_mem_region(itv->base_addr + IVTV_REG_OFFSET, + IVTV_REG_SIZE, "ivtv registers")) { + IVTV_ERR("Cannot request register memory region on card %d.\n", itv->num); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + return -EIO; + } + + if (itv->has_cx23415 && + !request_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE, "ivtv decoder")) { + IVTV_ERR("Cannot request decoder memory region on card %d.\n", itv->num); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + return -EIO; + } + + /* Check for bus mastering */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n"); + pci_set_master(dev); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (!(cmd & PCI_COMMAND_MASTER)) { + IVTV_ERR("Bus Mastering is not enabled\n"); + return -ENXIO; + } + } + IVTV_DEBUG_INFO("Bus Mastering Enabled.\n"); + + pci_read_config_byte(dev, PCI_CLASS_REVISION, &itv->card_rev); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + + if (pci_latency < 64 && ivtv_pci_latency) { + IVTV_INFO("Unreasonably low latency timer, " + "setting to 64 (was %d)\n", pci_latency); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency); + } + /* This config space value relates to DMA latencies. The + default value 0x8080 is too low however and will lead + to DMA errors. 0xffff is the max value which solves + these problems. */ + pci_write_config_dword(dev, 0x40, 0xffff); + + IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, " + "irq: %d, latency: %d, memory: 0x%lx\n", + itv->dev->device, itv->card_rev, dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), + itv->dev->irq, pci_latency, (unsigned long)itv->base_addr); + + return 0; +} + +static void ivtv_request_module(struct ivtv *itv, const char *name) +{ + if (request_module(name) != 0) { + IVTV_ERR("Failed to load module %s\n", name); + } else { + IVTV_DEBUG_INFO("Loaded module %s\n", name); + } +} + +static void ivtv_load_and_init_modules(struct ivtv *itv) +{ + struct v4l2_control ctrl; + u32 hw = itv->card->hw_all; + int i; + + /* load modules */ +#ifndef CONFIG_VIDEO_TUNER + if (hw & IVTV_HW_TUNER) { + ivtv_request_module(itv, "tuner"); +#ifdef HAVE_XC3028 + if (itv->options.tuner == TUNER_XCEIVE_XC3028) + ivtv_request_module(itv, "xc3028-tuner"); +#endif + } +#endif +#ifndef CONFIG_VIDEO_CX25840 + if (hw & IVTV_HW_CX25840) + ivtv_request_module(itv, "cx25840"); +#endif +#ifndef CONFIG_VIDEO_SAA711X + if (hw & IVTV_HW_SAA711X) + ivtv_request_module(itv, "saa7115"); +#endif +#ifndef CONFIG_VIDEO_SAA7127 + if (hw & IVTV_HW_SAA7127) + ivtv_request_module(itv, "saa7127"); +#endif + if (hw & IVTV_HW_SAA717X) + ivtv_request_module(itv, "saa717x"); +#ifndef CONFIG_VIDEO_UPD64031A + if (hw & IVTV_HW_UPD64031A) + ivtv_request_module(itv, "upd64031a"); +#endif +#ifndef CONFIG_VIDEO_UPD64083 + if (hw & IVTV_HW_UPD6408X) + ivtv_request_module(itv, "upd64083"); +#endif +#ifndef CONFIG_VIDEO_MSP3400 + if (hw & IVTV_HW_MSP34XX) + ivtv_request_module(itv, "msp3400"); +#endif + if (hw & IVTV_HW_TVAUDIO) + ivtv_request_module(itv, "tvaudio"); +#ifndef CONFIG_VIDEO_WM8775 + if (hw & IVTV_HW_WM8775) + ivtv_request_module(itv, "wm8775"); +#endif +#ifndef CONFIG_VIDEO_WM8739 + if (hw & IVTV_HW_WM8739) + ivtv_request_module(itv, "wm8739"); +#endif +#ifndef CONFIG_VIDEO_CS53L32A + if (hw & IVTV_HW_CS53L32A) + ivtv_request_module(itv, "cs53l32a"); +#endif + + /* check which i2c devices are actually found */ + for (i = 0; i < 32; i++) { + u32 device = 1 << i; + + if (!(device & hw)) + continue; + if (device == IVTV_HW_GPIO) { + /* GPIO is always available */ + itv->hw_flags |= IVTV_HW_GPIO; + continue; + } + if (ivtv_i2c_hw_addr(itv, device) > 0) + itv->hw_flags |= device; + } + + hw = itv->hw_flags; + + if (itv->card->type == IVTV_CARD_CX23416GYC) { + /* Several variations of this card exist, detect which card + type should be used. */ + if ((hw & (IVTV_HW_UPD64031A | IVTV_HW_UPD6408X)) == 0) + itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGRYCS); + else if ((hw & IVTV_HW_UPD64031A) == 0) + itv->card = ivtv_get_card(IVTV_CARD_CX23416GYC_NOGR); + } + + if (hw & IVTV_HW_CX25840) { + /* CX25840_CID_ENABLE_PVR150_WORKAROUND */ + ctrl.id = V4L2_CID_PRIVATE_BASE; + ctrl.value = itv->pvr150_workaround; + itv->video_dec_func(itv, VIDIOC_S_CTRL, &ctrl); + + itv->vbi.raw_decoder_line_size = 1444; + itv->vbi.raw_decoder_sav_odd_field = 0x20; + itv->vbi.raw_decoder_sav_even_field = 0x60; + itv->vbi.sliced_decoder_line_size = 272; + itv->vbi.sliced_decoder_sav_odd_field = 0xB0; + itv->vbi.sliced_decoder_sav_even_field = 0xF0; + } + + if (hw & IVTV_HW_SAA711X) { + struct v4l2_chip_ident v = { V4L2_CHIP_MATCH_I2C_DRIVER, I2C_DRIVERID_SAA711X }; + + /* determine the exact saa711x model */ + itv->hw_flags &= ~IVTV_HW_SAA711X; + + ivtv_saa7115(itv, VIDIOC_G_CHIP_IDENT, &v); + if (v.ident == V4L2_IDENT_SAA7114) { + itv->hw_flags |= IVTV_HW_SAA7114; + /* VBI is not yet supported by the saa7114 driver. */ + itv->v4l2_cap &= ~(V4L2_CAP_SLICED_VBI_CAPTURE|V4L2_CAP_VBI_CAPTURE); + } + else { + itv->hw_flags |= IVTV_HW_SAA7115; + } + itv->vbi.raw_decoder_line_size = 1443; + itv->vbi.raw_decoder_sav_odd_field = 0x25; + itv->vbi.raw_decoder_sav_even_field = 0x62; + itv->vbi.sliced_decoder_line_size = 51; + itv->vbi.sliced_decoder_sav_odd_field = 0xAB; + itv->vbi.sliced_decoder_sav_even_field = 0xEC; + } + + if (hw & IVTV_HW_SAA717X) { + itv->vbi.raw_decoder_line_size = 1443; + itv->vbi.raw_decoder_sav_odd_field = 0x25; + itv->vbi.raw_decoder_sav_even_field = 0x62; + itv->vbi.sliced_decoder_line_size = 51; + itv->vbi.sliced_decoder_sav_odd_field = 0xAB; + itv->vbi.sliced_decoder_sav_even_field = 0xEC; + } +} + +static int __devinit ivtv_probe(struct pci_dev *dev, + const struct pci_device_id *pci_id) +{ + int retval = 0; + int video_input; + int yuv_buf_size; + int vbi_buf_size; + int fw_retry_count = 3; + struct ivtv *itv; + struct v4l2_frequency vf; + + spin_lock(&ivtv_cards_lock); + + /* Make sure we've got a place for this card */ + if (ivtv_cards_active == IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv: Maximum number of cards detected (%d).\n", + ivtv_cards_active); + spin_unlock(&ivtv_cards_lock); + return -ENOMEM; + } + + itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC); + if (itv == 0) { + spin_unlock(&ivtv_cards_lock); + return -ENOMEM; + } + ivtv_cards[ivtv_cards_active] = itv; + itv->dev = dev; + itv->num = ivtv_cards_active++; + snprintf(itv->name, sizeof(itv->name) - 1, "ivtv%d", itv->num); + if (itv->num) { + printk(KERN_INFO "ivtv: ====================== NEXT CARD ======================\n"); + } + + spin_unlock(&ivtv_cards_lock); + + ivtv_process_options(itv); + if (itv->options.cardtype == -1) { + retval = -ENODEV; + goto err; + } + if (ivtv_init_struct1(itv)) { + retval = -ENOMEM; + goto err; + } + + IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr); + + /* PCI Device Setup */ + if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) { + if (retval == -EIO) + goto free_workqueue; + else if (retval == -ENXIO) + goto free_mem; + } + /* save itv in the pci struct for later use */ + pci_set_drvdata(dev, itv); + + /* map io memory */ + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_ENCODER_OFFSET, IVTV_ENCODER_SIZE); + itv->enc_mem = ioremap_nocache(itv->base_addr + IVTV_ENCODER_OFFSET, + IVTV_ENCODER_SIZE); + if (!itv->enc_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + + if (itv->has_cx23415) { + IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n", + itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + itv->dec_mem = ioremap_nocache(itv->base_addr + IVTV_DECODER_OFFSET, + IVTV_DECODER_SIZE); + if (!itv->dec_mem) { + IVTV_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_mem; + } + } + else { + itv->dec_mem = itv->enc_mem; + } + + /* map registers memory */ + 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_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n"); + IVTV_ERR("or disabling CONFIG_HIMEM4G into the kernel would help\n"); + retval = -ENOMEM; + goto free_io; + } + + while (--fw_retry_count > 0) { + /* load firmware */ + if (ivtv_firmware_init(itv) == 0) + break; + if (fw_retry_count > 1) + IVTV_WARN("Retry loading firmware\n"); + } + if (fw_retry_count == 0) { + IVTV_ERR("Error initializing firmware\n"); + goto free_i2c; + } + + /* Try and get firmware versions */ + IVTV_DEBUG_INFO("Getting firmware version..\n"); + ivtv_firmware_versions(itv); + + /* Check yuv output filter table */ + if (itv->has_cx23415) ivtv_yuv_filter_check(itv); + + ivtv_gpio_init(itv); + + /* active i2c */ + IVTV_DEBUG_INFO("activating i2c...\n"); + if (init_ivtv_i2c(itv)) { + IVTV_ERR("Could not initialize i2c\n"); + goto free_irq; + } + + IVTV_DEBUG_INFO("Active card count: %d.\n", ivtv_cards_active); + + if (itv->card->hw_all & IVTV_HW_TVEEPROM) { +#ifdef CONFIG_VIDEO_TVEEPROM_MODULE + ivtv_request_module(itv, "tveeprom"); +#endif + /* Based on the model number the cardtype may be changed. + The PCI IDs are not always reliable. */ + ivtv_process_eeprom(itv); + } + + if (itv->std == 0) { + itv->std = V4L2_STD_NTSC_M; + } + + if (itv->options.tuner == -1) { + int i; + + for (i = 0; i < IVTV_CARD_MAX_TUNERS; i++) { + if ((itv->std & itv->card->tuners[i].std) == 0) + continue; + itv->options.tuner = itv->card->tuners[i].tuner; + break; + } + } + /* if no tuner was found, then pick the first tuner in the card list */ + if (itv->options.tuner == -1 && itv->card->tuners[0].std) { + itv->std = itv->card->tuners[0].std; + itv->options.tuner = itv->card->tuners[0].tuner; + } + if (itv->options.radio == -1) + itv->options.radio = (itv->card->radio_input.audio_type != 0); + + /* The card is now fully identified, continue with card-specific + initialization. */ + ivtv_init_struct2(itv); + + ivtv_load_and_init_modules(itv); + + if (itv->std & V4L2_STD_525_60) { + itv->is_60hz = 1; + itv->is_out_60hz = 1; + } else { + itv->is_50hz = 1; + itv->is_out_50hz = 1; + } + itv->params.video_gop_size = itv->is_60hz ? 15 : 12; + + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_MPG] = 0x08000; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_PCM] = 0x01200; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_MPG] = 0x10000; + + /* 0x15180 == 720 * 480 / 4, 0x19500 == 720 * 576 / 4 */ + yuv_buf_size = itv->is_60hz ? 0x15180 : 0x19500; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_YUV] = yuv_buf_size / 2; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_YUV] = yuv_buf_size / 8; + + /* Setup VBI Raw Size. Should be big enough to hold PAL. + It is possible to switch between PAL and NTSC, so we need to + take the largest size here. */ + /* 1456 is multiple of 16, real size = 1444 */ + itv->vbi.raw_size = 1456; + /* We use a buffer size of 1/2 of the total size needed for a + frame. This is actually very useful, since we now receive + a field at a time and that makes 'compressing' the raw data + down to size by stripping off the SAV codes a lot easier. + Note: having two different buffer sizes prevents standard + switching on the fly. We need to find a better solution... */ + vbi_buf_size = itv->vbi.raw_size * (itv->is_60hz ? 24 : 36) / 2; + itv->stream_buf_size[IVTV_ENC_STREAM_TYPE_VBI] = vbi_buf_size; + itv->stream_buf_size[IVTV_DEC_STREAM_TYPE_VBI] = sizeof(struct v4l2_sliced_vbi_data) * 36; + + if (itv->options.radio > 0) + itv->v4l2_cap |= V4L2_CAP_RADIO; + + retval = ivtv_streams_setup(itv); + if (retval) { + IVTV_ERR("Error %d setting up streams\n", retval); + goto free_i2c; + } + + /* Start Threads */ + IVTV_DEBUG_INFO("Starting Threads\n"); + + /* Decoder Thread */ + if (itv->card->v4l2_capabilities & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_init_mpeg_decoder(itv); + } + + IVTV_DEBUG_IRQ("Masking interrupts\n"); + /* clear interrupt mask, effectively disabling interrupts */ + ivtv_set_irq_mask(itv, 0xffffffff); + + /* Register IRQ */ + retval = request_irq(itv->dev->irq, ivtv_irq_handler, + SA_SHIRQ | SA_INTERRUPT, itv->name, (void *)itv); + if (retval) { + IVTV_ERR("Failed to register irq %d\n", retval); + goto free_streams; + } + + /* On a cx23416 this seems to be able to enable DMA to the chip? */ + if (!itv->has_cx23415) + write_reg_sync(0x03, IVTV_REG_DMACONTROL); + + /* Default interrupts enabled. For the PVR350 this includes the + decoder VSYNC interrupt, which is always on. It is not only used + during decoding but also by the OSD. + Some old PVR250 cards had a cx23415, so testing for that is too + general. Instead test if the card has video output capability. */ + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT | IVTV_IRQ_DEC_VSYNC); + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_INIT); + + if (itv->options.tuner > -1) { + struct tuner_setup setup; + + setup.addr = ADDR_UNSET; + setup.type = itv->options.tuner; + setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */ +#ifdef HAVE_XC3028 + setup.initmode = V4L2_TUNER_ANALOG_TV; + if (itv->options.tuner == TUNER_XCEIVE_XC3028) { + setup.gpio_write = ivtv_reset_tuner_gpio; + setup.gpio_priv = itv; + } +#endif + ivtv_call_i2c_clients(itv, TUNER_SET_TYPE_ADDR, &setup); + } + + vf.tuner = 0; + vf.type = V4L2_TUNER_ANALOG_TV; + vf.frequency = 6400; /* the tuner 'baseline' frequency */ + if (itv->std & V4L2_STD_NTSC_M) { + /* Why on earth? */ + vf.frequency = 1076; /* ch. 4 67250*16/1000 */ + } + + /* The tuner is fixed to the standard. The other inputs (e.g. S-Video) + are not. */ + itv->tuner_std = itv->std; + + video_input = itv->active_input; + itv->active_input++; /* Force update of input */ + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_INPUT, &video_input); + + /* Let the VIDIOC_S_STD ioctl do all the work, keeps the code + in one place. */ + itv->std++; /* Force full standard initialization */ + itv->std_out = itv->std; + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_STD, &itv->tuner_std); + ivtv_v4l2_ioctls(itv, NULL, VIDIOC_S_FREQUENCY, &vf); + if (itv->has_cx23415) + ivtv_set_osd_alpha(itv); + + IVTV_INFO("Initialized %s, card #%d\n", itv->card_name, itv->num); + + return 0; + + free_irq: + free_irq(itv->dev->irq, (void *)itv); + free_streams: + ivtv_streams_cleanup(itv); + free_i2c: + exit_ivtv_i2c(itv); + free_io: + ivtv_iounmap(itv); + free_mem: + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + free_workqueue: + destroy_workqueue(itv->vbi.work_queues); + destroy_workqueue(itv->yuv_info.work_queues); + err: + if (retval == 0) + retval = -ENODEV; + IVTV_ERR("Error %d on initialization\n", retval); + + kfree(ivtv_cards[ivtv_cards_active]); + ivtv_cards[ivtv_cards_active] = NULL; + return retval; +} + +static void ivtv_remove(struct pci_dev *pci_dev) +{ + struct ivtv *itv = pci_get_drvdata(pci_dev); + + IVTV_DEBUG_INFO("Removing Card #%d.\n", itv->num); + + /* Stop all captures */ + IVTV_DEBUG_INFO(" Stopping all streams.\n"); + if (atomic_read(&itv->capturing) > 0) + ivtv_stop_all_captures(itv); + + /* Stop all decoding */ + IVTV_DEBUG_INFO(" Stopping decoding.\n"); + if (atomic_read(&itv->decoding) > 0) { + int type; + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) + type = IVTV_DEC_STREAM_TYPE_YUV; + else + type = IVTV_DEC_STREAM_TYPE_MPG; + ivtv_stop_v4l2_decode_stream(&itv->streams[type], + VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } + + /* Interrupts */ + IVTV_DEBUG_INFO(" Disabling interrupts.\n"); + ivtv_set_irq_mask(itv, 0xffffffff); + del_timer_sync(&itv->dma_timer); + + /* Stop all Work Queues */ + IVTV_DEBUG_INFO(" Stop Work Queues.\n"); + flush_workqueue(itv->vbi.work_queues); + flush_workqueue(itv->yuv_info.work_queues); + destroy_workqueue(itv->vbi.work_queues); + destroy_workqueue(itv->yuv_info.work_queues); + + IVTV_DEBUG_INFO(" Stopping Firmware.\n"); + ivtv_halt_firmware(itv); + + IVTV_DEBUG_INFO(" Unregistering v4l devices.\n"); + ivtv_streams_cleanup(itv); + IVTV_DEBUG_INFO(" Freeing dma resources.\n"); + ivtv_udma_free(itv); + + exit_ivtv_i2c(itv); + + IVTV_DEBUG_INFO(" Releasing irq.\n"); + free_irq(itv->dev->irq, (void *)itv); + + if (itv->dev) { + ivtv_iounmap(itv); + } + + IVTV_DEBUG_INFO(" Releasing mem.\n"); + release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE); + release_mem_region(itv->base_addr + IVTV_REG_OFFSET, IVTV_REG_SIZE); + if (itv->has_cx23415) + release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE); + + pci_disable_device(itv->dev); + + IVTV_INFO("Removed %s, card #%d\n", itv->card_name, itv->num); +} + +/* define a pci_driver for card detection */ +static struct pci_driver ivtv_pci_driver = { + .name = "ivtv", + .id_table = ivtv_pci_tbl, + .probe = ivtv_probe, + .remove = ivtv_remove, +}; + +static int module_start(void) +{ + printk(KERN_INFO "ivtv: ==================== START INIT IVTV ====================\n"); + printk(KERN_INFO "ivtv: version %s (" VERMAGIC_STRING ") loading\n", IVTV_VERSION); + + memset(ivtv_cards, 0, sizeof(ivtv_cards)); + + /* Validate parameters */ + if (ivtv_first_minor < 0 || ivtv_first_minor >= IVTV_MAX_CARDS) { + printk(KERN_ERR "ivtv: ivtv_first_minor must be between 0 and %d. Exiting...\n", + IVTV_MAX_CARDS - 1); + return -1; + } + + if (ivtv_debug < 0 || ivtv_debug > 511) { + ivtv_debug = 0; + printk(KERN_INFO "ivtv: debug value must be >= 0 and <= 511!\n"); + } + + if (pci_module_init(&ivtv_pci_driver)) { + printk(KERN_ERR "ivtv: Error detecting PCI card\n"); + return -ENODEV; + } + printk(KERN_INFO "ivtv: ==================== END INIT IVTV ====================\n"); + return 0; +} + +static void module_cleanup(void) +{ + int i, j; + + for (i = 0; i < ivtv_cards_active; i++) { + if (ivtv_cards[i] == NULL) + continue; + for (j = 0; j < IVTV_VBI_FRAMES; j++) { + kfree(ivtv_cards[i]->vbi.sliced_mpeg_data[j]); + } + kfree(ivtv_cards[i]); + } + pci_unregister_driver(&ivtv_pci_driver); +} + +EXPORT_SYMBOL(ivtv_set_irq_mask); +EXPORT_SYMBOL(ivtv_cards_active); +EXPORT_SYMBOL(ivtv_cards); +EXPORT_SYMBOL(ivtv_api); +EXPORT_SYMBOL(ivtv_vapi); +EXPORT_SYMBOL(ivtv_vapi_result); +EXPORT_SYMBOL(ivtv_clear_irq_mask); +EXPORT_SYMBOL(ivtv_debug); +EXPORT_SYMBOL(ivtv_reset_ir_gpio); +EXPORT_SYMBOL(ivtv_udma_setup); +EXPORT_SYMBOL(ivtv_udma_unmap); +EXPORT_SYMBOL(ivtv_udma_alloc); +EXPORT_SYMBOL(ivtv_udma_prepare); + +module_init(module_start); +module_exit(module_cleanup); diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h new file mode 100644 index 000000000000..546d7bbfcf5b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -0,0 +1,866 @@ +/* + ivtv driver internal defines and structures + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef IVTV_DRIVER_H +#define IVTV_DRIVER_H + +/* Internal header for ivtv project: + * Driver for the cx23415/6 chip. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + * License: GPL + * http://www.ivtvdriver.org + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* #define HAVE_XC3028 1 */ + +#include + +#ifdef CONFIG_LIRC_I2C +# error "This driver is not compatible with the LIRC I2C kernel configuration option." +#endif /* CONFIG_LIRC_I2C */ + +#ifndef CONFIG_PCI +# error "This driver requires kernel PCI support." +#endif /* CONFIG_PCI */ + +#define IVTV_ENCODER_OFFSET 0x00000000 +#define IVTV_ENCODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_DECODER_OFFSET 0x01000000 +#define IVTV_DECODER_SIZE 0x00800000 /* Last half isn't needed 0x01000000 */ + +#define IVTV_REG_OFFSET 0x02000000 +#define IVTV_REG_SIZE 0x00010000 + +/* Buffers on hardware offsets */ +#define IVTV_YUV_BUFFER_OFFSET 0x001a8600 /* First YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_1 0x00240400 /* Second YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_2 0x002d8200 /* Third YUV Buffer */ +#define IVTV_YUV_BUFFER_OFFSET_3 0x00370000 /* Fourth YUV Buffer */ +#define IVTV_YUV_BUFFER_UV_OFFSET 0x65400 /* Offset to UV Buffer */ + +/* Offset to filter table in firmware */ +#define IVTV_YUV_HORIZONTAL_FILTER_OFFSET 0x025d8 +#define IVTV_YUV_VERTICAL_FILTER_OFFSET 0x03358 + +extern const u32 yuv_offset[4]; + +/* Maximum ivtv driver instances. + Based on 6 PVR500s each with two PVR15s... + TODO: make this dynamic. I believe it is only a global in order to support + ivtv-fb. There must be a better way to do that. */ +#define IVTV_MAX_CARDS 12 + +/* Supported cards */ +#define IVTV_CARD_PVR_250 0 /* WinTV PVR 250 */ +#define IVTV_CARD_PVR_350 1 /* encoder, decoder, tv-out */ +#define IVTV_CARD_PVR_150 2 /* WinTV PVR 150 and PVR 500 (really just two + PVR150s on one PCI board) */ +#define IVTV_CARD_M179 3 /* AVerMedia M179 (encoder only) */ +#define IVTV_CARD_MPG600 4 /* Kuroutoshikou ITVC16-STVLP/YUAN MPG600, encoder only */ +#define IVTV_CARD_MPG160 5 /* Kuroutoshikou ITVC15-STVLP/YUAN MPG160 + cx23415 based, but does not have tv-out */ +#define IVTV_CARD_PG600 6 /* YUAN PG600/DIAMONDMM PVR-550 based on the CX Falcon 2 */ +#define IVTV_CARD_AVC2410 7 /* Adaptec AVC-2410 */ +#define IVTV_CARD_AVC2010 8 /* Adaptec AVD-2010 (No Tuner) */ +#define IVTV_CARD_TG5000TV 9 /* NAGASE TRANSGEAR 5000TV, encoder only */ +#define IVTV_CARD_VA2000MAX_SNT6 10 /* VA2000MAX-STN6 */ +#define IVTV_CARD_CX23416GYC 11 /* Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_GV_MVPRX 12 /* I/O Data GV-MVP/RX, RX2, RX2W */ +#define IVTV_CARD_GV_MVPRX2E 13 /* I/O Data GV-MVP/RX2E */ +#define IVTV_CARD_GOTVIEW_PCI_DVD 14 /* GotView PCI DVD */ +#define IVTV_CARD_GOTVIEW_PCI_DVD2 15 /* GotView PCI DVD2 */ +#define IVTV_CARD_YUAN_MPC622 16 /* Yuan MPC622 miniPCI */ +#define IVTV_CARD_DCTMTVP1 17 /* DIGITAL COWBOY DCT-MTVP1 */ +#ifdef HAVE_XC3028 +#define IVTV_CARD_PG600V2 18 /* Yuan PG600V2/GotView PCI DVD Lite/Club3D ZAP-TV1x01 */ +#define IVTV_CARD_LAST 18 +#else +#define IVTV_CARD_LAST 17 +#endif + +/* Variants of existing cards but with the same PCI IDs. The driver + detects these based on other device information. + These cards must always come last. + New cards must be inserted above, and the indices of the cards below + must be adjusted accordingly. */ + +/* PVR-350 V1 (uses saa7114) */ +#define IVTV_CARD_PVR_350_V1 (IVTV_CARD_LAST+1) +/* 2 variants of Kuroutoshikou CX23416GYC-STVLP (Yuan MPG600GR OEM) */ +#define IVTV_CARD_CX23416GYC_NOGR (IVTV_CARD_LAST+2) +#define IVTV_CARD_CX23416GYC_NOGRYCS (IVTV_CARD_LAST+3) + +#define IVTV_ENC_STREAM_TYPE_MPG 0 +#define IVTV_ENC_STREAM_TYPE_YUV 1 +#define IVTV_ENC_STREAM_TYPE_VBI 2 +#define IVTV_ENC_STREAM_TYPE_PCM 3 +#define IVTV_ENC_STREAM_TYPE_RAD 4 +#define IVTV_DEC_STREAM_TYPE_MPG 5 +#define IVTV_DEC_STREAM_TYPE_VBI 6 +#define IVTV_DEC_STREAM_TYPE_VOUT 7 +#define IVTV_DEC_STREAM_TYPE_YUV 8 +#define IVTV_MAX_STREAMS 9 + +#define IVTV_V4L2_DEC_MPG_OFFSET 16 /* offset from 0 to register decoder mpg v4l2 minors on */ +#define IVTV_V4L2_ENC_PCM_OFFSET 24 /* offset from 0 to register pcm v4l2 minors on */ +#define IVTV_V4L2_ENC_YUV_OFFSET 32 /* offset from 0 to register yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_YUV_OFFSET 48 /* offset from 0 to register decoder yuv v4l2 minors on */ +#define IVTV_V4L2_DEC_VBI_OFFSET 8 /* offset from 0 to register decoder vbi input v4l2 minors on */ +#define IVTV_V4L2_DEC_VOUT_OFFSET 16 /* offset from 0 to register vbi output v4l2 minors on */ + +#define IVTV_ENC_MEM_START 0x00000000 +#define IVTV_DEC_MEM_START 0x01000000 + +/* system vendor and device IDs */ +#define PCI_VENDOR_ID_ICOMP 0x4444 +#define PCI_DEVICE_ID_IVTV15 0x0803 +#define PCI_DEVICE_ID_IVTV16 0x0016 + +/* subsystem vendor ID */ +#define IVTV_PCI_ID_HAUPPAUGE 0x0070 +#define IVTV_PCI_ID_HAUPPAUGE_ALT1 0x0270 +#define IVTV_PCI_ID_HAUPPAUGE_ALT2 0x4070 +#define IVTV_PCI_ID_ADAPTEC 0x9005 +#define IVTV_PCI_ID_AVERMEDIA 0x1461 +#define IVTV_PCI_ID_YUAN1 0x12ab +#define IVTV_PCI_ID_YUAN2 0xff01 +#define IVTV_PCI_ID_YUAN3 0xffab +#define IVTV_PCI_ID_YUAN4 0xfbab +#define IVTV_PCI_ID_DIAMONDMM 0xff92 +#define IVTV_PCI_ID_IODATA 0x10fc +#define IVTV_PCI_ID_MELCO 0x1154 +#define IVTV_PCI_ID_GOTVIEW1 0xffac +#define IVTV_PCI_ID_GOTVIEW2 0xffad + +/* Decoder Buffer hardware size on Chip */ +#define IVTV_DEC_MAX_BUF 0x00100000 /* max bytes in decoder buffer */ +#define IVTV_DEC_MIN_BUF 0x00010000 /* min bytes in dec buffer */ + +/* ======================================================================== */ +/* ========================== START USER SETTABLE DMA VARIABLES =========== */ +/* ======================================================================== */ + +#define IVTV_DMA_SG_OSD_ENT (2883584/PAGE_SIZE) /* sg entities */ + +/* DMA Buffers, Default size in MB allocated */ +#define IVTV_DEFAULT_ENC_MPG_BUFFERS 4 +#define IVTV_DEFAULT_ENC_YUV_BUFFERS 2 +#define IVTV_DEFAULT_ENC_VBI_BUFFERS 1 +#define IVTV_DEFAULT_ENC_PCM_BUFFERS 1 +#define IVTV_DEFAULT_DEC_MPG_BUFFERS 1 +#define IVTV_DEFAULT_DEC_YUV_BUFFERS 1 +#define IVTV_DEFAULT_DEC_VBI_BUFFERS 1 + +/* ======================================================================== */ +/* ========================== END USER SETTABLE DMA VARIABLES ============= */ +/* ======================================================================== */ + +/* Decoder Status Register */ +#define IVTV_DMA_ERR_LIST 0x00000010 +#define IVTV_DMA_ERR_WRITE 0x00000008 +#define IVTV_DMA_ERR_READ 0x00000004 +#define IVTV_DMA_SUCCESS_WRITE 0x00000002 +#define IVTV_DMA_SUCCESS_READ 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) + +/* DMA Registers */ +#define IVTV_REG_DMAXFER (0x0000) +#define IVTV_REG_DMASTATUS (0x0004) +#define IVTV_REG_DECDMAADDR (0x0008) +#define IVTV_REG_ENCDMAADDR (0x000c) +#define IVTV_REG_DMACONTROL (0x0010) +#define IVTV_REG_IRQSTATUS (0x0040) +#define IVTV_REG_IRQMASK (0x0048) + +/* Setup Registers */ +#define IVTV_REG_ENC_SDRAM_REFRESH (0x07F8) +#define IVTV_REG_ENC_SDRAM_PRECHARGE (0x07FC) +#define IVTV_REG_DEC_SDRAM_REFRESH (0x08F8) +#define IVTV_REG_DEC_SDRAM_PRECHARGE (0x08FC) +#define IVTV_REG_VDM (0x2800) +#define IVTV_REG_AO (0x2D00) +#define IVTV_REG_BYTEFLUSH (0x2D24) +#define IVTV_REG_SPU (0x9050) +#define IVTV_REG_HW_BLOCKS (0x9054) +#define IVTV_REG_VPU (0x9058) +#define IVTV_REG_APU (0xA064) + +#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_AUD_MODE_CHG (0x1 << 24) +#define IVTV_IRQ_DEC_DATA_REQ (0x1 << 22) +#define IVTV_IRQ_DEC_DMA_COMPLETE (0x1 << 20) +#define IVTV_IRQ_DEC_VBI_RE_INSERT (0x1 << 19) +#define IVTV_IRQ_DMA_ERR (0x1 << 18) +#define IVTV_IRQ_DMA_WRITE (0x1 << 17) +#define IVTV_IRQ_DMA_READ (0x1 << 16) +#define IVTV_IRQ_DEC_VSYNC (0x1 << 10) + +/* IRQ Masks */ +#define IVTV_IRQ_MASK_INIT (IVTV_IRQ_DMA_ERR|IVTV_IRQ_ENC_DMA_COMPLETE|IVTV_IRQ_DMA_READ) + +#define IVTV_IRQ_MASK_CAPTURE (IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_EOS) +#define IVTV_IRQ_MASK_DECODE (IVTV_IRQ_DEC_DATA_REQ|IVTV_IRQ_DEC_AUD_MODE_CHG) + +/* i2c stuff */ +#define I2C_CLIENTS_MAX 16 + +/* debugging */ + +#define IVTV_DBGFLG_WARN (1 << 0) +#define IVTV_DBGFLG_INFO (1 << 1) +#define IVTV_DBGFLG_API (1 << 2) +#define IVTV_DBGFLG_DMA (1 << 3) +#define IVTV_DBGFLG_IOCTL (1 << 4) +#define IVTV_DBGFLG_I2C (1 << 5) +#define IVTV_DBGFLG_IRQ (1 << 6) +#define IVTV_DBGFLG_DEC (1 << 7) +#define IVTV_DBGFLG_YUV (1 << 8) + +/* NOTE: extra space before comma in 'itv->num , ## args' is required for + gcc-2.95, otherwise it won't compile. */ +#define IVTV_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info",fmt , ## args) +#define IVTV_DEBUG_API(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_DEBUG_DMA(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_DEBUG_IOCTL(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_DEBUG_I2C(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_DEBUG_IRQ(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_DEBUG_DEC(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_DEBUG_YUV(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +#define IVTV_FB_DEBUG(x, type, fmt, args...) \ + do { \ + if ((x) & ivtv_debug) \ + printk(KERN_INFO "ivtv%d-fb " type ": " fmt, itv->num , ## args); \ + } while (0) +#define IVTV_FB_DEBUG_WARN(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_WARN, "warning", fmt , ## args) +#define IVTV_FB_DEBUG_INFO(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args) +#define IVTV_FB_DEBUG_API(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_API, "api", fmt , ## args) +#define IVTV_FB_DEBUG_DMA(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DMA, "dma", fmt , ## args) +#define IVTV_FB_DEBUG_IOCTL(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IOCTL, "ioctl", fmt , ## args) +#define IVTV_FB_DEBUG_I2C(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_I2C, "i2c", fmt , ## args) +#define IVTV_FB_DEBUG_IRQ(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_IRQ, "irq", fmt , ## args) +#define IVTV_FB_DEBUG_DEC(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_DEC, "dec", fmt , ## args) +#define IVTV_FB_DEBUG_YUV(fmt, args...) IVTV_FB_DEBUG(IVTV_DBGFLG_YUV, "yuv", fmt , ## args) + +/* Standard kernel messages */ +#define IVTV_ERR(fmt, args...) printk(KERN_ERR "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_WARN(fmt, args...) printk(KERN_WARNING "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_INFO(fmt, args...) printk(KERN_INFO "ivtv%d: " fmt, itv->num , ## args) +#define IVTV_FB_ERR(fmt, args...) printk(KERN_ERR "ivtv%d-fb: " fmt, itv->num , ## args) +#define IVTV_FB_INFO(fmt, args...) printk(KERN_INFO "ivtv%d-fb: " fmt, itv->num , ## args) + +/* 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 + +/* output modes (cx23415 only) */ +#define OUT_NONE 0 +#define OUT_MPG 1 +#define OUT_YUV 2 +#define OUT_UDMA_YUV 3 +#define OUT_PASSTHROUGH 4 + +#define IVTV_MAX_PGM_INDEX (400) + +extern int ivtv_debug; + + +struct ivtv_options { + int megabytes[IVTV_MAX_STREAMS]; /* Size in megabytes of each stream */ + int cardtype; /* force card type on load */ + int tuner; /* set tuner on load */ + int radio; /* enable/disable radio */ + int newi2c; /* New I2C algorithm */ +}; + +#define IVTV_MBOX_DMA_START 6 +#define IVTV_MBOX_DMA_END 8 +#define IVTV_MBOX_DMA 9 +#define IVTV_MBOX_FIELD_DISPLAYED 8 + +/* ivtv-specific mailbox template */ +struct ivtv_mailbox { + u32 flags; + u32 cmd; + u32 retval; + u32 timeout; + u32 data[CX2341X_MBOX_MAX_DATA]; +}; + +struct ivtv_api_cache { + unsigned long last_jiffies; /* when last command was issued */ + u32 data[CX2341X_MBOX_MAX_DATA]; /* last sent api data */ +}; + +struct ivtv_mailbox_data { + volatile struct ivtv_mailbox __iomem *mbox; + /* Bits 0-2 are for the encoder mailboxes, 0-1 are for the decoder mailboxes. + If the bit is set, then the corresponding mailbox is in use by the driver. */ + unsigned long busy; + u8 max_mbox; +}; + +/* per-buffer bit flags */ +#define IVTV_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ + +/* per-stream, s_flags */ +#define IVTV_F_S_DMA_PENDING 0 /* this stream has pending DMA */ +#define IVTV_F_S_DMA_HAS_VBI 1 /* the current DMA request also requests VBI data */ +#define IVTV_F_S_NEEDS_DATA 2 /* this decoding stream needs more data */ + +#define IVTV_F_S_CLAIMED 3 /* this stream is claimed */ +#define IVTV_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */ +#define IVTV_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */ +#define IVTV_F_S_PASSTHROUGH 6 /* this stream is in passthrough mode */ +#define IVTV_F_S_STREAMOFF 7 /* signal end of stream EOS */ +#define IVTV_F_S_APPL_IO 8 /* this stream is used read/written by an application */ + +/* per-ivtv, i_flags */ +#define IVTV_F_I_DMA 0 /* DMA in progress */ +#define IVTV_F_I_UDMA 1 /* UDMA in progress */ +#define IVTV_F_I_UDMA_PENDING 2 /* UDMA pending */ + +#define IVTV_F_I_SPEED_CHANGE 3 /* A speed change is in progress */ +#define IVTV_F_I_EOS 4 /* End of encoder stream reached */ +#define IVTV_F_I_RADIO_USER 5 /* The radio tuner is selected */ +#define IVTV_F_I_DIG_RST 6 /* Reset digitizer */ +#define IVTV_F_I_DEC_YUV 7 /* YUV instead of MPG is being decoded */ +#define IVTV_F_I_ENC_VBI 8 /* VBI DMA */ +#define IVTV_F_I_UPDATE_CC 9 /* CC should be updated */ +#define IVTV_F_I_UPDATE_WSS 10 /* WSS should be updated */ +#define IVTV_F_I_UPDATE_VPS 11 /* VPS should be updated */ +#define IVTV_F_I_DECODING_YUV 12 /* this stream is YUV frame decoding */ +#define IVTV_F_I_ENC_PAUSED 13 /* the encoder is paused */ +#define IVTV_F_I_VALID_DEC_TIMINGS 14 /* last_dec_timing is valid */ + +/* Event notifications */ +#define IVTV_F_I_EV_DEC_STOPPED 28 /* decoder stopped event */ +#define IVTV_F_I_EV_VSYNC 29 /* VSYNC event */ +#define IVTV_F_I_EV_VSYNC_FIELD 30 /* VSYNC event field (0 = first, 1 = second field) */ +#define IVTV_F_I_EV_VSYNC_ENABLED 31 /* VSYNC event enabled */ + +/* Scatter-Gather array element, used in DMA transfers */ +struct ivtv_SG_element { + u32 src; + u32 dst; + u32 size; +}; + +struct ivtv_user_dma { + struct mutex lock; + int page_count; + struct page *map[IVTV_DMA_SG_OSD_ENT]; + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element SGarray[IVTV_DMA_SG_OSD_ENT]; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist SGlist[IVTV_DMA_SG_OSD_ENT]; +}; + +struct ivtv_dma_page_info { + unsigned long uaddr; + unsigned long first; + unsigned long last; + unsigned int offset; + unsigned int tail; + int page_count; +}; + +struct ivtv_buffer { + struct list_head list; + dma_addr_t dma_handle; + unsigned long b_flags; + char *buf; + + u32 bytesused; + u32 readpos; +}; + +struct ivtv_queue { + struct list_head list; + u32 buffers; + u32 length; + u32 bytesused; +}; + +struct ivtv; /* forward reference */ + +struct ivtv_stream { + /* These first four fields are always set, even if the stream + is not actually created. */ + struct video_device *v4l2dev; /* NULL when stream not created */ + struct ivtv *itv; /* for ease of use */ + const char *name; /* name of the stream */ + int type; /* stream type */ + + u32 id; + spinlock_t qlock; /* locks access to the queues */ + unsigned long s_flags; /* status flags, see above */ + int dma; /* can be PCI_DMA_TODEVICE, + PCI_DMA_FROMDEVICE or + PCI_DMA_NONE */ + u32 dma_offset; + u32 dma_backup; + u64 dma_pts; + + int subtype; + wait_queue_head_t waitq; + u32 dma_last_offset; + + /* Buffer Stats */ + u32 buffers; + u32 buf_size; + u32 buffers_stolen; + + /* Buffer Queues */ + struct ivtv_queue q_free; /* free buffers */ + struct ivtv_queue q_full; /* full buffers */ + struct ivtv_queue q_io; /* waiting for I/O */ + struct ivtv_queue q_dma; /* waiting for DMA */ + struct ivtv_queue q_predma; /* waiting for DMA */ + + /* Base Dev SG Array for cx23415/6 */ + struct ivtv_SG_element *SGarray; + dma_addr_t SG_handle; + int SG_length; + + /* SG List of Buffers */ + struct scatterlist *SGlist; +}; + +struct ivtv_open_id { + u32 open_id; + int type; + struct ivtv *itv; +}; + +#define IVTV_YUV_UPDATE_HORIZONTAL 0x01 +#define IVTV_YUV_UPDATE_VERTICAL 0x02 + +struct yuv_frame_info +{ + u32 update; + int src_x; + int src_y; + unsigned int src_w; + unsigned int src_h; + int dst_x; + int dst_y; + unsigned int dst_w; + unsigned int dst_h; + int pan_x; + int pan_y; + u32 vis_w; + u32 vis_h; + u32 interlaced_y; + u32 interlaced_uv; + int tru_x; + u32 tru_w; + u32 tru_h; + u32 offset_y; +}; + +#define IVTV_YUV_MODE_INTERLACED 0x00 +#define IVTV_YUV_MODE_PROGRESSIVE 0x01 +#define IVTV_YUV_MODE_AUTO 0x02 +#define IVTV_YUV_MODE_MASK 0x03 + +#define IVTV_YUV_SYNC_EVEN 0x00 +#define IVTV_YUV_SYNC_ODD 0x04 +#define IVTV_YUV_SYNC_MASK 0x04 + +struct yuv_playback_info +{ + u32 reg_2834; + u32 reg_2838; + u32 reg_283c; + u32 reg_2840; + u32 reg_2844; + u32 reg_2848; + u32 reg_2854; + u32 reg_285c; + u32 reg_2864; + + u32 reg_2870; + u32 reg_2874; + u32 reg_2890; + u32 reg_2898; + u32 reg_289c; + + u32 reg_2918; + u32 reg_291c; + u32 reg_2920; + u32 reg_2924; + u32 reg_2928; + u32 reg_292c; + u32 reg_2930; + + u32 reg_2934; + + u32 reg_2938; + u32 reg_293c; + u32 reg_2940; + u32 reg_2944; + u32 reg_2948; + u32 reg_294c; + u32 reg_2950; + u32 reg_2954; + u32 reg_2958; + u32 reg_295c; + u32 reg_2960; + u32 reg_2964; + u32 reg_2968; + u32 reg_296c; + + u32 reg_2970; + + int v_filter_1; + int v_filter_2; + int h_filter; + + u32 osd_x_offset; + u32 osd_y_offset; + + u32 osd_x_pan; + u32 osd_y_pan; + + u32 osd_vis_w; + u32 osd_vis_h; + + int decode_height; + + int frame_interlaced; + int frame_interlaced_last; + + int lace_mode; + int lace_threshold; + int lace_threshold_last; + int lace_sync_field; + + atomic_t next_dma_frame; + atomic_t next_fill_frame; + + u32 yuv_forced_update; + int update_frame; + struct workqueue_struct *work_queues; + struct work_struct work_queue; + struct yuv_frame_info new_frame_info[4]; + struct yuv_frame_info old_frame_info; + struct yuv_frame_info old_frame_info_args; + + void *blanking_ptr; + dma_addr_t blanking_dmaptr; +}; + +#define IVTV_VBI_FRAMES 32 + +/* VBI data */ +struct vbi_info { + u32 dec_start; + u32 enc_start, enc_size; + int fpi; + u32 frame; + u32 dma_offset; + u8 cc_data_odd[256]; + u8 cc_data_even[256]; + int cc_pos; + u8 cc_no_update; + u8 vps[5]; + u8 vps_found; + int wss; + u8 wss_found; + u8 wss_no_update; + u32 raw_decoder_line_size; + u8 raw_decoder_sav_odd_field; + u8 raw_decoder_sav_even_field; + u32 sliced_decoder_line_size; + u8 sliced_decoder_sav_odd_field; + u8 sliced_decoder_sav_even_field; + struct v4l2_format in; + /* convenience pointer to sliced struct in vbi_in union */ + struct v4l2_sliced_vbi_format *sliced_in; + u32 service_set_in; + u32 service_set_out; + int insert_mpeg; + + /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines. + One for /dev/vbi0 and one for /dev/vbi8 */ + struct v4l2_sliced_vbi_data sliced_data[36]; + struct v4l2_sliced_vbi_data sliced_dec_data[36]; + + /* Buffer for VBI data inserted into MPEG stream. + The first byte is a dummy byte that's never used. + The next 16 bytes contain the MPEG header for the VBI data, + the remainder is the actual VBI data. + The max size accepted by the MPEG VBI reinsertion turns out + to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes, + where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is + a single line header byte and 2 * 18 is the number of VBI lines per frame. + + However, it seems that the data must be 1K aligned, so we have to + pad the data until the 1 or 2 K boundary. + + This pointer array will allocate 2049 bytes to store each VBI frame. */ + u8 *sliced_mpeg_data[IVTV_VBI_FRAMES]; + u32 sliced_mpeg_size[IVTV_VBI_FRAMES]; + struct ivtv_buffer sliced_mpeg_buf; + u32 inserted_frame; + + struct workqueue_struct *work_queues; + struct work_struct work_queue; + u32 start[2], count; + u32 raw_size; + u32 sliced_size; +}; + +/* forward declaration of struct defined in ivtv-cards.h */ +struct ivtv_card; + +/* Struct to hold info about ivtv cards */ +struct ivtv { + int num; /* board number, -1 during init! */ + char name[8]; /* board name for printk and interrupts (e.g. 'ivtv0') */ + struct pci_dev *dev; /* PCI device */ + const struct ivtv_card *card; /* card information */ + const char *card_name; /* full name of the card */ + u8 has_cx23415; /* 1 if it is a cx23415 based card, 0 for cx23416 */ + u8 is_50hz; + u8 is_60hz; + u8 is_out_50hz; + u8 is_out_60hz; + u8 pvr150_workaround; /* 1 if the cx25840 needs to workaround a PVR150 bug */ + u8 nof_inputs; /* number of video inputs */ + u8 nof_audio_inputs; /* number of audio inputs */ + u32 v4l2_cap; /* V4L2 capabilities of card */ + u32 hw_flags; /* Hardware description of the board */ + + /* controlling Video decoder function */ + int (*video_dec_func)(struct ivtv *, unsigned int, void *); + + struct ivtv_options options; /* User options */ + int stream_buf_size[IVTV_MAX_STREAMS]; /* Stream buffer size */ + struct ivtv_stream streams[IVTV_MAX_STREAMS]; /* Stream data */ + int speed; + u8 speed_mute_audio; + unsigned long i_flags; /* global ivtv flags */ + atomic_t capturing; /* count number of active capture streams */ + atomic_t decoding; /* count number of active decoding streams */ + u32 irq_rr_idx; /* Round-robin stream index */ + int cur_dma_stream; /* index of stream doing DMA */ + u32 dma_data_req_offset; + u32 dma_data_req_size; + int output_mode; /* NONE, MPG, YUV, UDMA YUV, passthrough */ + spinlock_t lock; /* lock access to this struct */ + int search_pack_header; + + spinlock_t dma_reg_lock; /* lock access to DMA engine registers */ + + /* User based DMA for OSD */ + struct ivtv_user_dma udma; + + int open_id; /* incremented each time an open occurs, used as unique ID. + starts at 1, so 0 can be used as uninitialized value + in the stream->id. */ + + u32 base_addr; + u32 irqmask; + struct timer_list dma_timer; /* Timer used to catch unfinished DMAs */ + + struct vbi_info vbi; + + struct ivtv_mailbox_data enc_mbox; + struct ivtv_mailbox_data dec_mbox; + struct ivtv_api_cache api_cache[256]; /* Cached API Commands */ + + u8 card_rev; + volatile void __iomem *enc_mem, *dec_mem, *reg_mem; + + u32 pgm_info_offset; + u32 pgm_info_num; + u32 pgm_info_write_idx; + u32 pgm_info_read_idx; + struct v4l2_enc_idx_entry pgm_info[IVTV_MAX_PGM_INDEX]; + + u64 mpg_data_received; + u64 vbi_data_inserted; + + wait_queue_head_t cap_w; + /* when the next decoder event arrives this queue is woken up */ + wait_queue_head_t event_waitq; + /* when the next decoder vsync arrives this queue is woken up */ + wait_queue_head_t vsync_waitq; + /* when the current DMA is finished this queue is woken up */ + wait_queue_head_t dma_waitq; + + /* OSD support */ + unsigned long osd_video_pbase; + int osd_global_alpha_state; /* 0=off : 1=on */ + int osd_local_alpha_state; /* 0=off : 1=on */ + int osd_color_key_state; /* 0=off : 1=on */ + u8 osd_global_alpha; /* Current global alpha */ + u32 osd_color_key; /* Current color key */ + u32 osd_pixelformat; /* Current pixel format */ + struct v4l2_rect osd_rect; /* Current OSD position and size */ + struct v4l2_rect main_rect; /* Current Main window position and size */ + + u32 last_dec_timing[3]; /* Store last retrieved pts/scr/frame values */ + + /* i2c */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + struct mutex i2c_bus_lock; + int i2c_state; + struct i2c_client *i2c_clients[I2C_CLIENTS_MAX]; + + /* v4l2 and User settings */ + + /* codec settings */ + struct cx2341x_mpeg_params params; + u32 audio_input; + u32 active_input; + u32 active_output; + v4l2_std_id std; + v4l2_std_id std_out; + v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */ + u8 audio_stereo_mode; + u8 audio_bilingual_mode; + + /* dualwatch */ + unsigned long dualwatch_jiffies; + u16 dualwatch_stereo_mode; + + /* Digitizer type */ + int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */ + + u32 lastVsyncFrame; + + struct yuv_playback_info yuv_info; + struct osd_info *osd_info; +}; + +/* Globals */ +extern struct ivtv *ivtv_cards[]; +extern int ivtv_cards_active; +extern int ivtv_first_minor; +extern spinlock_t ivtv_cards_lock; + +/*==============Prototypes==================*/ + +/* Hardware/IRQ */ +void ivtv_set_irq_mask(struct ivtv *itv, u32 mask); +void ivtv_clear_irq_mask(struct ivtv *itv, u32 mask); + +/* try to set output mode, return current mode. */ +int ivtv_set_output_mode(struct ivtv *itv, int mode); + +/* return current output stream based on current mode */ +struct ivtv_stream *ivtv_get_output_stream(struct ivtv *itv); + +/* Return non-zero if a signal is pending */ +int ivtv_sleep_timeout(int timeout, int intr); + +/* Wait on queue, returns -EINTR if interrupted */ +int ivtv_waitq(wait_queue_head_t *waitq); + +/* Read Hauppauge eeprom */ +struct tveeprom; /* forward reference */ +void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv); + +/* This is a PCI post thing, where if the pci register is not read, then + the write doesn't always take effect right away. By reading back the + register any pending PCI writes will be performed (in order), and so + you can be sure that the writes are guaranteed to be done. + + Rarely needed, only in some timing sensitive cases. + Apparently if this is not done some motherboards seem + to kill the firmware and get into the broken state until computer is + rebooted. */ +#define write_sync(val, reg) \ + do { writel(val, reg); readl(reg); } while (0) + +#define read_reg(reg) readl(itv->reg_mem + (reg)) +#define write_reg(val, reg) writel(val, itv->reg_mem + (reg)) +#define write_reg_sync(val, reg) \ + do { write_reg(val, reg); read_reg(reg); } while (0) + +#define read_enc(addr) readl(itv->enc_mem + (u32)(addr)) +#define write_enc(val, addr) writel(val, itv->enc_mem + (u32)(addr)) +#define write_enc_sync(val, addr) \ + do { write_enc(val, addr); read_enc(addr); } while (0) + +#define read_dec(addr) readl(itv->dec_mem + (u32)(addr)) +#define write_dec(val, addr) writel(val, itv->dec_mem + (u32)(addr)) +#define write_dec_sync(val, addr) \ + do { write_dec(val, addr); read_dec(addr); } while (0) + +#endif /* IVTV_DRIVER_H */ diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c new file mode 100644 index 000000000000..90e0f51e635c --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -0,0 +1,918 @@ +/* + file operation functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-vbi.h" +#include "ivtv-mailbox.h" +#include "ivtv-audio.h" +#include "ivtv-streams.h" +#include "ivtv-yuv.h" +#include "ivtv-controls.h" +#include "ivtv-ioctl.h" + +/* This function tries to claim the stream for a specific file descriptor. + If no one else is using this stream then the stream is claimed and + associated VBI streams are also automatically claimed. + Possible error returns: -EBUSY if someone else has claimed + the stream or 0 on success. */ +int ivtv_claim_stream(struct ivtv_open_id *id, int type) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[type]; + struct ivtv_stream *s_vbi; + int vbi_type; + + if (test_and_set_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + /* someone already claimed this stream */ + if (s->id == id->open_id) { + /* yes, this file descriptor did. So that's OK. */ + return 0; + } + if (s->id == -1 && (type == IVTV_DEC_STREAM_TYPE_VBI || + type == IVTV_ENC_STREAM_TYPE_VBI)) { + /* VBI is handled already internally, now also assign + the file descriptor to this stream for external + reading of the stream. */ + s->id = id->open_id; + IVTV_DEBUG_INFO("Start Read VBI\n"); + return 0; + } + /* someone else is using this stream already */ + IVTV_DEBUG_INFO("Stream %d is busy\n", type); + return -EBUSY; + } + s->id = id->open_id; + if (type == IVTV_DEC_STREAM_TYPE_VBI) { + /* Enable reinsertion interrupt */ + ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + } + + /* IVTV_DEC_STREAM_TYPE_MPG needs to claim IVTV_DEC_STREAM_TYPE_VBI, + IVTV_ENC_STREAM_TYPE_MPG needs to claim IVTV_ENC_STREAM_TYPE_VBI + (provided VBI insertion is on and sliced VBI is selected), for all + other streams we're done */ + if (type == IVTV_DEC_STREAM_TYPE_MPG) { + vbi_type = IVTV_DEC_STREAM_TYPE_VBI; + } else if (type == IVTV_ENC_STREAM_TYPE_MPG && + itv->vbi.insert_mpeg && itv->vbi.sliced_in->service_set) { + vbi_type = IVTV_ENC_STREAM_TYPE_VBI; + } else { + return 0; + } + s_vbi = &itv->streams[vbi_type]; + + if (!test_and_set_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags)) { + /* Enable reinsertion interrupt */ + if (vbi_type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_clear_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + } + /* mark that it is used internally */ + set_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags); + return 0; +} + +/* This function releases a previously claimed stream. It will take into + account associated VBI streams. */ +void ivtv_release_stream(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi; + + s->id = -1; + if ((s->type == IVTV_DEC_STREAM_TYPE_VBI || s->type == IVTV_ENC_STREAM_TYPE_VBI) && + test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { + /* this stream is still in use internally */ + return; + } + if (!test_and_clear_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + IVTV_DEBUG_WARN("Release stream %s not in use!\n", s->name); + return; + } + + ivtv_flush_queues(s); + + /* disable reinsertion interrupt */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + + /* IVTV_DEC_STREAM_TYPE_MPG needs to release IVTV_DEC_STREAM_TYPE_VBI, + IVTV_ENC_STREAM_TYPE_MPG needs to release IVTV_ENC_STREAM_TYPE_VBI, + for all other streams we're done */ + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + s_vbi = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; + else if (s->type == IVTV_ENC_STREAM_TYPE_MPG) + s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + else + return; + + /* clear internal use flag */ + if (!test_and_clear_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags)) { + /* was already cleared */ + return; + } + if (s_vbi->id != -1) { + /* VBI stream still claimed by a file descriptor */ + return; + } + /* disable reinsertion interrupt */ + if (s_vbi->type == IVTV_DEC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_DEC_VBI_RE_INSERT); + clear_bit(IVTV_F_S_CLAIMED, &s_vbi->s_flags); + ivtv_flush_queues(s_vbi); +} + +static void ivtv_dualwatch(struct ivtv *itv) +{ + struct v4l2_tuner vt; + u16 new_bitmap; + u16 new_stereo_mode; + const u16 stereo_mask = 0x0300; + const u16 dual = 0x0200; + + new_stereo_mode = itv->params.audio_properties & stereo_mask; + memset(&vt, 0, sizeof(vt)); + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, &vt); + if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 && (vt.rxsubchans & V4L2_TUNER_SUB_LANG2)) + new_stereo_mode = dual; + + if (new_stereo_mode == itv->dualwatch_stereo_mode) + return; + + new_bitmap = new_stereo_mode | (itv->params.audio_properties & ~stereo_mask); + + IVTV_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n", + itv->dualwatch_stereo_mode, new_stereo_mode, new_bitmap); + + if (ivtv_vapi(itv, CX2341X_ENC_SET_AUDIO_PROPERTIES, 1, new_bitmap) == 0) { + itv->dualwatch_stereo_mode = new_stereo_mode; + return; + } + IVTV_DEBUG_INFO("dualwatch: changing stereo flag failed\n"); +} + +static void ivtv_update_pgm_info(struct ivtv *itv) +{ + u32 wr_idx = (read_enc(itv->pgm_info_offset) - itv->pgm_info_offset - 4) / 24; + int cnt; + int i = 0; + + if (wr_idx >= itv->pgm_info_num) { + IVTV_DEBUG_WARN("Invalid PGM index %d (>= %d)\n", wr_idx, itv->pgm_info_num); + return; + } + cnt = (wr_idx + itv->pgm_info_num - itv->pgm_info_write_idx) % itv->pgm_info_num; + while (i < cnt) { + int idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; + struct v4l2_enc_idx_entry *e = itv->pgm_info + idx; + u32 addr = itv->pgm_info_offset + 4 + idx * 24; + const int mapping[] = { V4L2_ENC_IDX_FRAME_P, V4L2_ENC_IDX_FRAME_I, V4L2_ENC_IDX_FRAME_B, 0 }; + + e->offset = read_enc(addr + 4) + ((u64)read_enc(addr + 8) << 32); + if (e->offset > itv->mpg_data_received) { + break; + } + e->offset += itv->vbi_data_inserted; + e->length = read_enc(addr); + e->pts = read_enc(addr + 16) + ((u64)(read_enc(addr + 20) & 1) << 32); + e->flags = mapping[read_enc(addr + 12) & 3]; + i++; + } + itv->pgm_info_write_idx = (itv->pgm_info_write_idx + i) % itv->pgm_info_num; +} + +static struct ivtv_buffer *ivtv_get_buffer(struct ivtv_stream *s, int non_block, int *err) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + struct ivtv_buffer *buf; + DEFINE_WAIT(wait); + + *err = 0; + while (1) { + if (s->type == IVTV_ENC_STREAM_TYPE_MPG) { + /* Process pending program info updates and pending VBI data */ + ivtv_update_pgm_info(itv); + + if (jiffies - itv->dualwatch_jiffies > HZ) { + itv->dualwatch_jiffies = jiffies; + ivtv_dualwatch(itv); + } + + if (test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { + while ((buf = ivtv_dequeue(s_vbi, &s_vbi->q_full))) { + /* byteswap and process VBI data */ + ivtv_process_vbi_data(itv, buf, s_vbi->dma_pts, s_vbi->type); + ivtv_enqueue(s_vbi, buf, &s_vbi->q_free); + } + } + buf = &itv->vbi.sliced_mpeg_buf; + if (buf->readpos != buf->bytesused) { + return buf; + } + } + + /* do we have leftover data? */ + buf = ivtv_dequeue(s, &s->q_io); + if (buf) + return buf; + + /* do we have new data? */ + buf = ivtv_dequeue(s, &s->q_full); + if (buf) { + if (!test_and_clear_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags)) + return buf; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG) + /* byteswap MPG data */ + ivtv_buf_swap(buf); + else if (s->type != IVTV_DEC_STREAM_TYPE_VBI) { + /* byteswap and process VBI data */ + ivtv_process_vbi_data(itv, buf, s->dma_pts, s->type); + } + return buf; + } + /* return if file was opened with O_NONBLOCK */ + if (non_block) { + *err = -EAGAIN; + return NULL; + } + + /* return if end of stream */ + if (s->type != IVTV_DEC_STREAM_TYPE_VBI && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + IVTV_DEBUG_INFO("EOS %s\n", s->name); + return NULL; + } + + /* wait for more data to arrive */ + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become available before we were added to the waitqueue */ + if (!s->q_full.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + IVTV_DEBUG_INFO("User stopped %s\n", s->name); + *err = -EINTR; + return NULL; + } + } +} + +static void ivtv_setup_sliced_vbi_buf(struct ivtv *itv) +{ + int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; + + itv->vbi.sliced_mpeg_buf.buf = itv->vbi.sliced_mpeg_data[idx]; + itv->vbi.sliced_mpeg_buf.bytesused = itv->vbi.sliced_mpeg_size[idx]; + itv->vbi.sliced_mpeg_buf.readpos = 0; +} + +static size_t ivtv_copy_buf_to_user(struct ivtv_stream *s, struct ivtv_buffer *buf, + char __user *ubuf, size_t ucount) +{ + struct ivtv *itv = s->itv; + size_t len = buf->bytesused - buf->readpos; + + if (len > ucount) len = ucount; + if (itv->vbi.insert_mpeg && s->type == IVTV_ENC_STREAM_TYPE_MPG && + itv->vbi.sliced_in->service_set && buf != &itv->vbi.sliced_mpeg_buf) { + const char *start = buf->buf + buf->readpos; + const char *p = start + 1; + const u8 *q; + u8 ch = itv->search_pack_header ? 0xba : 0xe0; + int stuffing, i; + + while (start + len > p && (q = memchr(p, 0, start + len - p))) { + p = q + 1; + if ((char *)q + 15 >= buf->buf + buf->bytesused || + q[1] != 0 || q[2] != 1 || q[3] != ch) { + continue; + } + if (!itv->search_pack_header) { + if ((q[6] & 0xc0) != 0x80) + continue; + if (((q[7] & 0xc0) == 0x80 && (q[9] & 0xf0) == 0x20) || + ((q[7] & 0xc0) == 0xc0 && (q[9] & 0xf0) == 0x30)) { + ch = 0xba; + itv->search_pack_header = 1; + p = q + 9; + } + continue; + } + stuffing = q[13] & 7; + /* all stuffing bytes must be 0xff */ + for (i = 0; i < stuffing; i++) + if (q[14 + i] != 0xff) + break; + if (i == stuffing && (q[4] & 0xc4) == 0x44 && (q[12] & 3) == 3 && + q[14 + stuffing] == 0 && q[15 + stuffing] == 0 && + q[16 + stuffing] == 1) { + itv->search_pack_header = 0; + len = (char *)q - start; + ivtv_setup_sliced_vbi_buf(itv); + break; + } + } + } + if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) { + IVTV_DEBUG_WARN("copy %zd bytes to user failed for %s\n", len, s->name); + return -EFAULT; + } + /*IVTV_INFO("copied %lld %d %d %d %d %d vbi %d\n", itv->mpg_data_received, len, ucount, + buf->readpos, buf->bytesused, buf->bytesused - buf->readpos - len, + buf == &itv->vbi.sliced_mpeg_buf); */ + buf->readpos += len; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && buf != &itv->vbi.sliced_mpeg_buf) + itv->mpg_data_received += len; + return len; +} + +static ssize_t ivtv_read(struct ivtv_stream *s, char __user *ubuf, size_t tot_count, int non_block) +{ + struct ivtv *itv = s->itv; + size_t tot_written = 0; + int single_frame = 0; + + if (atomic_read(&itv->capturing) == 0 && s->id == -1) { + /* shouldn't happen */ + IVTV_DEBUG_WARN("Stream %s not initialized before read\n", s->name); + return -EIO; + } + + /* Each VBI buffer is one frame, the v4l2 API says that for VBI the frames should + arrive one-by-one, so make sure we never output more than one VBI frame at a time */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI || + (s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set)) + single_frame = 1; + + for (;;) { + struct ivtv_buffer *buf; + int rc; + + buf = ivtv_get_buffer(s, non_block, &rc); + if (buf == NULL && rc == -EAGAIN && tot_written) + break; + if (buf == NULL) + return rc; + rc = ivtv_copy_buf_to_user(s, buf, ubuf + tot_written, tot_count - tot_written); + if (buf != &itv->vbi.sliced_mpeg_buf) { + ivtv_enqueue(s, buf, (buf->readpos == buf->bytesused) ? &s->q_free : &s->q_io); + } + else if (buf->readpos == buf->bytesused) { + int idx = itv->vbi.inserted_frame % IVTV_VBI_FRAMES; + itv->vbi.sliced_mpeg_size[idx] = 0; + itv->vbi.inserted_frame++; + itv->vbi_data_inserted += buf->bytesused; + } + if (rc < 0) + return rc; + tot_written += rc; + + if (tot_written == tot_count || single_frame) + break; + } + return tot_written; +} + +static ssize_t ivtv_read_pos(struct ivtv_stream *s, char __user *ubuf, size_t count, + loff_t *pos, int non_block) +{ + ssize_t rc = count ? ivtv_read(s, ubuf, count, non_block) : 0; + struct ivtv *itv = s->itv; + + IVTV_DEBUG_INFO("read %zd from %s, got %zd\n", count, s->name, rc); + if (rc > 0) + pos += rc; + return rc; +} + +int ivtv_start_capture(struct ivtv_open_id *id) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + struct ivtv_stream *s_vbi; + + if (s->type == IVTV_ENC_STREAM_TYPE_RAD || + s->type == IVTV_DEC_STREAM_TYPE_MPG || + s->type == IVTV_DEC_STREAM_TYPE_YUV || + s->type == IVTV_DEC_STREAM_TYPE_VOUT) { + /* you cannot read from these stream types. */ + return -EPERM; + } + + /* Try to claim this stream. */ + if (ivtv_claim_stream(id, s->type)) + return -EBUSY; + + /* This stream does not need to start capturing */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* If capture is already in progress, then we also have to + do nothing extra. */ + if (test_bit(IVTV_F_S_STREAMOFF, &s->s_flags) || test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return 0; + } + + /* Start VBI capture if required */ + s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_INTERNAL_USE, &s_vbi->s_flags) && + !test_and_set_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { + /* Note: the IVTV_ENC_STREAM_TYPE_VBI is claimed + automatically when the MPG stream is claimed. + We only need to start the VBI capturing. */ + if (ivtv_start_v4l2_encode_stream(s_vbi)) { + IVTV_DEBUG_WARN("VBI capture start failed\n"); + + /* Failure, clean up and return an error */ + clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + /* also releases the associated VBI stream */ + ivtv_release_stream(s); + return -EIO; + } + IVTV_DEBUG_INFO("VBI insertion started\n"); + } + + /* Tell the card to start capturing */ + if (!ivtv_start_v4l2_encode_stream(s)) { + /* We're done */ + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + /* Resume a possibly paused encoder */ + if (test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + return 0; + } + + /* failure, clean up */ + IVTV_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name); + + /* Note: the IVTV_ENC_STREAM_TYPE_VBI is released + automatically when the MPG stream is released. + We only need to stop the VBI capturing. */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags)) { + ivtv_stop_v4l2_encode_stream(s_vbi, 0); + clear_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags); + } + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + ivtv_release_stream(s); + return -EIO; +} + +ssize_t ivtv_v4l2_read(struct file * filp, char __user *buf, size_t count, loff_t * pos) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int rc; + + IVTV_DEBUG_IOCTL("read %zd bytes from %s\n", count, s->name); + + rc = ivtv_start_capture(id); + if (rc) + return rc; + return ivtv_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK); +} + +int ivtv_start_decoding(struct ivtv_open_id *id, int speed) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + if (atomic_read(&itv->decoding) == 0) { + if (ivtv_claim_stream(id, s->type)) { + /* someone else is using this stream already */ + IVTV_DEBUG_WARN("start decode, stream already claimed\n"); + return -EBUSY; + } + ivtv_start_v4l2_decode_stream(s, 0); + } + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + return ivtv_set_speed(itv, speed); + return 0; +} + +ssize_t ivtv_v4l2_write(struct file *filp, const char __user *user_buf, size_t count, loff_t *pos) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + struct ivtv_buffer *buf; + struct ivtv_queue q; + int bytes_written = 0; + int mode; + int rc; + DEFINE_WAIT(wait); + + IVTV_DEBUG_IOCTL("write %zd bytes to %s\n", count, s->name); + + if (s->type != IVTV_DEC_STREAM_TYPE_MPG && + s->type != IVTV_DEC_STREAM_TYPE_YUV && + s->type != IVTV_DEC_STREAM_TYPE_VOUT) + /* not decoder streams */ + return -EPERM; + + /* Try to claim this stream */ + if (ivtv_claim_stream(id, s->type)) + return -EBUSY; + + /* This stream does not need to start any decoding */ + if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) { + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return ivtv_write_vbi(itv, user_buf, count); + } + + mode = s->type == IVTV_DEC_STREAM_TYPE_MPG ? OUT_MPG : OUT_YUV; + + if (ivtv_set_output_mode(itv, mode) != mode) { + ivtv_release_stream(s); + return -EBUSY; + } + ivtv_queue_init(&q); + set_bit(IVTV_F_S_APPL_IO, &s->s_flags); + +retry: + for (;;) { + /* Gather buffers */ + while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_io))) + ivtv_enqueue(s, buf, &q); + while (q.length - q.bytesused < count && (buf = ivtv_dequeue(s, &s->q_free))) { + ivtv_enqueue(s, buf, &q); + } + if (q.buffers) + break; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); + /* New buffers might have become free before we were added to the waitqueue */ + if (!s->q_free.buffers) + schedule(); + finish_wait(&s->waitq, &wait); + if (signal_pending(current)) { + IVTV_DEBUG_INFO("User stopped %s\n", s->name); + return -EINTR; + } + } + + /* copy user data into buffers */ + while ((buf = ivtv_dequeue(s, &q))) { + /* Make sure we really got all the user data */ + rc = ivtv_buf_copy_from_user(s, buf, user_buf, count); + + if (rc < 0) { + ivtv_queue_move(s, &q, NULL, &s->q_free, 0); + return rc; + } + user_buf += rc; + count -= rc; + bytes_written += rc; + + if (buf->bytesused != s->buf_size) { + /* incomplete, leave in q_io for next time */ + ivtv_enqueue(s, buf, &s->q_io); + break; + } + /* Byteswap MPEG buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_MPG) + ivtv_buf_swap(buf); + ivtv_enqueue(s, buf, &s->q_full); + } + + /* Start decoder (returns 0 if already started) */ + rc = ivtv_start_decoding(id, itv->speed); + if (rc) { + IVTV_DEBUG_WARN("Failed start decode stream %s\n", s->name); + + /* failure, clean up */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + return rc; + } + if (test_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags)) { + if (s->q_full.length >= itv->dma_data_req_size) { + int got_sig; + + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (!(got_sig = signal_pending(current)) && + test_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) { + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + if (got_sig) { + IVTV_DEBUG_INFO("User interrupted %s\n", s->name); + return -EINTR; + } + + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); + ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 1); + } + } + /* more user data is available, wait until buffers become free + to transfer the rest. */ + if (count && !(filp->f_flags & O_NONBLOCK)) + goto retry; + IVTV_DEBUG_INFO("Wrote %d bytes to %s (%d)\n", bytes_written, s->name, s->q_full.bytesused); + return bytes_written; +} + +unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int res = 0; + + /* add stream's waitq to the poll list */ + poll_wait(filp, &s->waitq, wait); + + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + if (test_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags) || + test_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + res = POLLPRI; + + /* Allow write if buffers are available for writing */ + if (s->q_free.buffers) + res |= POLLOUT | POLLWRNORM; + return res; +} + +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + int eof = test_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + /* Start a capture if there is none */ + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + int rc = ivtv_start_capture(id); + + if (rc) { + IVTV_DEBUG_INFO("Could not start capture for %s (%d)\n", + s->name, rc); + return POLLERR; + } + } + + /* add stream's waitq to the poll list */ + poll_wait(filp, &s->waitq, wait); + + if (eof || s->q_full.length) + return POLLIN | POLLRDNORM; + return 0; +} + +void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* 'Unclaim' this stream */ + + /* Stop capturing */ + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + + IVTV_DEBUG_INFO("close stopping capture\n"); + /* Special case: a running VBI capture for VBI insertion + in the mpeg stream. Need to stop that too. */ + if (id->type == IVTV_ENC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_STREAMING, &s_vbi->s_flags) && + !test_bit(IVTV_F_S_APPL_IO, &s_vbi->s_flags)) { + IVTV_DEBUG_INFO("close stopping embedded VBI capture\n"); + ivtv_stop_v4l2_encode_stream(s_vbi, 0); + } + if ((id->type == IVTV_DEC_STREAM_TYPE_VBI || + id->type == IVTV_ENC_STREAM_TYPE_VBI) && + test_bit(IVTV_F_S_INTERNAL_USE, &s->s_flags)) { + /* Also used internally, don't stop capturing */ + s->id = -1; + } + else { + ivtv_stop_v4l2_encode_stream(s, gop_end); + } + } + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + ivtv_release_stream(s); +} + +void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts) +{ + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* Stop decoding */ + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + IVTV_DEBUG_INFO("close stopping decode\n"); + + ivtv_stop_v4l2_decode_stream(s, flags, pts); + } + clear_bit(IVTV_F_S_APPL_IO, &s->s_flags); + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + if (id->type == IVTV_DEC_STREAM_TYPE_YUV && test_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags)) { + /* Restore registers we've changed & clean up any mess we've made */ + ivtv_yuv_close(itv); + } + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && itv->output_mode == OUT_YUV) + itv->output_mode = OUT_NONE; + else if (s->type == IVTV_DEC_STREAM_TYPE_MPG && itv->output_mode == OUT_MPG) + itv->output_mode = OUT_NONE; + + itv->speed = 0; + ivtv_release_stream(s); +} + +int ivtv_v4l2_close(struct inode *inode, struct file *filp) +{ + struct ivtv_open_id *id = filp->private_data; + struct ivtv *itv = id->itv; + struct ivtv_stream *s = &itv->streams[id->type]; + + IVTV_DEBUG_IOCTL("close() of %s\n", s->name); + + /* Easy case first: this stream was never claimed by us */ + if (s->id != id->open_id) { + kfree(id); + return 0; + } + + /* 'Unclaim' this stream */ + + /* Stop radio */ + if (id->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Closing radio device, return to TV mode */ + ivtv_mute(itv); + /* Mark that the radio is no longer in use */ + clear_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); + /* Switch tuner to TV */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + /* Select correct audio input (i.e. TV tuner or Line in) */ + ivtv_audio_set_io(itv); + /* Done! Unmute and continue. */ + ivtv_unmute(itv); + ivtv_release_stream(s); + } else if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) { + ivtv_stop_decoding(id, VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY, 0); + } else { + ivtv_stop_capture(id, 0); + } + kfree(id); + return 0; +} + +int ivtv_v4l2_open(struct inode *inode, struct file *filp) +{ + int x, y = 0; + struct ivtv_open_id *item; + struct ivtv *itv = NULL; + struct ivtv_stream *s = NULL; + int minor = MINOR(inode->i_rdev); + + /* Find which card this open was on */ + spin_lock(&ivtv_cards_lock); + for (x = 0; itv == NULL && x < ivtv_cards_active; x++) { + /* find out which stream this open was on */ + for (y = 0; y < IVTV_MAX_STREAMS; y++) { + s = &ivtv_cards[x]->streams[y]; + if (s->v4l2dev && s->v4l2dev->minor == minor) { + itv = ivtv_cards[x]; + break; + } + } + } + spin_unlock(&ivtv_cards_lock); + + if (itv == NULL) { + /* Couldn't find a device registered + on that minor, shouldn't happen! */ + printk(KERN_WARNING "ivtv: no ivtv device found on minor %d\n", minor); + return -ENXIO; + } + + if (y == IVTV_DEC_STREAM_TYPE_MPG && + test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_YUV].s_flags)) + return -EBUSY; + + if (y == IVTV_DEC_STREAM_TYPE_YUV && + test_bit(IVTV_F_S_CLAIMED, &itv->streams[IVTV_DEC_STREAM_TYPE_MPG].s_flags)) + return -EBUSY; + + if (y == IVTV_DEC_STREAM_TYPE_YUV) { + if (read_reg(0x82c) == 0) { + IVTV_ERR("Tried to open YUV output device but need to send data to mpeg decoder before it can be used\n"); + /* return -ENODEV; */ + } + ivtv_udma_alloc(itv); + } + + /* Allocate memory */ + item = kmalloc(sizeof(struct ivtv_open_id), GFP_KERNEL); + if (NULL == item) { + IVTV_DEBUG_WARN("nomem on v4l2 open\n"); + return -ENOMEM; + } + item->itv = itv; + item->type = y; + + item->open_id = itv->open_id++; + filp->private_data = item; + + if (item->type == IVTV_ENC_STREAM_TYPE_RAD) { + /* Try to claim this stream */ + if (ivtv_claim_stream(item, item->type)) { + /* No, it's already in use */ + kfree(item); + return -EBUSY; + } + + /* We have the radio */ + ivtv_mute(itv); + /* Switch tuner to radio */ + ivtv_call_i2c_clients(itv, AUDC_SET_RADIO, NULL); + /* Mark that the radio is being used. */ + set_bit(IVTV_F_I_RADIO_USER, &itv->i_flags); + /* Select the correct audio input (i.e. radio tuner) */ + ivtv_audio_set_io(itv); + /* Done! Unmute and continue. */ + ivtv_unmute(itv); + } + + /* YUV or MPG Decoding Mode? */ + if (y == IVTV_DEC_STREAM_TYPE_MPG) + clear_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + else if (y == IVTV_DEC_STREAM_TYPE_YUV) + { + set_bit(IVTV_F_I_DEC_YUV, &itv->i_flags); + } + + return 0; +} + +void ivtv_mute(struct ivtv *itv) +{ + struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 1 }; + + /* Mute sound to avoid pop */ + ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl); + + if (atomic_read(&itv->capturing)) + ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 1); + + IVTV_DEBUG_INFO("Mute\n"); +} + +void ivtv_unmute(struct ivtv *itv) +{ + struct v4l2_control ctrl = { V4L2_CID_AUDIO_MUTE, 0 }; + + /* initialize or refresh input */ + if (atomic_read(&itv->capturing) == 0) + ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); + + ivtv_sleep_timeout(HZ / 10, 0); + + if (atomic_read(&itv->capturing)) { + ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); + ivtv_vapi(itv, CX2341X_ENC_MUTE_AUDIO, 1, 0); + } + + /* Unmute */ + ivtv_control_ioctls(itv, VIDIOC_S_CTRL, &ctrl); + IVTV_DEBUG_INFO("Unmute\n"); +} diff --git a/drivers/media/video/ivtv/ivtv-fileops.h b/drivers/media/video/ivtv/ivtv-fileops.h new file mode 100644 index 000000000000..1afa950209b8 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-fileops.h @@ -0,0 +1,45 @@ +/* + file operation functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Testing/Debugging */ +int ivtv_v4l2_open(struct inode *inode, struct file *filp); +ssize_t ivtv_v4l2_read(struct file *filp, char __user *buf, size_t count, + loff_t * pos); +ssize_t ivtv_v4l2_write(struct file *filp, const char __user *buf, size_t count, + loff_t * pos); +int ivtv_v4l2_close(struct inode *inode, struct file *filp); +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait); +unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table * wait); +int ivtv_start_capture(struct ivtv_open_id *id); +void ivtv_stop_capture(struct ivtv_open_id *id, int gop_end); +int ivtv_start_decoding(struct ivtv_open_id *id, int speed); +void ivtv_stop_decoding(struct ivtv_open_id *id, int flags, u64 pts); +void ivtv_mute(struct ivtv *itv); +void ivtv_unmute(struct ivtv *itv); + +/* Utilities */ + +/* Try to claim a stream for the filehandle. Return 0 on success, + -EBUSY if stream already claimed. Once a stream is claimed, it + remains claimed until the associated filehandle is closed. */ +int ivtv_claim_stream(struct ivtv_open_id *id, int type); + +/* Release a previously claimed stream. */ +void ivtv_release_stream(struct ivtv_stream *s); diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c new file mode 100644 index 000000000000..d4c910b782af --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-firmware.c @@ -0,0 +1,272 @@ +/* + ivtv firmware functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" +#include "ivtv-firmware.h" +#include + +#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) /* 600 ms */ + +#define IVTV_DECODE_INIT_MPEG_FILENAME "v4l-cx2341x-init.mpg" +#define IVTV_DECODE_INIT_MPEG_SIZE (152*1024) + +/* Encoder/decoder firmware sizes */ +#define IVTV_FW_ENC_SIZE (376836) +#define IVTV_FW_DEC_SIZE (256*1024) + +static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv *itv, long size) +{ + const struct firmware *fw = NULL; + int retries = 3; + +retry: + if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) { + int i; + volatile u32 __iomem *dst = (volatile u32 __iomem *)mem; + const u32 *src = (const u32 *)fw->data; + + /* temporarily allow 256 KB encoding firmwares as well for + compatibility with blackbird cards */ + if (fw->size != size && fw->size != 256 * 1024) { + /* Due to race conditions in firmware loading (esp. with udev <0.95) + the wrong file was sometimes loaded. So we check filesizes to + see if at least the right-sized file was loaded. If not, then we + retry. */ + IVTV_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n", fn, size, fw->size); + release_firmware(fw); + retries--; + goto retry; + } + for (i = 0; i < fw->size; i += 4) { + /* no need for endianness conversion on the ppc */ + __raw_writel(*src, dst); + dst++; + src++; + } + release_firmware(fw); + IVTV_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size); + return size; + } + IVTV_ERR("unable to open firmware %s (must be %ld bytes)\n", fn, size); + IVTV_ERR("did you put the firmware in the hotplug firmware directory?\n"); + return -ENOMEM; +} + +void ivtv_halt_firmware(struct ivtv *itv) +{ + IVTV_DEBUG_INFO("Preparing for firmware halt.\n"); + if (itv->has_cx23415 && itv->dec_mbox.mbox) + ivtv_vapi(itv, CX2341X_DEC_HALT_FW, 0); + if (itv->enc_mbox.mbox) + ivtv_vapi(itv, CX2341X_ENC_HALT_FW, 0); + + ivtv_sleep_timeout(HZ / 100, 0); + itv->enc_mbox.mbox = itv->dec_mbox.mbox = NULL; + + IVTV_DEBUG_INFO("Stopping VDM\n"); + write_reg(IVTV_CMD_VDM_STOP, IVTV_REG_VDM); + + IVTV_DEBUG_INFO("Stopping AO\n"); + write_reg(IVTV_CMD_AO_STOP, IVTV_REG_AO); + + IVTV_DEBUG_INFO("pinging (?) APU\n"); + write_reg(IVTV_CMD_APU_PING, IVTV_REG_APU); + + IVTV_DEBUG_INFO("Stopping VPU\n"); + if (!itv->has_cx23415) + write_reg(IVTV_CMD_VPU_STOP16, IVTV_REG_VPU); + else + write_reg(IVTV_CMD_VPU_STOP15, IVTV_REG_VPU); + + IVTV_DEBUG_INFO("Resetting Hw Blocks\n"); + write_reg(IVTV_CMD_HW_BLOCKS_RST, IVTV_REG_HW_BLOCKS); + + IVTV_DEBUG_INFO("Stopping SPU\n"); + write_reg(IVTV_CMD_SPU_STOP, IVTV_REG_SPU); + + ivtv_sleep_timeout(HZ / 100, 0); + + IVTV_DEBUG_INFO("init Encoder SDRAM pre-charge\n"); + write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_ENC_SDRAM_PRECHARGE); + + IVTV_DEBUG_INFO("init Encoder SDRAM refresh to 1us\n"); + write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_ENC_SDRAM_REFRESH); + + if (itv->has_cx23415) { + IVTV_DEBUG_INFO("init Decoder SDRAM pre-charge\n"); + write_reg(IVTV_CMD_SDRAM_PRECHARGE_INIT, IVTV_REG_DEC_SDRAM_PRECHARGE); + + IVTV_DEBUG_INFO("init Decoder SDRAM refresh to 1us\n"); + write_reg(IVTV_CMD_SDRAM_REFRESH_INIT, IVTV_REG_DEC_SDRAM_REFRESH); + } + + IVTV_DEBUG_INFO("Sleeping for %dms (600 recommended)\n", + (int)(IVTV_SDRAM_SLEEPTIME * 1000 / HZ)); + ivtv_sleep_timeout(IVTV_SDRAM_SLEEPTIME, 0); +} + +void ivtv_firmware_versions(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + /* Encoder */ + ivtv_vapi_result(itv, data, CX2341X_ENC_GET_VERSION, 0); + IVTV_INFO("Encoder revision: 0x%08x\n", data[0]); + + if (data[0] != 0x02060039) + IVTV_WARN("Recommended firmware version is 0x02060039.\n"); + + if (itv->has_cx23415) { + /* Decoder */ + ivtv_vapi_result(itv, data, CX2341X_DEC_GET_VERSION, 0); + IVTV_INFO("Decoder revision: 0x%08x\n", data[0]); + } +} + +static int ivtv_firmware_copy(struct ivtv *itv) +{ + IVTV_DEBUG_INFO("Loading encoder image\n"); + if (load_fw_direct(CX2341X_FIRM_ENC_FILENAME, + itv->enc_mem, itv, IVTV_FW_ENC_SIZE) != IVTV_FW_ENC_SIZE) { + IVTV_DEBUG_WARN("failed loading encoder firmware\n"); + return -3; + } + if (!itv->has_cx23415) + return 0; + + IVTV_DEBUG_INFO("Loading decoder image\n"); + if (load_fw_direct(CX2341X_FIRM_DEC_FILENAME, + itv->dec_mem, itv, IVTV_FW_DEC_SIZE) != IVTV_FW_DEC_SIZE) { + IVTV_DEBUG_WARN("failed loading decoder firmware\n"); + return -1; + } + return 0; +} + +static volatile struct ivtv_mailbox __iomem *ivtv_search_mailbox(const volatile u8 __iomem *mem, u32 size) +{ + int i; + + /* mailbox is preceeded by a 16 byte 'magic cookie' starting at a 256-byte + address boundary */ + for (i = 0; i < size; i += 0x100) { + if (readl(mem + i) == 0x12345678 && + readl(mem + i + 4) == 0x34567812 && + readl(mem + i + 8) == 0x56781234 && + readl(mem + i + 12) == 0x78123456) { + return (volatile struct ivtv_mailbox __iomem *)(mem + i + 16); + } + } + return NULL; +} + +int ivtv_firmware_init(struct ivtv *itv) +{ + int err; + + ivtv_halt_firmware(itv); + + /* load firmware */ + err = ivtv_firmware_copy(itv); + if (err) { + IVTV_DEBUG_WARN("Error %d loading firmware\n", err); + return err; + } + + /* start firmware */ + write_reg(read_reg(IVTV_REG_SPU) & IVTV_MASK_SPU_ENABLE, IVTV_REG_SPU); + ivtv_sleep_timeout(HZ / 10, 0); + if (itv->has_cx23415) + write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE15, IVTV_REG_VPU); + else + write_reg(read_reg(IVTV_REG_VPU) & IVTV_MASK_VPU_ENABLE16, IVTV_REG_VPU); + ivtv_sleep_timeout(HZ / 10, 0); + + /* find mailboxes and ping firmware */ + itv->enc_mbox.mbox = ivtv_search_mailbox(itv->enc_mem, IVTV_ENCODER_SIZE); + if (itv->enc_mbox.mbox == NULL) + IVTV_ERR("Encoder mailbox not found\n"); + else if (ivtv_vapi(itv, CX2341X_ENC_PING_FW, 0)) { + IVTV_ERR("Encoder firmware dead!\n"); + itv->enc_mbox.mbox = NULL; + } + if (itv->enc_mbox.mbox == NULL) + return -ENODEV; + + if (!itv->has_cx23415) + return 0; + + itv->dec_mbox.mbox = ivtv_search_mailbox(itv->dec_mem, IVTV_DECODER_SIZE); + if (itv->dec_mbox.mbox == NULL) + IVTV_ERR("Decoder mailbox not found\n"); + else if (itv->has_cx23415 && ivtv_vapi(itv, CX2341X_DEC_PING_FW, 0)) { + IVTV_ERR("Decoder firmware dead!\n"); + itv->dec_mbox.mbox = NULL; + } + return itv->dec_mbox.mbox ? 0 : -ENODEV; +} + +void ivtv_init_mpeg_decoder(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + long readbytes; + volatile u8 __iomem *mem_offset; + + data[0] = 0; + data[1] = itv->params.width; /* YUV source width */ + data[2] = itv->params.height; + data[3] = itv->params.audio_properties; /* Audio settings to use, + bitmap. see docs. */ + if (ivtv_api(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, data)) { + IVTV_ERR("ivtv_init_mpeg_decoder failed to set decoder source\n"); + return; + } + + if (ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1) != 0) { + IVTV_ERR("ivtv_init_mpeg_decoder failed to start playback\n"); + return; + } + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + mem_offset = itv->dec_mem + data[1]; + + if ((readbytes = load_fw_direct(IVTV_DECODE_INIT_MPEG_FILENAME, + mem_offset, itv, IVTV_DECODE_INIT_MPEG_SIZE)) <= 0) { + IVTV_DEBUG_WARN("failed to read mpeg decoder initialisation file %s\n", + IVTV_DECODE_INIT_MPEG_FILENAME); + } else { + ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, readbytes, 0); + ivtv_sleep_timeout(HZ / 10, 0); + } + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 4, 0, 0, 0, 1); +} diff --git a/drivers/media/video/ivtv/ivtv-firmware.h b/drivers/media/video/ivtv/ivtv-firmware.h new file mode 100644 index 000000000000..8b2ffe658905 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-firmware.h @@ -0,0 +1,25 @@ +/* + ivtv firmware functions. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_firmware_init(struct ivtv *itv); +void ivtv_firmware_versions(struct ivtv *itv); +void ivtv_halt_firmware(struct ivtv *itv); +void ivtv_init_mpeg_decoder(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c new file mode 100644 index 000000000000..bc8f8ca2961f --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-gpio.c @@ -0,0 +1,307 @@ +/* + gpio functions. + Merging GPIO support into driver: + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-gpio.h" +#include + +/* + * GPIO assignment of Yuan MPG600/MPG160 + * + * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 + * OUTPUT IN1 IN0 AM3 AM2 AM1 AM0 + * INPUT DM1 DM0 + * + * IN* : Input selection + * IN1 IN0 + * 1 1 N/A + * 1 0 Line + * 0 1 N/A + * 0 0 Tuner + * + * AM* : Audio Mode + * AM3 0: Normal 1: Mixed(Sub+Main channel) + * AM2 0: Subchannel 1: Main channel + * AM1 0: Stereo 1: Mono + * AM0 0: Normal 1: Mute + * + * DM* : Detected tuner audio Mode + * DM1 0: Stereo 1: Mono + * DM0 0: Multiplex 1: Normal + * + * GPIO Initial Settings + * MPG600 MPG160 + * DIR 0x3080 0x7080 + * OUTPUT 0x000C 0x400C + * + * Special thanks to Makoto Iguchi and Mr. Anonymous + * for analyzing GPIO of MPG160. + * + ***************************************************************************** + * + * GPIO assignment of Avermedia M179 (per information direct from AVerMedia) + * + * bit 15 14 13 12 | 11 10 9 8 | 7 6 5 4 | 3 2 1 0 + * OUTPUT IN0 AM0 IN1 AM1 AM2 IN2 BR0 BR1 + * INPUT + * + * IN* : Input selection + * IN0 IN1 IN2 + * * 1 * Mute + * 0 0 0 Line-In + * 1 0 0 TV Tuner Audio + * 0 0 1 FM Audio + * 1 0 1 Mute + * + * AM* : Audio Mode + * AM0 AM1 AM2 + * 0 0 0 TV Tuner Audio: L_OUT=(L+R)/2, R_OUT=SAP + * 0 0 1 TV Tuner Audio: L_OUT=R_OUT=SAP (SAP) + * 0 1 0 TV Tuner Audio: L_OUT=L, R_OUT=R (stereo) + * 0 1 1 TV Tuner Audio: mute + * 1 * * TV Tuner Audio: L_OUT=R_OUT=(L+R)/2 (mono) + * + * BR* : Audio Sample Rate (BR stands for bitrate for some reason) + * BR0 BR1 + * 0 0 32 kHz + * 0 1 44.1 kHz + * 1 0 48 kHz + * + * DM* : Detected tuner audio Mode + * Unknown currently + * + * Special thanks to AVerMedia Technologies, Inc. and Jiun-Kuei Jung at + * AVerMedia for providing the GPIO information used to add support + * for the M179 cards. + */ + +/********************* GPIO stuffs *********************/ + +/* GPIO registers */ +#define IVTV_REG_GPIO_IN 0x9008 +#define IVTV_REG_GPIO_OUT 0x900c +#define IVTV_REG_GPIO_DIR 0x9020 + +void ivtv_reset_ir_gpio(struct ivtv *itv) +{ + int curdir, curout; + + if (itv->card->type != IVTV_CARD_PVR_150) + return; + IVTV_DEBUG_INFO("Resetting PVR150 IR\n"); + curout = read_reg(IVTV_REG_GPIO_OUT); + curdir = read_reg(IVTV_REG_GPIO_DIR); + curdir |= 0x80; + write_reg(curdir, IVTV_REG_GPIO_DIR); + curout = (curout & ~0xF) | 1; + write_reg(curout, IVTV_REG_GPIO_OUT); + /* We could use something else for smaller time */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + curout |= 2; + write_reg(curout, IVTV_REG_GPIO_OUT); + curdir &= ~0x80; + write_reg(curdir, IVTV_REG_GPIO_DIR); +} + +#ifdef HAVE_XC3028 +int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr) +{ + int curdir, curout; + struct ivtv *itv = (struct ivtv *) priv; + + if (itv->card->type != IVTV_CARD_PG600V2 || itv->options.tuner != TUNER_XCEIVE_XC3028) + return -EINVAL; + IVTV_INFO("Resetting tuner.\n"); + curout = read_reg(IVTV_REG_GPIO_OUT); + curdir = read_reg(IVTV_REG_GPIO_DIR); + curdir |= (1 << 12); /* GPIO bit 12 */ + + curout &= ~(1 << 12); + write_reg(curout, IVTV_REG_GPIO_OUT); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + curout |= (1 << 12); + write_reg(curout, IVTV_REG_GPIO_OUT); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + + return 0; +} +#endif + +void ivtv_gpio_init(struct ivtv *itv) +{ + if (itv->card->gpio_init.direction == 0) + return; + + IVTV_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT)); + + /* init output data then direction */ + write_reg(itv->card->gpio_init.initial_value, IVTV_REG_GPIO_OUT); + write_reg(itv->card->gpio_init.direction, IVTV_REG_GPIO_DIR); +} + +static struct v4l2_queryctrl gpio_ctrl_mute = { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0, +}; + +int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg) +{ + struct v4l2_tuner *tuner = arg; + struct v4l2_control *ctrl = arg; + struct v4l2_routing *route = arg; + u16 mask, data; + + switch (command) { + case VIDIOC_INT_AUDIO_CLOCK_FREQ: + mask = itv->card->gpio_audio_freq.mask; + switch (*(u32 *)arg) { + case 32000: + data = itv->card->gpio_audio_freq.f32000; + break; + case 44100: + data = itv->card->gpio_audio_freq.f44100; + break; + case 48000: + default: + data = itv->card->gpio_audio_freq.f48000; + break; + } + break; + + case VIDIOC_G_TUNER: + mask = itv->card->gpio_audio_detect.mask; + if (mask == 0 || (read_reg(IVTV_REG_GPIO_IN) & mask)) + tuner->rxsubchans = V4L2_TUNER_MODE_STEREO | + V4L2_TUNER_MODE_LANG1 | V4L2_TUNER_MODE_LANG2; + else + tuner->rxsubchans = V4L2_TUNER_SUB_MONO; + return 0; + + case VIDIOC_S_TUNER: + mask = itv->card->gpio_audio_mode.mask; + switch (tuner->audmode) { + case V4L2_TUNER_MODE_LANG1: + data = itv->card->gpio_audio_mode.lang1; + break; + case V4L2_TUNER_MODE_LANG2: + data = itv->card->gpio_audio_mode.lang2; + break; + case V4L2_TUNER_MODE_MONO: + data = itv->card->gpio_audio_mode.mono; + break; + case V4L2_TUNER_MODE_STEREO: + case V4L2_TUNER_MODE_LANG1_LANG2: + default: + data = itv->card->gpio_audio_mode.stereo; + break; + } + break; + + case AUDC_SET_RADIO: + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.radio; + break; + + case VIDIOC_S_STD: + mask = itv->card->gpio_audio_input.mask; + data = itv->card->gpio_audio_input.tuner; + break; + + case VIDIOC_INT_S_AUDIO_ROUTING: + if (route->input > 2) + return -EINVAL; + mask = itv->card->gpio_audio_input.mask; + switch (route->input) { + case 0: + data = itv->card->gpio_audio_input.tuner; + break; + case 1: + data = itv->card->gpio_audio_input.linein; + break; + case 2: + default: + data = itv->card->gpio_audio_input.radio; + break; + } + break; + + case VIDIOC_G_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = itv->card->gpio_audio_mute.mute; + ctrl->value = (read_reg(IVTV_REG_GPIO_OUT) & mask) == data; + return 0; + + case VIDIOC_S_CTRL: + if (ctrl->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + mask = itv->card->gpio_audio_mute.mask; + data = ctrl->value ? itv->card->gpio_audio_mute.mute : 0; + break; + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + + if (qc->id != V4L2_CID_AUDIO_MUTE) + return -EINVAL; + *qc = gpio_ctrl_mute; + return 0; + } + + case VIDIOC_LOG_STATUS: + IVTV_INFO("GPIO status: DIR=0x%04x OUT=0x%04x IN=0x%04x\n", + read_reg(IVTV_REG_GPIO_DIR), read_reg(IVTV_REG_GPIO_OUT), + read_reg(IVTV_REG_GPIO_IN)); + return 0; + + case VIDIOC_INT_S_VIDEO_ROUTING: + if (route->input > 2) /* 0:Tuner 1:Composite 2:S-Video */ + return -EINVAL; + mask = itv->card->gpio_video_input.mask; + if (route->input == 0) + data = itv->card->gpio_video_input.tuner; + else if (route->input == 1) + data = itv->card->gpio_video_input.composite; + else + data = itv->card->gpio_video_input.svideo; + break; + + default: + return -EINVAL; + } + if (mask) + write_reg((read_reg(IVTV_REG_GPIO_OUT) & ~mask) | (data & mask), IVTV_REG_GPIO_OUT); + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-gpio.h b/drivers/media/video/ivtv/ivtv-gpio.h new file mode 100644 index 000000000000..c301d2a39346 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-gpio.h @@ -0,0 +1,25 @@ +/* + gpio functions. + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* GPIO stuff */ +void ivtv_gpio_init(struct ivtv *itv); +void ivtv_reset_ir_gpio(struct ivtv *itv); +int ivtv_reset_tuner_gpio(enum v4l2_tuner_type mode, void *priv, int ptr); +int ivtv_gpio(struct ivtv *itv, unsigned int command, void *arg); diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c new file mode 100644 index 000000000000..17353415b0a3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -0,0 +1,750 @@ +/* + I2C functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + This file includes an i2c implementation that was reverse engineered + from the Hauppauge windows driver. Older ivtv versions used i2c-algo-bit, + which whilst fine under most circumstances, had trouble with the Zilog + CPU on the PVR-150 which handles IR functions (occasional inability to + communicate with the chip until it was reset) and also with the i2c + bus being completely unreachable when multiple PVR cards were present. + + The implementation is very similar to i2c-algo-bit, but there are enough + subtle differences that the two are hard to merge. The general strategy + employed by i2c-algo-bit is to use udelay() to implement the timing + when putting out bits on the scl/sda lines. The general strategy taken + here is to poll the lines for state changes (see ivtv_waitscl and + ivtv_waitsda). In addition there are small delays at various locations + which poll the SCL line 5 times (ivtv_scldelay). I would guess that + since this is memory mapped I/O that the length of those delays is tied + to the PCI bus clock. There is some extra code to do with recovery + and retries. Since it is not known what causes the actual i2c problems + in the first place, the only goal if one was to attempt to use + i2c-algo-bit would be to try to make it follow the same code path. + This would be a lot of work, and I'm also not convinced that it would + provide a generic benefit to i2c-algo-bit. Therefore consider this + an engineering solution -- not pretty, but it works. + + Some more general comments about what we are doing: + + The i2c bus is a 2 wire serial bus, with clock (SCL) and data (SDA) + lines. To communicate on the bus (as a master, we don't act as a slave), + we first initiate a start condition (ivtv_start). We then write the + address of the device that we want to communicate with, along with a flag + that indicates whether this is a read or a write. The slave then issues + an ACK signal (ivtv_ack), which tells us that it is ready for reading / + writing. We then proceed with reading or writing (ivtv_read/ivtv_write), + and finally issue a stop condition (ivtv_stop) to make the bus available + to other masters. + + There is an additional form of transaction where a write may be + immediately followed by a read. In this case, there is no intervening + stop condition. (Only the msp3400 chip uses this method of data transfer). + */ + +#include "ivtv-driver.h" +#include "ivtv-cards.h" +#include "ivtv-gpio.h" + +#include + +/* i2c implementation for cx23415/6 chip, ivtv project. + * Author: Kevin Thayer (nufan_wfk at yahoo.com) + */ +/* i2c stuff */ +#define IVTV_REG_I2C_SETSCL_OFFSET 0x7000 +#define IVTV_REG_I2C_SETSDA_OFFSET 0x7004 +#define IVTV_REG_I2C_GETSCL_OFFSET 0x7008 +#define IVTV_REG_I2C_GETSDA_OFFSET 0x700c + +#ifndef I2C_ADAP_CLASS_TV_ANALOG +#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG +#endif /* I2C_ADAP_CLASS_TV_ANALOG */ + +#define IVTV_CS53L32A_I2C_ADDR 0x11 +#define IVTV_CX25840_I2C_ADDR 0x44 +#define IVTV_SAA7115_I2C_ADDR 0x21 +#define IVTV_SAA7127_I2C_ADDR 0x44 +#define IVTV_SAA717x_I2C_ADDR 0x21 +#define IVTV_MSP3400_I2C_ADDR 0x40 +#define IVTV_HAUPPAUGE_I2C_ADDR 0x50 +#define IVTV_WM8739_I2C_ADDR 0x1a +#define IVTV_WM8775_I2C_ADDR 0x1b +#define IVTV_TEA5767_I2C_ADDR 0x60 +#define IVTV_UPD64031A_I2C_ADDR 0x12 +#define IVTV_UPD64083_I2C_ADDR 0x5c +#define IVTV_TDA985X_I2C_ADDR 0x5b + +/* This array should match the IVTV_HW_ defines */ +static const u8 hw_driverids[] = { + I2C_DRIVERID_CX25840, + I2C_DRIVERID_SAA711X, + I2C_DRIVERID_SAA7127, + I2C_DRIVERID_MSP3400, + I2C_DRIVERID_TUNER, + I2C_DRIVERID_WM8775, + I2C_DRIVERID_CS53L32A, + I2C_DRIVERID_TVEEPROM, + I2C_DRIVERID_SAA711X, + I2C_DRIVERID_TVAUDIO, + I2C_DRIVERID_UPD64031A, + I2C_DRIVERID_UPD64083, + I2C_DRIVERID_SAA717X, + I2C_DRIVERID_WM8739, + 0 /* IVTV_HW_GPIO dummy driver ID */ +}; + +/* This array should match the IVTV_HW_ defines */ +static const char * const hw_drivernames[] = { + "cx2584x", + "saa7115", + "saa7127", + "msp3400", + "tuner", + "wm8775", + "cs53l32a", + "tveeprom", + "saa7114", + "tvaudio", + "upd64031a", + "upd64083", + "saa717x", + "wm8739", + "gpio", +}; + +static int attach_inform(struct i2c_client *client) +{ + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + int i; + + 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; + } + } + if (i == I2C_CLIENTS_MAX) { + IVTV_ERR("insufficient room for new I2C client!\n"); + } + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + int i; + struct ivtv *itv = (struct ivtv *)i2c_get_adapdata(client->adapter); + + 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_I2C("i2c detach [client=%s,%s]\n", + client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed"); + + return 0; +} + +/* Set the serial clock line to the desired state */ +static void ivtv_setscl(struct ivtv *itv, int state) +{ + /* write them out */ + /* write bits are inverted */ + write_reg(~state, IVTV_REG_I2C_SETSCL_OFFSET); +} + +/* Set the serial data line to the desired state */ +static void ivtv_setsda(struct ivtv *itv, int state) +{ + /* write them out */ + /* write bits are inverted */ + write_reg(~state & 1, IVTV_REG_I2C_SETSDA_OFFSET); +} + +/* Read the serial clock line */ +static int ivtv_getscl(struct ivtv *itv) +{ + return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; +} + +/* Read the serial data line */ +static int ivtv_getsda(struct ivtv *itv) +{ + return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; +} + +/* Implement a short delay by polling the serial clock line */ +static void ivtv_scldelay(struct ivtv *itv) +{ + int i; + + for (i = 0; i < 5; ++i) + ivtv_getscl(itv); +} + +/* Wait for the serial clock line to become set to a specific value */ +static int ivtv_waitscl(struct ivtv *itv, int val) +{ + int i; + + ivtv_scldelay(itv); + for (i = 0; i < 1000; ++i) { + if (ivtv_getscl(itv) == val) + return 1; + } + return 0; +} + +/* Wait for the serial data line to become set to a specific value */ +static int ivtv_waitsda(struct ivtv *itv, int val) +{ + int i; + + ivtv_scldelay(itv); + for (i = 0; i < 1000; ++i) { + if (ivtv_getsda(itv) == val) + return 1; + } + return 0; +} + +/* Wait for the slave to issue an ACK */ +static int ivtv_ack(struct ivtv *itv) +{ + int ret = 0; + + if (ivtv_getscl(itv) == 1) { + IVTV_DEBUG_I2C("SCL was high starting an ack\n"); + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Could not set SCL low starting an ack\n"); + return -EREMOTEIO; + } + } + ivtv_setsda(itv, 1); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitsda(itv, 0)) { + IVTV_DEBUG_I2C("Slave did not ack\n"); + ret = -EREMOTEIO; + } + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Failed to set SCL low after ACK\n"); + ret = -EREMOTEIO; + } + return ret; +} + +/* Write a single byte to the i2c bus and wait for the slave to ACK */ +static int ivtv_sendbyte(struct ivtv *itv, unsigned char byte) +{ + int i, bit; + + IVTV_DEBUG_I2C("write %x\n",byte); + for (i = 0; i < 8; ++i, byte<<=1) { + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Error setting SCL low\n"); + return -EREMOTEIO; + } + bit = (byte>>7)&1; + ivtv_setsda(itv, bit); + if (!ivtv_waitsda(itv, bit)) { + IVTV_DEBUG_I2C("Error setting SDA\n"); + return -EREMOTEIO; + } + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("Slave not ready for bit\n"); + return -EREMOTEIO; + } + } + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("Error setting SCL low\n"); + return -EREMOTEIO; + } + return ivtv_ack(itv); +} + +/* Read a byte from the i2c bus and send a NACK if applicable (i.e. for the + final byte) */ +static int ivtv_readbyte(struct ivtv *itv, unsigned char *byte, int nack) +{ + int i; + + *byte = 0; + + ivtv_setsda(itv, 1); + ivtv_scldelay(itv); + for (i = 0; i < 8; ++i) { + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("Error setting SCL high\n"); + return -EREMOTEIO; + } + *byte = ((*byte)<<1)|ivtv_getsda(itv); + } + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setsda(itv, nack); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + ivtv_scldelay(itv); + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + IVTV_DEBUG_I2C("read %x\n",*byte); + return 0; +} + +/* Issue a start condition on the i2c bus to alert slaves to prepare for + an address write */ +static int ivtv_start(struct ivtv *itv) +{ + int sda; + + sda = ivtv_getsda(itv); + if (sda != 1) { + IVTV_DEBUG_I2C("SDA was low at start\n"); + ivtv_setsda(itv, 1); + if (!ivtv_waitsda(itv, 1)) { + IVTV_DEBUG_I2C("SDA stuck low\n"); + return -EREMOTEIO; + } + } + if (ivtv_getscl(itv) != 1) { + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("SCL stuck low at start\n"); + return -EREMOTEIO; + } + } + ivtv_setsda(itv, 0); + ivtv_scldelay(itv); + return 0; +} + +/* Issue a stop condition on the i2c bus to release it */ +static int ivtv_stop(struct ivtv *itv) +{ + int i; + + if (ivtv_getscl(itv) != 0) { + IVTV_DEBUG_I2C("SCL not low when stopping\n"); + ivtv_setscl(itv, 0); + if (!ivtv_waitscl(itv, 0)) { + IVTV_DEBUG_I2C("SCL could not be set low\n"); + } + } + ivtv_setsda(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + if (!ivtv_waitscl(itv, 1)) { + IVTV_DEBUG_I2C("SCL could not be set high\n"); + return -EREMOTEIO; + } + ivtv_scldelay(itv); + ivtv_setsda(itv, 1); + if (!ivtv_waitsda(itv, 1)) { + IVTV_DEBUG_I2C("resetting I2C\n"); + for (i = 0; i < 16; ++i) { + ivtv_setscl(itv, 0); + ivtv_scldelay(itv); + ivtv_setscl(itv, 1); + ivtv_scldelay(itv); + ivtv_setsda(itv, 1); + } + ivtv_waitsda(itv, 1); + return -EREMOTEIO; + } + return 0; +} + +/* Write a message to the given i2c slave. do_stop may be 0 to prevent + issuing the i2c stop condition (when following with a read) */ +static int ivtv_write(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len, int do_stop) +{ + int retry, ret = -EREMOTEIO; + u32 i; + + for (retry = 0; ret != 0 && retry < 8; ++retry) { + ret = ivtv_start(itv); + + if (ret == 0) { + ret = ivtv_sendbyte(itv, addr<<1); + for (i = 0; ret == 0 && i < len; ++i) + ret = ivtv_sendbyte(itv, data[i]); + } + if (ret != 0 || do_stop) { + ivtv_stop(itv); + } + } + if (ret) + IVTV_DEBUG_I2C("i2c write to %x failed\n", addr); + return ret; +} + +/* Read data from the given i2c slave. A stop condition is always issued. */ +static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data, u32 len) +{ + int retry, ret = -EREMOTEIO; + u32 i; + + for (retry = 0; ret != 0 && retry < 8; ++retry) { + ret = ivtv_start(itv); + if (ret == 0) + ret = ivtv_sendbyte(itv, (addr << 1) | 1); + for (i = 0; ret == 0 && i < len; ++i) { + ret = ivtv_readbyte(itv, &data[i], i == len - 1); + } + ivtv_stop(itv); + } + if (ret) + IVTV_DEBUG_I2C("i2c read from %x failed\n", addr); + return ret; +} + +/* Kernel i2c transfer implementation. Takes a number of messages to be read + or written. If a read follows a write, this will occur without an + intervening stop condition */ +static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct ivtv *itv = i2c_get_adapdata(i2c_adap); + int retval; + int i; + + mutex_lock(&itv->i2c_bus_lock); + for (i = retval = 0; retval == 0 && i < num; i++) { + if (msgs[i].flags & I2C_M_RD) + retval = ivtv_read(itv, msgs[i].addr, msgs[i].buf, msgs[i].len); + else { + /* if followed by a read, don't stop */ + int stop = !(i + 1 < num && msgs[i + 1].flags == I2C_M_RD); + + retval = ivtv_write(itv, msgs[i].addr, msgs[i].buf, msgs[i].len, stop); + } + } + mutex_unlock(&itv->i2c_bus_lock); + return retval ? retval : num; +} + +/* Kernel i2c capabilities */ +static u32 ivtv_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static struct i2c_algorithm ivtv_algo = { + .master_xfer = ivtv_xfer, + .functionality = ivtv_functionality, +}; + +/* template for our-bit banger */ +static struct i2c_adapter ivtv_i2c_adap_hw_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_CX2341X, + .algo = &ivtv_algo, + .algo_data = NULL, /* filled from template */ + .client_register = attach_inform, + .client_unregister = detach_inform, + .owner = THIS_MODULE, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +}; + +static void ivtv_setscl_old(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 */ + write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSCL_OFFSET); +} + +static void ivtv_setsda_old(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 */ + write_reg(~itv->i2c_state, IVTV_REG_I2C_SETSDA_OFFSET); +} + +static int ivtv_getscl_old(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + + return read_reg(IVTV_REG_I2C_GETSCL_OFFSET) & 1; +} + +static int ivtv_getsda_old(void *data) +{ + struct ivtv *itv = (struct ivtv *)data; + + return read_reg(IVTV_REG_I2C_GETSDA_OFFSET) & 1; +} + +/* template for i2c-bit-algo */ +static struct i2c_adapter ivtv_i2c_adap_template = { + .name = "ivtv i2c driver", + .id = I2C_HW_B_CX2341X, /* 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, + .owner = THIS_MODULE, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif +}; + +static struct i2c_algo_bit_data ivtv_i2c_algo_template = { + NULL, /* ?? */ + ivtv_setsda_old, /* setsda function */ + ivtv_setscl_old, /* " */ + ivtv_getsda_old, /* " */ + ivtv_getscl_old, /* " */ + 10, /* udelay */ + 200 /* timeout */ +}; + +static struct i2c_client ivtv_i2c_client_template = { + .name = "ivtv internal use only", +}; + +int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg) +{ + struct i2c_client *client; + int retval; + int i; + + IVTV_DEBUG_I2C("call_i2c_client addr=%02x\n", addr); + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = itv->i2c_clients[i]; + if (client == NULL) { + continue; + } + if (client->driver->command == NULL) { + continue; + } + if (addr == client->addr) { + retval = client->driver->command(client, cmd, arg); + return retval; + } + } + IVTV_ERR("i2c addr 0x%02x not found for command 0x%x!\n", addr, cmd); + return -ENODEV; +} + +/* Find the i2c device based on the driver ID and return + its i2c address or -ENODEV if no matching device was found. */ +int ivtv_i2c_id_addr(struct ivtv *itv, u32 id) +{ + struct i2c_client *client; + int retval = -ENODEV; + int i; + + for (i = 0; i < I2C_CLIENTS_MAX; i++) { + client = itv->i2c_clients[i]; + if (client == NULL) + continue; + if (id == client->driver->id) { + retval = client->addr; + break; + } + } + return retval; +} + +/* Find the i2c device name matching the DRIVERID */ +static const char *ivtv_i2c_id_name(u32 id) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (hw_driverids[i] == id) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device name matching the IVTV_HW_ flag */ +static const char *ivtv_i2c_hw_name(u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return hw_drivernames[i]; + return "unknown device"; +} + +/* Find the i2c device matching the IVTV_HW_ flag and return + its i2c address or -ENODEV if no matching device was found. */ +int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(hw_driverids); i++) + if (1 << i == hw) + return ivtv_i2c_id_addr(itv, hw_driverids[i]); + return -ENODEV; +} + +/* Calls i2c device based on IVTV_HW_ flag. If hw == 0, then do nothing. + If hw == IVTV_HW_GPIO then call the gpio handler. */ +int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg) +{ + int addr; + + if (hw == IVTV_HW_GPIO) + return ivtv_gpio(itv, cmd, arg); + if (hw == 0) + return 0; + + addr = ivtv_i2c_hw_addr(itv, hw); + if (addr < 0) { + IVTV_ERR("i2c hardware 0x%08x (%s) not found for command 0x%x!\n", + hw, ivtv_i2c_hw_name(hw), cmd); + return addr; + } + return ivtv_call_i2c_client(itv, addr, cmd, arg); +} + +/* Calls i2c device based on I2C driver ID. */ +int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg) +{ + int addr; + + addr = ivtv_i2c_id_addr(itv, id); + if (addr < 0) { + IVTV_ERR("i2c ID 0x%08x (%s) not found for command 0x%x!\n", + id, ivtv_i2c_id_name(id), cmd); + return addr; + } + return ivtv_call_i2c_client(itv, addr, cmd, arg); +} + +int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_CX25840_I2C_ADDR, cmd, arg); +} + +int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA7115_I2C_ADDR, cmd, arg); +} + +int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA7127_I2C_ADDR, cmd, arg); +} + +int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_SAA717x_I2C_ADDR, cmd, arg); +} + +int ivtv_msp34xx(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_MSP3400_I2C_ADDR, cmd, arg); +} + +int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_UPD64031A_I2C_ADDR, cmd, arg); +} + +int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg) +{ + return ivtv_call_i2c_client(itv, IVTV_UPD64083_I2C_ADDR, cmd, arg); +} + +/* broadcast cmd for all I2C clients and for the gpio subsystem */ +void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg) +{ + if (itv->i2c_adap.algo == NULL) { + IVTV_ERR("adapter is not set"); + return; + } + i2c_clients_command(&itv->i2c_adap, cmd, arg); + if (itv->hw_flags & IVTV_HW_GPIO) + ivtv_gpio(itv, cmd, arg); +} + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG_I2C("i2c init\n"); + + if (itv->options.newi2c > 0) { + memcpy(&itv->i2c_adap, &ivtv_i2c_adap_hw_template, + sizeof(struct i2c_adapter)); + } else { + memcpy(&itv->i2c_adap, &ivtv_i2c_adap_template, + sizeof(struct i2c_adapter)); + memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, + sizeof(struct i2c_algo_bit_data)); + itv->i2c_algo.data = itv; + itv->i2c_adap.algo_data = &itv->i2c_algo; + } + + sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d", + itv->num); + i2c_set_adapdata(&itv->i2c_adap, itv); + + memcpy(&itv->i2c_client, &ivtv_i2c_client_template, + sizeof(struct i2c_client)); + itv->i2c_client.adapter = &itv->i2c_adap; + itv->i2c_adap.dev.parent = &itv->dev->dev; + + IVTV_DEBUG_I2C("setting scl and sda to 1\n"); + ivtv_setscl(itv, 1); + ivtv_setsda(itv, 1); + + if (itv->options.newi2c > 0) + return i2c_add_adapter(&itv->i2c_adap); + else + return i2c_bit_add_bus(&itv->i2c_adap); +} + +void __devexit exit_ivtv_i2c(struct ivtv *itv) +{ + IVTV_DEBUG_I2C("i2c exit\n"); + + i2c_del_adapter(&itv->i2c_adap); +} diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h new file mode 100644 index 000000000000..136dd684f4b5 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -0,0 +1,38 @@ +/* + I2C functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_cx25840(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa7115(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa7127(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_saa717x(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_msp34xx(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_upd64031a(struct ivtv *itv, unsigned int cmd, void *arg); +int ivtv_upd64083(struct ivtv *itv, unsigned int cmd, void *arg); + +int ivtv_i2c_id_addr(struct ivtv *itv, u32 id); +int ivtv_i2c_hw_addr(struct ivtv *itv, u32 hw); +int ivtv_i2c_hw(struct ivtv *itv, u32 hw, unsigned int cmd, void *arg); +int ivtv_i2c_id(struct ivtv *itv, u32 id, unsigned int cmd, void *arg); +int ivtv_call_i2c_client(struct ivtv *itv, int addr, unsigned int cmd, void *arg); +void ivtv_call_i2c_clients(struct ivtv *itv, unsigned int cmd, void *arg); + +/* init + register i2c algo-bit adapter */ +int __devinit init_ivtv_i2c(struct ivtv *itv); +void __devexit exit_ivtv_i2c(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c new file mode 100644 index 000000000000..448e8dd5b42f --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -0,0 +1,1555 @@ +/* + ioctl system call + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-version.h" +#include "ivtv-mailbox.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-fileops.h" +#include "ivtv-vbi.h" +#include "ivtv-audio.h" +#include "ivtv-video.h" +#include "ivtv-streams.h" +#include "ivtv-yuv.h" +#include "ivtv-ioctl.h" +#include "ivtv-gpio.h" +#include "ivtv-controls.h" +#include "ivtv-cards.h" +#include +#include +#include +#include +#include + +u16 service2vbi(int type) +{ + switch (type) { + case V4L2_SLICED_TELETEXT_B: + return IVTV_SLICED_TYPE_TELETEXT_B; + case V4L2_SLICED_CAPTION_525: + return IVTV_SLICED_TYPE_CAPTION_525; + case V4L2_SLICED_WSS_625: + return IVTV_SLICED_TYPE_WSS_625; + case V4L2_SLICED_VPS: + return IVTV_SLICED_TYPE_VPS; + default: + return 0; + } +} + +static int valid_service_line(int field, int line, int is_pal) +{ + return (is_pal && line >= 6 && (line != 23 || field == 0)) || + (!is_pal && line >= 10 && line < 22); +} + +static u16 select_service_from_set(int field, int line, u16 set, int is_pal) +{ + u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525); + int i; + + set = set & valid_set; + if (set == 0 || !valid_service_line(field, line, is_pal)) { + return 0; + } + if (!is_pal) { + if (line == 21 && (set & V4L2_SLICED_CAPTION_525)) + return V4L2_SLICED_CAPTION_525; + } + else { + if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS)) + return V4L2_SLICED_VPS; + if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625)) + return V4L2_SLICED_WSS_625; + if (line == 23) + return 0; + } + for (i = 0; i < 32; i++) { + if ((1 << i) & set) + return 1 << i; + } + return 0; +} + +void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + u16 set = fmt->service_set; + int f, l; + + fmt->service_set = 0; + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal); + } + } +} + +static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal); + set |= fmt->service_lines[f][l]; + } + } + return set != 0; +} + +u16 get_service_set(struct v4l2_sliced_vbi_format *fmt) +{ + int f, l; + u16 set = 0; + + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + set |= fmt->service_lines[f][l]; + } + } + return set; +} + +static const struct { + v4l2_std_id std; + char *name; +} enum_stds[] = { + { V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" }, + { V4L2_STD_PAL_DK, "PAL-DK" }, + { V4L2_STD_PAL_I, "PAL-I" }, + { V4L2_STD_PAL_M, "PAL-M" }, + { V4L2_STD_PAL_N, "PAL-N" }, + { V4L2_STD_PAL_Nc, "PAL-Nc" }, + { V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" }, + { V4L2_STD_SECAM_DK, "SECAM-DK" }, + { V4L2_STD_SECAM_L, "SECAM-L" }, + { V4L2_STD_SECAM_LC, "SECAM-L'" }, + { V4L2_STD_NTSC_M, "NTSC-M" }, + { V4L2_STD_NTSC_M_JP, "NTSC-J" }, + { V4L2_STD_NTSC_M_KR, "NTSC-K" }, +}; + +static const struct v4l2_standard ivtv_std_60hz = +{ + .frameperiod = {.numerator = 1001, .denominator = 30000}, + .framelines = 525, +}; + +static const struct v4l2_standard ivtv_std_50hz = +{ + .frameperiod = {.numerator = 1, .denominator = 25}, + .framelines = 625, +}; + +void ivtv_set_osd_alpha(struct ivtv *itv) +{ + ivtv_vapi(itv, CX2341X_OSD_SET_GLOBAL_ALPHA, 3, + itv->osd_global_alpha_state, itv->osd_global_alpha, !itv->osd_local_alpha_state); + ivtv_vapi(itv, CX2341X_OSD_SET_CHROMA_KEY, 2, itv->osd_color_key_state, itv->osd_color_key); +} + +int ivtv_set_speed(struct ivtv *itv, int speed) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + int single_step = (speed == 1 || speed == -1); + DEFINE_WAIT(wait); + + if (speed == 0) speed = 1000; + + /* No change? */ + if (speed == itv->speed && !single_step) + return 0; + + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + + if (single_step && (speed < 0) == (itv->speed < 0)) { + /* Single step video and no need to change direction */ + ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); + itv->speed = speed; + return 0; + } + if (single_step) + /* Need to change direction */ + speed = speed < 0 ? -1000 : 1000; + + data[0] = (speed > 1000 || speed < -1000) ? 0x80000000 : 0; + data[0] |= (speed > 1000 || speed < -1500) ? 0x40000000 : 0; + data[1] = (speed < 0); + data[2] = speed < 0 ? 3 : 7; + data[3] = itv->params.video_b_frames; + data[4] = (speed == 1500 || speed == 500) ? itv->speed_mute_audio : 0; + data[5] = 0; + data[6] = 0; + + if (speed == 1500 || speed == -1500) data[0] |= 1; + else if (speed == 2000 || speed == -2000) data[0] |= 2; + else if (speed > -1000 && speed < 0) data[0] |= (-1000 / speed); + else if (speed < 1000 && speed > 0) data[0] |= (1000 / speed); + + /* If not decoding, just change speed setting */ + if (atomic_read(&itv->decoding) > 0) { + int got_sig = 0; + + /* Stop all DMA and decoding activity */ + ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, 0); + + /* Wait for any DMA to finish */ + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + while (itv->i_flags & IVTV_F_I_DMA) { + got_sig = signal_pending(current); + if (got_sig) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + if (got_sig) + return -EINTR; + + /* Change Speed safely */ + ivtv_api(itv, CX2341X_DEC_SET_PLAYBACK_SPEED, 7, data); + IVTV_DEBUG_INFO("Setting Speed to 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x 0x%08x\n", + data[0], data[1], data[2], data[3], data[4], data[5], data[6]); + } + if (single_step) { + speed = (speed < 0) ? -1 : 1; + ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); + } + itv->speed = speed; + return 0; +} + +static int ivtv_validate_speed(int cur_speed, int new_speed) +{ + int fact = new_speed < 0 ? -1 : 1; + int s; + + if (new_speed < 0) new_speed = -new_speed; + if (cur_speed < 0) cur_speed = -cur_speed; + + if (cur_speed <= new_speed) { + if (new_speed > 1500) return fact * 2000; + if (new_speed > 1000) return fact * 1500; + } + else { + if (new_speed >= 2000) return fact * 2000; + if (new_speed >= 1500) return fact * 1500; + if (new_speed >= 1000) return fact * 1000; + } + if (new_speed == 0) return 1000; + if (new_speed == 1 || new_speed == 1000) return fact * new_speed; + + s = new_speed; + new_speed = 1000 / new_speed; + if (1000 / cur_speed == new_speed) + new_speed += (cur_speed < s) ? -1 : 1; + if (new_speed > 60) return 1000 / (fact * 60); + return 1000 / (fact * new_speed); +} + +static int ivtv_video_command(struct ivtv *itv, struct ivtv_open_id *id, + struct video_command *vc, int try) +{ + struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + switch (vc->cmd) { + case VIDEO_CMD_PLAY: { + vc->play.speed = ivtv_validate_speed(itv->speed, vc->play.speed); + if (vc->play.speed < 0) + vc->play.format = VIDEO_PLAY_FMT_GOP; + if (try) break; + + if (ivtv_set_output_mode(itv, OUT_MPG) != OUT_MPG) + return -EBUSY; + return ivtv_start_decoding(id, vc->play.speed); + } + + case VIDEO_CMD_STOP: + if (vc->flags & VIDEO_CMD_STOP_IMMEDIATELY) + vc->stop.pts = 0; + if (try) break; + if (atomic_read(&itv->decoding) == 0) + return 0; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + + itv->output_mode = OUT_NONE; + return ivtv_stop_v4l2_decode_stream(s, vc->flags, vc->stop.pts); + + case VIDEO_CMD_FREEZE: + if (try) break; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + if (atomic_read(&itv->decoding) > 0) { + ivtv_vapi(itv, CX2341X_DEC_PAUSE_PLAYBACK, 1, + (vc->flags & VIDEO_CMD_FREEZE_TO_BLACK) ? 1 : 0); + } + break; + + case VIDEO_CMD_CONTINUE: + if (try) break; + if (itv->output_mode != OUT_MPG) + return -EBUSY; + if (atomic_read(&itv->decoding) > 0) { + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 0); + } + break; + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_itvc(struct ivtv *itv, unsigned int cmd, void *arg) +{ + struct v4l2_register *regs = arg; + unsigned long flags; + volatile u8 __iomem *reg_start; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (regs->reg >= IVTV_REG_OFFSET && regs->reg < IVTV_REG_OFFSET + IVTV_REG_SIZE) + reg_start = itv->reg_mem - IVTV_REG_OFFSET; + else if (itv->has_cx23415 && regs->reg >= IVTV_DECODER_OFFSET && + regs->reg < IVTV_DECODER_OFFSET + IVTV_DECODER_SIZE) + reg_start = itv->dec_mem - IVTV_DECODER_OFFSET; + else if (regs->reg >= 0 && regs->reg < IVTV_ENCODER_SIZE) + reg_start = itv->enc_mem; + else + return -EINVAL; + + spin_lock_irqsave(&ivtv_cards_lock, flags); + if (cmd == VIDIOC_DBG_G_REGISTER) { + regs->val = readl(regs->reg + reg_start); + } else { + writel(regs->val, regs->reg + reg_start); + } + spin_unlock_irqrestore(&ivtv_cards_lock, flags); + return 0; +} + +static int ivtv_get_fmt(struct ivtv *itv, int streamtype, struct v4l2_format *fmt) +{ + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + fmt->fmt.pix.left = itv->main_rect.left; + fmt->fmt.pix.top = itv->main_rect.top; + fmt->fmt.pix.width = itv->main_rect.width; + fmt->fmt.pix.height = itv->main_rect.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (itv->output_mode == OUT_UDMA_YUV) { + switch (itv->yuv_info.lace_mode & IVTV_YUV_MODE_MASK) { + case IVTV_YUV_MODE_INTERLACED: + fmt->fmt.pix.field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) ? + V4L2_FIELD_INTERLACED_BT : V4L2_FIELD_INTERLACED_TB; + break; + case IVTV_YUV_MODE_PROGRESSIVE: + fmt->fmt.pix.field = V4L2_FIELD_NONE; + break; + default: + fmt->fmt.pix.field = V4L2_FIELD_ANY; + break; + } + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } + else if (itv->output_mode == OUT_YUV || + streamtype == IVTV_ENC_STREAM_TYPE_YUV || + streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + fmt->fmt.pix.left = 0; + fmt->fmt.pix.top = 0; + fmt->fmt.pix.width = itv->params.width; + fmt->fmt.pix.height = itv->params.height; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (streamtype == IVTV_ENC_STREAM_TYPE_YUV || + streamtype == IVTV_DEC_STREAM_TYPE_YUV) { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12; + /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */ + fmt->fmt.pix.sizeimage = + fmt->fmt.pix.height * fmt->fmt.pix.width + + fmt->fmt.pix.height * (fmt->fmt.pix.width / 2); + } else { + fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.sizeimage = 128 * 1024; + } + break; + + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + fmt->fmt.win.chromakey = itv->osd_color_key; + fmt->fmt.win.global_alpha = itv->osd_global_alpha; + break; + + case V4L2_BUF_TYPE_VBI_CAPTURE: + fmt->fmt.vbi.sampling_rate = 27000000; + fmt->fmt.vbi.offset = 248; + fmt->fmt.vbi.samples_per_line = itv->vbi.raw_decoder_line_size - 4; + fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + fmt->fmt.vbi.start[0] = itv->vbi.start[0]; + fmt->fmt.vbi.start[1] = itv->vbi.start[1]; + fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = itv->vbi.count; + break; + + case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + if (itv->is_60hz) { + vbifmt->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + vbifmt->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + vbifmt->service_lines[0][23] = V4L2_SLICED_WSS_625; + vbifmt->service_lines[0][16] = V4L2_SLICED_VPS; + } + vbifmt->service_set = get_service_set(vbifmt); + break; + } + + case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: + { + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines)); + + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { + vbifmt->service_set = itv->is_50hz ? V4L2_SLICED_VBI_625 : + V4L2_SLICED_VBI_525; + expand_service_set(vbifmt, itv->is_50hz); + break; + } + + itv->video_dec_func(itv, VIDIOC_G_FMT, fmt); + vbifmt->service_set = get_service_set(vbifmt); + break; + } + case V4L2_BUF_TYPE_VBI_OUTPUT: + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_try_or_set_fmt(struct ivtv *itv, int streamtype, + struct v4l2_format *fmt, int set_fmt) +{ + struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced; + u16 set; + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect r; + int field; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + field = fmt->fmt.pix.field; + r.top = fmt->fmt.pix.top; + r.left = fmt->fmt.pix.left; + r.width = fmt->fmt.pix.width; + r.height = fmt->fmt.pix.height; + ivtv_get_fmt(itv, streamtype, fmt); + if (itv->output_mode != OUT_UDMA_YUV) { + /* TODO: would setting the rect also be valid for this mode? */ + fmt->fmt.pix.top = r.top; + fmt->fmt.pix.left = r.left; + fmt->fmt.pix.width = r.width; + fmt->fmt.pix.height = r.height; + } + if (itv->output_mode == OUT_UDMA_YUV) { + /* TODO: add checks for validity */ + fmt->fmt.pix.field = field; + } + if (set_fmt) { + if (itv->output_mode == OUT_UDMA_YUV) { + switch (field) { + case V4L2_FIELD_NONE: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_PROGRESSIVE; + break; + case V4L2_FIELD_ANY: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_AUTO; + break; + case V4L2_FIELD_INTERLACED_BT: + itv->yuv_info.lace_mode = + IVTV_YUV_MODE_INTERLACED|IVTV_YUV_SYNC_ODD; + break; + case V4L2_FIELD_INTERLACED_TB: + default: + itv->yuv_info.lace_mode = IVTV_YUV_MODE_INTERLACED; + break; + } + itv->yuv_info.lace_sync_field = (itv->yuv_info.lace_mode & IVTV_YUV_SYNC_MASK) == IVTV_YUV_SYNC_EVEN ? 0 : 1; + + /* Force update of yuv registers */ + itv->yuv_info.yuv_forced_update = 1; + return 0; + } + if (!ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + r.width, r.height, r.left, r.top)) + itv->main_rect = r; + else + return -EINVAL; + } + return 0; + } + + if (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY) { + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + if (set_fmt) { + itv->osd_color_key = fmt->fmt.win.chromakey; + itv->osd_global_alpha = fmt->fmt.win.global_alpha; + ivtv_set_osd_alpha(itv); + } + return 0; + } + + /* set window size */ + if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + int w = fmt->fmt.pix.width; + int h = fmt->fmt.pix.height; + + if (w > 720) w = 720; + else if (w < 1) w = 1; + if (h > (itv->is_50hz ? 576 : 480)) h = (itv->is_50hz ? 576 : 480); + else if (h < 2) h = 2; + ivtv_get_fmt(itv, streamtype, fmt); + fmt->fmt.pix.width = w; + fmt->fmt.pix.height = h; + + if (!set_fmt || (itv->params.width == w && itv->params.height == h)) + return 0; + if (atomic_read(&itv->capturing) > 0) + return -EBUSY; + + itv->params.width = w; + itv->params.height = h; + if (w != 720 || h != (itv->is_50hz ? 576 : 480)) + itv->params.video_temporal_filter = 0; + else + itv->params.video_temporal_filter = 8; + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + return ivtv_get_fmt(itv, streamtype, fmt); + } + + /* set raw VBI format */ + if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (set_fmt && streamtype == IVTV_ENC_STREAM_TYPE_VBI && + itv->vbi.sliced_in->service_set && + atomic_read(&itv->capturing) > 0) { + return -EBUSY; + } + if (set_fmt) { + itv->vbi.sliced_in->service_set = 0; + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + } + return ivtv_get_fmt(itv, streamtype, fmt); + } + + /* set sliced VBI output + In principle the user could request that only certain + VBI types are output and that the others are ignored. + I.e., suppress CC in the even fields or only output + WSS and no VPS. Currently though there is no choice. */ + if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) + return ivtv_get_fmt(itv, streamtype, fmt); + + /* any else but sliced VBI capture is an error */ + if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) + return -EINVAL; + + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) + return ivtv_get_fmt(itv, streamtype, fmt); + + /* set sliced VBI capture format */ + vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36; + memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved)); + + if (vbifmt->service_set) + expand_service_set(vbifmt, itv->is_50hz); + set = check_service_set(vbifmt, itv->is_50hz); + vbifmt->service_set = get_service_set(vbifmt); + + if (!set_fmt) + return 0; + if (set == 0) + return -EINVAL; + if (atomic_read(&itv->capturing) > 0 && itv->vbi.sliced_in->service_set == 0) { + return -EBUSY; + } + itv->video_dec_func(itv, VIDIOC_S_FMT, fmt); + memcpy(itv->vbi.sliced_in, vbifmt, sizeof(*itv->vbi.sliced_in)); + return 0; +} + +static int ivtv_internal_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + struct v4l2_register *reg = arg; + + switch (cmd) { + /* ioctls to allow direct access to the encoder registers for testing */ + case VIDIOC_DBG_G_REGISTER: + IVTV_DEBUG_IOCTL("VIDIOC_DBG_G_REGISTER\n"); + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + + case VIDIOC_DBG_S_REGISTER: + IVTV_DEBUG_IOCTL("VIDIOC_DBG_S_REGISTER\n"); + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) + return ivtv_itvc(itv, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + + case VIDIOC_G_CHIP_IDENT: { + struct v4l2_chip_ident *chip = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_CHIP_IDENT\n"); + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + if (reg->match_type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) { + struct v4l2_chip_ident *chip = arg; + + chip->ident = itv->has_cx23415 ? V4L2_IDENT_CX23415 : V4L2_IDENT_CX23416; + } + return 0; + } + if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER) + return ivtv_i2c_id(itv, reg->match_chip, cmd, arg); + if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR) + return ivtv_call_i2c_client(itv, reg->match_chip, cmd, arg); + return -EINVAL; + } + + case VIDIOC_INT_S_AUDIO_ROUTING: { + struct v4l2_routing *route = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING\n"); + ivtv_audio_set_route(itv, route); + break; + } + + case VIDIOC_INT_RESET: + IVTV_DEBUG_IOCTL("VIDIOC_INT_RESET\n"); + ivtv_reset_ir_gpio(itv); + break; + + default: + return -EINVAL; + } + return 0; +} + +int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = NULL; + + if (filp) id = (struct ivtv_open_id *)filp->private_data; + + switch (cmd) { + case VIDIOC_QUERYCAP:{ + struct v4l2_capability *vcap = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_QUERYCAP\n"); + + memset(vcap, 0, sizeof(*vcap)); + strcpy(vcap->driver, IVTV_DRIVER_NAME); /* driver name */ + strcpy(vcap->card, itv->card_name); /* card type */ + strcpy(vcap->bus_info, pci_name(itv->dev)); /* bus info... */ + vcap->version = IVTV_DRIVER_VERSION; /* version */ + vcap->capabilities = itv->v4l2_cap; /* capabilities */ + + /* reserved.. must set to 0! */ + vcap->reserved[0] = vcap->reserved[1] = + vcap->reserved[2] = vcap->reserved[3] = 0; + break; + } + + case VIDIOC_ENUMAUDIO:{ + struct v4l2_audio *vin = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_ENUMAUDIO\n"); + + return ivtv_get_audio_input(itv, vin->index, vin); + } + + case VIDIOC_G_AUDIO:{ + struct v4l2_audio *vin = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_AUDIO\n"); + vin->index = itv->audio_input; + return ivtv_get_audio_input(itv, vin->index, vin); + } + + case VIDIOC_S_AUDIO:{ + struct v4l2_audio *vout = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_AUDIO\n"); + + if (vout->index >= itv->nof_audio_inputs) + return -EINVAL; + itv->audio_input = vout->index; + ivtv_audio_set_io(itv); + break; + } + + case VIDIOC_ENUMAUDOUT:{ + struct v4l2_audioout *vin = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_ENUMAUDOUT\n"); + + /* set it to defaults from our table */ + return ivtv_get_audio_output(itv, vin->index, vin); + } + + case VIDIOC_G_AUDOUT:{ + struct v4l2_audioout *vin = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_AUDOUT\n"); + vin->index = 0; + return ivtv_get_audio_output(itv, vin->index, vin); + } + + case VIDIOC_S_AUDOUT:{ + struct v4l2_audioout *vout = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_AUDOUT\n"); + + return ivtv_get_audio_output(itv, vout->index, vout); + } + + case VIDIOC_ENUMINPUT:{ + struct v4l2_input *vin = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_ENUMINPUT\n"); + + /* set it to defaults from our table */ + return ivtv_get_input(itv, vin->index, vin); + } + + case VIDIOC_ENUMOUTPUT:{ + struct v4l2_output *vout = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_ENUMOUTPUT\n"); + + return ivtv_get_output(itv, vout->index, vout); + } + + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: { + struct v4l2_format *fmt = arg; + + if (cmd == VIDIOC_S_FMT) { + IVTV_DEBUG_IOCTL("VIDIOC_S_FMT\n"); + } else { + IVTV_DEBUG_IOCTL("VIDIOC_TRY_FMT\n"); + } + return ivtv_try_or_set_fmt(itv, id->type, fmt, cmd == VIDIOC_S_FMT); + } + + case VIDIOC_G_FMT: { + struct v4l2_format *fmt = arg; + int type = fmt->type; + + IVTV_DEBUG_IOCTL("VIDIOC_G_FMT\n"); + memset(fmt, 0, sizeof(*fmt)); + fmt->type = type; + return ivtv_get_fmt(itv, id->type, fmt); + } + + case VIDIOC_S_CROP: { + struct v4l2_crop *crop = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_CROP\n"); + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return itv->video_dec_func(itv, VIDIOC_S_CROP, arg); + } + + case VIDIOC_G_CROP: { + struct v4l2_crop *crop = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_CROP\n"); + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + return itv->video_dec_func(itv, VIDIOC_G_CROP, arg); + } + + case VIDIOC_ENUM_FMT: { + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12, + { 0, 0, 0, 0 } + }, + { 1, 0, V4L2_FMT_FLAG_COMPRESSED, + "MPEG", V4L2_PIX_FMT_MPEG, + { 0, 0, 0, 0 } + } + }; + struct v4l2_fmtdesc *fmt = arg; + enum v4l2_buf_type type = fmt->type; + + switch (type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + case V4L2_BUF_TYPE_VIDEO_OUTPUT: + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + break; + default: + return -EINVAL; + } + if (fmt->index > 1) + return -EINVAL; + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; + } + + case VIDIOC_G_INPUT:{ + IVTV_DEBUG_IOCTL("VIDIOC_G_INPUT\n"); + + *(int *)arg = itv->active_input; + break; + } + + case VIDIOC_S_INPUT:{ + int inp = *(int *)arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_INPUT\n"); + + if (inp < 0 || inp >= itv->nof_inputs) + return -EINVAL; + + if (inp == itv->active_input) { + IVTV_DEBUG_INFO("Input unchanged\n"); + break; + } + IVTV_DEBUG_INFO("Changing input from %d to %d\n", + itv->active_input, inp); + + itv->active_input = inp; + /* Set the audio input to whatever is appropriate for the + input type. */ + itv->audio_input = itv->card->video_inputs[inp].audio_index; + + /* prevent others from messing with the streams until + we're finished changing inputs. */ + ivtv_mute(itv); + ivtv_video_set_io(itv); + ivtv_audio_set_io(itv); + ivtv_unmute(itv); + break; + } + + case VIDIOC_G_OUTPUT:{ + IVTV_DEBUG_IOCTL("VIDIOC_G_OUTPUT\n"); + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + *(int *)arg = itv->active_output; + break; + } + + case VIDIOC_S_OUTPUT:{ + int outp = *(int *)arg; + struct v4l2_routing route; + + IVTV_DEBUG_IOCTL("VIDIOC_S_OUTPUT\n"); + + if (outp >= itv->card->nof_outputs) + return -EINVAL; + + if (outp == itv->active_output) { + IVTV_DEBUG_INFO("Output unchanged\n"); + break; + } + IVTV_DEBUG_INFO("Changing output from %d to %d\n", + itv->active_output, outp); + + itv->active_output = outp; + route.input = SAA7127_INPUT_TYPE_NORMAL; + route.output = itv->card->video_outputs[outp].video_output; + ivtv_saa7127(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + break; + } + + case VIDIOC_G_FREQUENCY:{ + struct v4l2_frequency *vf = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_FREQUENCY\n"); + + if (vf->tuner != 0) + return -EINVAL; + ivtv_call_i2c_clients(itv, cmd, arg); + break; + } + + case VIDIOC_S_FREQUENCY:{ + struct v4l2_frequency vf = *(struct v4l2_frequency *)arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_FREQUENCY\n"); + + if (vf.tuner != 0) + return -EINVAL; + + ivtv_mute(itv); + IVTV_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency); + ivtv_call_i2c_clients(itv, cmd, &vf); + ivtv_unmute(itv); + break; + } + + case VIDIOC_ENUMSTD:{ + struct v4l2_standard *vs = arg; + int idx = vs->index; + + IVTV_DEBUG_IOCTL("VIDIOC_ENUMSTD\n"); + + if (idx < 0 || idx >= ARRAY_SIZE(enum_stds)) + return -EINVAL; + + *vs = (enum_stds[idx].std & V4L2_STD_525_60) ? + ivtv_std_60hz : ivtv_std_50hz; + vs->index = idx; + vs->id = enum_stds[idx].std; + strcpy(vs->name, enum_stds[idx].name); + break; + } + + case VIDIOC_G_STD:{ + IVTV_DEBUG_IOCTL("VIDIOC_G_STD\n"); + *(v4l2_std_id *) arg = itv->std; + break; + } + + case VIDIOC_S_STD: { + v4l2_std_id std = *(v4l2_std_id *) arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_STD\n"); + + if ((std & V4L2_STD_ALL) == 0) + return -EINVAL; + + if (std == itv->std) + break; + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) || + atomic_read(&itv->capturing) > 0 || + atomic_read(&itv->decoding) > 0) { + /* Switching standard would turn off the radio or mess + with already running streams, prevent that by + returning EBUSY. */ + return -EBUSY; + } + + itv->std = std; + itv->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0; + itv->params.is_50hz = itv->is_50hz = !itv->is_60hz; + itv->params.width = 720; + itv->params.height = itv->is_50hz ? 576 : 480; + itv->vbi.count = itv->is_50hz ? 18 : 12; + itv->vbi.start[0] = itv->is_50hz ? 6 : 10; + itv->vbi.start[1] = itv->is_50hz ? 318 : 273; + if (itv->hw_flags & IVTV_HW_CX25840) { + itv->vbi.sliced_decoder_line_size = itv->is_60hz ? 272 : 284; + } + IVTV_DEBUG_INFO("Switching standard to %llx.\n", itv->std); + + /* Tuner */ + ivtv_call_i2c_clients(itv, VIDIOC_S_STD, &itv->std); + + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + /* set display standard */ + itv->std_out = std; + itv->is_out_60hz = itv->is_60hz; + itv->is_out_50hz = itv->is_50hz; + ivtv_call_i2c_clients(itv, VIDIOC_INT_S_STD_OUTPUT, &itv->std_out); + ivtv_vapi(itv, CX2341X_DEC_SET_STANDARD, 1, itv->is_out_50hz); + itv->main_rect.left = itv->main_rect.top = 0; + itv->main_rect.width = 720; + itv->main_rect.height = itv->params.height; + ivtv_vapi(itv, CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, 4, + 720, itv->main_rect.height, 0, 0); + } + break; + } + + case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */ + struct v4l2_tuner *vt = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_TUNER\n"); + + if (vt->index != 0) + return -EINVAL; + + ivtv_call_i2c_clients(itv, VIDIOC_S_TUNER, vt); + break; + } + + case VIDIOC_G_TUNER: { + struct v4l2_tuner *vt = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_TUNER\n"); + + if (vt->index != 0) + return -EINVAL; + + memset(vt, 0, sizeof(*vt)); + ivtv_call_i2c_clients(itv, VIDIOC_G_TUNER, vt); + + if (test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags)) { + strcpy(vt->name, "ivtv Radio Tuner"); + vt->type = V4L2_TUNER_RADIO; + } else { + strcpy(vt->name, "ivtv TV Tuner"); + vt->type = V4L2_TUNER_ANALOG_TV; + } + break; + } + + case VIDIOC_G_SLICED_VBI_CAP: { + struct v4l2_sliced_vbi_cap *cap = arg; + int set = itv->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525; + int f, l; + enum v4l2_buf_type type = cap->type; + + IVTV_DEBUG_IOCTL("VIDIOC_G_SLICED_VBI_CAP\n"); + memset(cap, 0, sizeof(*cap)); + cap->type = type; + if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) { + for (f = 0; f < 2; f++) { + for (l = 0; l < 24; l++) { + if (valid_service_line(f, l, itv->is_50hz)) { + cap->service_lines[f][l] = set; + } + } + } + return 0; + } + if (type == V4L2_BUF_TYPE_SLICED_VBI_OUTPUT) { + if (!(itv->v4l2_cap & V4L2_CAP_SLICED_VBI_OUTPUT)) + return -EINVAL; + if (itv->is_60hz) { + cap->service_lines[0][21] = V4L2_SLICED_CAPTION_525; + cap->service_lines[1][21] = V4L2_SLICED_CAPTION_525; + } else { + cap->service_lines[0][23] = V4L2_SLICED_WSS_625; + cap->service_lines[0][16] = V4L2_SLICED_VPS; + } + return 0; + } + return -EINVAL; + } + + case VIDIOC_LOG_STATUS: + { + int has_output = itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT; + struct v4l2_input vidin; + struct v4l2_audio audin; + int i; + + IVTV_INFO("================= START STATUS CARD #%d =================\n", itv->num); + if (itv->hw_flags & IVTV_HW_TVEEPROM) { + struct tveeprom tv; + + ivtv_read_eeprom(itv, &tv); + } + ivtv_call_i2c_clients(itv, VIDIOC_LOG_STATUS, NULL); + ivtv_get_input(itv, itv->active_input, &vidin); + ivtv_get_audio_input(itv, itv->audio_input, &audin); + IVTV_INFO("Video Input: %s\n", vidin.name); + IVTV_INFO("Audio Input: %s\n", audin.name); + if (has_output) { + struct v4l2_output vidout; + struct v4l2_audioout audout; + int mode = itv->output_mode; + static const char * const output_modes[] = { + "None", + "MPEG Streaming", + "YUV Streaming", + "YUV Frames", + "Passthrough", + }; + + ivtv_get_output(itv, itv->active_output, &vidout); + ivtv_get_audio_output(itv, 0, &audout); + IVTV_INFO("Video Output: %s\n", vidout.name); + IVTV_INFO("Audio Output: %s\n", audout.name); + if (mode < 0 || mode > OUT_PASSTHROUGH) + mode = OUT_NONE; + IVTV_INFO("Output Mode: %s\n", output_modes[mode]); + } + IVTV_INFO("Tuner: %s\n", + test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV"); + cx2341x_log_status(&itv->params, itv->name); + IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags); + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + struct ivtv_stream *s = &itv->streams[i]; + + if (s->v4l2dev == NULL || s->buffers == 0) + continue; + IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, + (s->buffers - s->q_free.buffers) * 100 / s->buffers, + (s->buffers * s->buf_size) / 1024, s->buffers); + } + IVTV_INFO("Read MPEG/VBI: %lld/%lld bytes\n", itv->mpg_data_received, itv->vbi_data_inserted); + IVTV_INFO("================== END STATUS CARD #%d ==================\n", itv->num); + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_ivtv_ioctls(struct file *filp, unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + int nonblocking = filp->f_flags & O_NONBLOCK; + struct ivtv_stream *s = &itv->streams[id->type]; + + switch (cmd) { + case IVTV_IOC_DMA_FRAME: { + struct ivtv_dma_frame *args = arg; + + IVTV_DEBUG_IOCTL("IVTV_IOC_DMA_FRAME\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + if (args->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return -EINVAL; + if (itv->output_mode == OUT_UDMA_YUV && args->y_source == NULL) + return 0; + if (ivtv_claim_stream(id, id->type)) { + return -EBUSY; + } + if (ivtv_set_output_mode(itv, OUT_UDMA_YUV) != OUT_UDMA_YUV) { + ivtv_release_stream(s); + return -EBUSY; + } + if (args->y_source == NULL) + return 0; + return ivtv_yuv_prep_frame(itv, args); + } + + case VIDEO_GET_PTS: { + u32 data[CX2341X_MBOX_MAX_DATA]; + u64 *pts = arg; + + IVTV_DEBUG_IOCTL("VIDEO_GET_PTS\n"); + if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { + *pts = s->dma_pts; + break; + } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *pts = (u64) ((u64)itv->last_dec_timing[2] << 32) | + (u64)itv->last_dec_timing[1]; + break; + } + *pts = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *pts = (u64) ((u64) data[2] << 32) | (u64) data[1]; + /*timing->scr = (u64) (((u64) data[4] << 32) | (u64) (data[3]));*/ + } + break; + } + + case VIDEO_GET_FRAME_COUNT: { + u32 data[CX2341X_MBOX_MAX_DATA]; + u64 *frame = arg; + + IVTV_DEBUG_IOCTL("VIDEO_GET_FRAME_COUNT\n"); + if (s->type < IVTV_DEC_STREAM_TYPE_MPG) { + *frame = 0; + break; + } + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + + if (test_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags)) { + *frame = itv->last_dec_timing[0]; + break; + } + *frame = 0; + if (atomic_read(&itv->decoding)) { + if (ivtv_api(itv, CX2341X_DEC_GET_TIMING_INFO, 5, data)) { + IVTV_DEBUG_WARN("GET_TIMING: couldn't read clock\n"); + return -EIO; + } + memcpy(itv->last_dec_timing, data, sizeof(itv->last_dec_timing)); + set_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + *frame = data[0]; + } + break; + } + + case VIDEO_PLAY: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_PLAY\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_PLAY; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_STOP: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_STOP\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_STOP; + vc.flags = VIDEO_CMD_STOP_TO_BLACK | VIDEO_CMD_STOP_IMMEDIATELY; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_FREEZE: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_FREEZE\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_FREEZE; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_CONTINUE: { + struct video_command vc; + + IVTV_DEBUG_IOCTL("VIDEO_CONTINUE\n"); + memset(&vc, 0, sizeof(vc)); + vc.cmd = VIDEO_CMD_CONTINUE; + return ivtv_video_command(itv, id, &vc, 0); + } + + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: { + struct video_command *vc = arg; + int try = (cmd == VIDEO_TRY_COMMAND); + + if (try) + IVTV_DEBUG_IOCTL("VIDEO_TRY_COMMAND\n"); + else + IVTV_DEBUG_IOCTL("VIDEO_COMMAND\n"); + return ivtv_video_command(itv, id, vc, try); + } + + case VIDEO_GET_EVENT: { + struct video_event *ev = arg; + DEFINE_WAIT(wait); + + IVTV_DEBUG_IOCTL("VIDEO_GET_EVENT\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + memset(ev, 0, sizeof(*ev)); + set_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + + while (1) { + if (test_and_clear_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags)) + ev->type = VIDEO_EVENT_DECODER_STOPPED; + else if (test_and_clear_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags)) { + ev->type = VIDEO_EVENT_VSYNC; + ev->timestamp = test_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags) ? + 1 : 0; + clear_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags); + } + if (ev->type) + return 0; + if (nonblocking) + return -EAGAIN; + /* wait for event */ + prepare_to_wait(&itv->event_waitq, &wait, TASK_INTERRUPTIBLE); + if ((itv->i_flags & (IVTV_F_I_EV_DEC_STOPPED|IVTV_F_I_EV_VSYNC)) == 0) + schedule(); + finish_wait(&itv->event_waitq, &wait); + if (signal_pending(current)) { + /* return if a signal was received */ + IVTV_DEBUG_INFO("User stopped wait for event\n"); + return -EINTR; + } + } + break; + } + + case VIDIOC_G_ENC_INDEX: { + struct v4l2_enc_idx *idx = arg; + int i; + + IVTV_DEBUG_IOCTL("VIDIOC_G_ENC_INDEX\n"); + idx->entries = (itv->pgm_info_write_idx + IVTV_MAX_PGM_INDEX - itv->pgm_info_read_idx) % + IVTV_MAX_PGM_INDEX; + if (idx->entries > V4L2_ENC_IDX_ENTRIES) + idx->entries = V4L2_ENC_IDX_ENTRIES; + for (i = 0; i < idx->entries; i++) { + idx->entry[i] = itv->pgm_info[(itv->pgm_info_read_idx + i) % IVTV_MAX_PGM_INDEX]; + } + itv->pgm_info_read_idx = (itv->pgm_info_read_idx + idx->entries) % IVTV_MAX_PGM_INDEX; + break; + } + + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: { + struct v4l2_encoder_cmd *enc = arg; + int try = cmd == VIDIOC_TRY_ENCODER_CMD; + + if (try) + IVTV_DEBUG_IOCTL("VIDIOC_TRY_ENCODER_CMD\n"); + else + IVTV_DEBUG_IOCTL("VIDIOC_ENCODER_CMD\n"); + switch (enc->cmd) { + case V4L2_ENC_CMD_START: + return ivtv_start_capture(id); + + case V4L2_ENC_CMD_STOP: + ivtv_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END); + return 0; + + case V4L2_ENC_CMD_PAUSE: + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (test_and_set_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + ivtv_mute(itv); + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 0); + break; + + case V4L2_ENC_CMD_RESUME: + if (!atomic_read(&itv->capturing)) + return -EPERM; + if (!test_and_clear_bit(IVTV_F_I_ENC_PAUSED, &itv->i_flags)) + return 0; + ivtv_vapi(itv, CX2341X_ENC_PAUSE_ENCODER, 1, 1); + ivtv_unmute(itv); + break; + } + break; + } + + case VIDIOC_G_FBUF: { + struct v4l2_framebuffer *fb = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_G_FBUF\n"); + memset(fb, 0, sizeof(*fb)); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + break; + fb->capability = V4L2_FBUF_CAP_EXTERNOVERLAY | V4L2_FBUF_CAP_CHROMAKEY | + V4L2_FBUF_CAP_LOCAL_ALPHA | V4L2_FBUF_CAP_GLOBAL_ALPHA; + fb->fmt.pixelformat = itv->osd_pixelformat; + fb->fmt.width = itv->osd_rect.width; + fb->fmt.height = itv->osd_rect.height; + fb->fmt.left = itv->osd_rect.left; + fb->fmt.top = itv->osd_rect.top; + fb->base = (void *)itv->osd_video_pbase; + if (itv->osd_global_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_GLOBAL_ALPHA; + if (itv->osd_local_alpha_state) + fb->flags |= V4L2_FBUF_FLAG_LOCAL_ALPHA; + if (itv->osd_color_key_state) + fb->flags |= V4L2_FBUF_FLAG_CHROMAKEY; + break; + } + + case VIDIOC_S_FBUF: { + struct v4l2_framebuffer *fb = arg; + + IVTV_DEBUG_IOCTL("VIDIOC_S_FBUF\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT_OVERLAY)) + break; + itv->osd_global_alpha_state = (fb->flags & V4L2_FBUF_FLAG_GLOBAL_ALPHA) != 0; + itv->osd_local_alpha_state = (fb->flags & V4L2_FBUF_FLAG_LOCAL_ALPHA) != 0; + itv->osd_color_key_state = (fb->flags & V4L2_FBUF_FLAG_CHROMAKEY) != 0; + break; + } + + default: + return -EINVAL; + } + return 0; +} + +static int ivtv_v4l2_do_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, void *arg) +{ + struct ivtv_open_id *id = (struct ivtv_open_id *)filp->private_data; + struct ivtv *itv = id->itv; + + IVTV_DEBUG_IOCTL("v4l2 ioctl 0x%08x\n", cmd); + + switch (cmd) { + case VIDIOC_DBG_G_REGISTER: + case VIDIOC_DBG_S_REGISTER: + case VIDIOC_G_CHIP_IDENT: + case VIDIOC_INT_S_AUDIO_ROUTING: + case VIDIOC_INT_RESET: + return ivtv_internal_ioctls(filp, cmd, arg); + + case VIDIOC_QUERYCAP: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_OUTPUT: + case VIDIOC_S_OUTPUT: + case VIDIOC_G_FMT: + case VIDIOC_S_FMT: + case VIDIOC_TRY_FMT: + case VIDIOC_ENUM_FMT: + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_S_TUNER: + case VIDIOC_G_TUNER: + case VIDIOC_ENUMAUDIO: + case VIDIOC_S_AUDIO: + case VIDIOC_G_AUDIO: + case VIDIOC_ENUMAUDOUT: + case VIDIOC_S_AUDOUT: + case VIDIOC_G_AUDOUT: + case VIDIOC_G_SLICED_VBI_CAP: + case VIDIOC_LOG_STATUS: + return ivtv_v4l2_ioctls(itv, filp, cmd, arg); + + case VIDIOC_QUERYMENU: + case VIDIOC_QUERYCTRL: + case VIDIOC_S_CTRL: + case VIDIOC_G_CTRL: + case VIDIOC_S_EXT_CTRLS: + case VIDIOC_G_EXT_CTRLS: + case VIDIOC_TRY_EXT_CTRLS: + return ivtv_control_ioctls(itv, cmd, arg); + + case IVTV_IOC_DMA_FRAME: + case VIDEO_GET_PTS: + case VIDEO_GET_FRAME_COUNT: + case VIDEO_GET_EVENT: + case VIDEO_PLAY: + case VIDEO_STOP: + case VIDEO_FREEZE: + case VIDEO_CONTINUE: + case VIDEO_COMMAND: + case VIDEO_TRY_COMMAND: + case VIDIOC_G_ENC_INDEX: + case VIDIOC_ENCODER_CMD: + case VIDIOC_TRY_ENCODER_CMD: + case VIDIOC_G_FBUF: + case VIDIOC_S_FBUF: + return ivtv_ivtv_ioctls(filp, cmd, arg); + + case 0x00005401: /* Handle isatty() calls */ + return -EINVAL; + default: + return v4l_compat_translate_ioctl(inode, filp, cmd, arg, + ivtv_v4l2_do_ioctl); + } + 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; + + /* Filter dvb ioctls that cannot be handled by video_usercopy */ + switch (cmd) { + case VIDEO_SELECT_SOURCE: + IVTV_DEBUG_IOCTL("VIDEO_SELECT_SOURCE\n"); + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return -EINVAL; + return ivtv_passthrough_mode(itv, arg == VIDEO_SOURCE_DEMUX); + + case AUDIO_SET_MUTE: + IVTV_DEBUG_IOCTL("AUDIO_SET_MUTE\n"); + itv->speed_mute_audio = arg; + return 0; + + case AUDIO_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_CHANNEL_SELECT\n"); + if (arg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_stereo_mode = arg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + case AUDIO_BILINGUAL_CHANNEL_SELECT: + IVTV_DEBUG_IOCTL("AUDIO_BILINGUAL_CHANNEL_SELECT\n"); + if (arg > AUDIO_STEREO_SWAPPED) + return -EINVAL; + itv->audio_bilingual_mode = arg; + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + return 0; + + default: + break; + } + return video_usercopy(inode, filp, cmd, arg, ivtv_v4l2_do_ioctl); +} diff --git a/drivers/media/video/ivtv/ivtv-ioctl.h b/drivers/media/video/ivtv/ivtv-ioctl.h new file mode 100644 index 000000000000..cbccf7a9f65c --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-ioctl.h @@ -0,0 +1,28 @@ +/* + ioctl system call + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +u16 service2vbi(int type); +void expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal); +u16 get_service_set(struct v4l2_sliced_vbi_format *fmt); +int ivtv_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +int ivtv_v4l2_ioctls(struct ivtv *itv, struct file *filp, unsigned int cmd, void *arg); +void ivtv_set_osd_alpha(struct ivtv *itv); +int ivtv_set_speed(struct ivtv *itv, int speed); diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c new file mode 100644 index 000000000000..0656e18b7c7e --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-irq.c @@ -0,0 +1,818 @@ +/* interrupt handling + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-firmware.h" +#include "ivtv-fileops.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" +#include "ivtv-ioctl.h" +#include "ivtv-mailbox.h" +#include "ivtv-vbi.h" + +#define DMA_MAGIC_COOKIE 0x000001fe + +#define SLICED_VBI_PIO 1 + +static void ivtv_dma_dec_start(struct ivtv_stream *s); + +static const int ivtv_stream_map[] = { + IVTV_ENC_STREAM_TYPE_MPG, + IVTV_ENC_STREAM_TYPE_YUV, + IVTV_ENC_STREAM_TYPE_PCM, + IVTV_ENC_STREAM_TYPE_VBI, +}; + +static inline int ivtv_use_pio(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + + return s->dma == PCI_DMA_NONE || + (SLICED_VBI_PIO && s->type == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set); +} + +/* Determine the required DMA size, setup enough buffers in the predma queue and + actually copy the data from the card to the buffers in case a PIO transfer is + required for this stream. + */ +static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf; + struct list_head *p; + u32 bytes_needed = 0; + u32 offset, size; + u32 UVoffset = 0, UVsize = 0; + int skip_bufs = s->q_predma.buffers; + int idx = s->SG_length; + int rc; + + /* sanity checks */ + if (s->v4l2dev == NULL) { + IVTV_DEBUG_WARN("Stream %s not started\n", s->name); + return -1; + } + if (!test_bit(IVTV_F_S_CLAIMED, &s->s_flags)) { + IVTV_DEBUG_WARN("Stream %s not open\n", s->name); + return -1; + } + + /* determine offset, size and PTS for the various streams */ + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_MPG: + offset = data[1]; + size = data[2]; + s->dma_pts = 0; + break; + + case IVTV_ENC_STREAM_TYPE_YUV: + offset = data[1]; + size = data[2]; + UVoffset = data[3]; + UVsize = data[4]; + s->dma_pts = ((u64) data[5] << 32) | data[6]; + break; + + case IVTV_ENC_STREAM_TYPE_PCM: + offset = data[1] + 12; + size = data[2] - 12; + s->dma_pts = read_dec(offset - 8) | + ((u64)(read_dec(offset - 12)) << 32); + if (itv->has_cx23415) + offset += IVTV_DECODER_OFFSET; + break; + + case IVTV_ENC_STREAM_TYPE_VBI: + size = itv->vbi.enc_size * itv->vbi.fpi; + offset = read_enc(itv->vbi.enc_start - 4) + 12; + if (offset == 12) { + IVTV_DEBUG_INFO("VBI offset == 0\n"); + return -1; + } + s->dma_pts = read_enc(offset - 4) | ((u64)read_enc(offset - 8) << 32); + break; + + case IVTV_DEC_STREAM_TYPE_VBI: + size = read_dec(itv->vbi.dec_start + 4) + 8; + offset = read_dec(itv->vbi.dec_start) + itv->vbi.dec_start; + s->dma_pts = 0; + offset += IVTV_DECODER_OFFSET; + break; + default: + /* shouldn't happen */ + return -1; + } + + /* if this is the start of the DMA then fill in the magic cookie */ + if (s->SG_length == 0) { + if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || + s->type == IVTV_DEC_STREAM_TYPE_VBI)) { + s->dma_backup = read_dec(offset - IVTV_DECODER_OFFSET); + write_dec_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset - IVTV_DECODER_OFFSET); + } + else { + s->dma_backup = read_enc(offset); + write_enc_sync(cpu_to_le32(DMA_MAGIC_COOKIE), offset); + } + s->dma_offset = offset; + } + + bytes_needed = size; + if (s->type == IVTV_ENC_STREAM_TYPE_YUV) { + /* The size for the Y samples needs to be rounded upwards to a + multiple of the buf_size. The UV samples then start in the + next buffer. */ + bytes_needed = s->buf_size * ((bytes_needed + s->buf_size - 1) / s->buf_size); + bytes_needed += UVsize; + } + + IVTV_DEBUG_DMA("%s %s: 0x%08x bytes at 0x%08x\n", + ivtv_use_pio(s) ? "PIO" : "DMA", s->name, bytes_needed, offset); + + rc = ivtv_queue_move(s, &s->q_free, &s->q_full, &s->q_predma, bytes_needed); + if (rc < 0) { /* Insufficient buffers */ + IVTV_DEBUG_WARN("Cannot obtain %d bytes for %s data transfer\n", + bytes_needed, s->name); + return -1; + } + if (rc && !s->buffers_stolen && (s->s_flags & IVTV_F_S_APPL_IO)) { + IVTV_WARN("All %s stream buffers are full. Dropping data.\n", s->name); + IVTV_WARN("Cause: the application is not reading fast enough.\n"); + } + s->buffers_stolen = rc; + + /* got the buffers, now fill in SGarray (DMA) or copy the data from the card + to the buffers (PIO). */ + buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); + memset(buf->buf, 0, 128); + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + + if (skip_bufs-- > 0) + continue; + if (!ivtv_use_pio(s)) { + s->SGarray[idx].dst = cpu_to_le32(buf->dma_handle); + s->SGarray[idx].src = cpu_to_le32(offset); + s->SGarray[idx].size = cpu_to_le32(s->buf_size); + } + buf->bytesused = (size < s->buf_size) ? size : s->buf_size; + + /* If PIO, then copy the data from the card to the buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_VBI) { + memcpy_fromio(buf->buf, itv->dec_mem + offset - IVTV_DECODER_OFFSET, buf->bytesused); + } + else if (ivtv_use_pio(s)) { + memcpy_fromio(buf->buf, itv->enc_mem + offset, buf->bytesused); + } + + s->q_predma.bytesused += buf->bytesused; + size -= buf->bytesused; + offset += s->buf_size; + + /* Sync SG buffers */ + ivtv_buf_sync_for_device(s, buf); + + if (size == 0) { /* YUV */ + /* process the UV section */ + offset = UVoffset; + size = UVsize; + } + idx++; + } + s->SG_length = idx; + return 0; +} + +static void dma_post(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf = NULL; + struct list_head *p; + u32 offset; + u32 *u32buf; + int x = 0; + + if (ivtv_use_pio(s)) { + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + s->SG_length = 0; + } + IVTV_DEBUG_DMA("%s %s completed (%x)\n", ivtv_use_pio(s) ? "PIO" : "DMA", + s->name, s->dma_offset); + list_for_each(p, &s->q_dma.list) { + buf = list_entry(p, struct ivtv_buffer, list); + u32buf = (u32 *)buf->buf; + + /* Sync Buffer */ + ivtv_buf_sync_for_cpu(s, buf); + + if (x == 0) { + offset = s->dma_last_offset; + if (u32buf[offset / 4] != DMA_MAGIC_COOKIE) + { + for (offset = 0; offset < 64; offset++) { + if (u32buf[offset] == DMA_MAGIC_COOKIE) { + break; + } + } + offset *= 4; + if (offset == 256) { + IVTV_DEBUG_WARN("%s: Couldn't find start of buffer within the first 256 bytes\n", s->name); + offset = s->dma_last_offset; + } + if (s->dma_last_offset != offset) + IVTV_DEBUG_WARN("%s: offset %d -> %d\n", s->name, s->dma_last_offset, offset); + s->dma_last_offset = offset; + } + if (itv->has_cx23415 && (s->type == IVTV_ENC_STREAM_TYPE_PCM || + s->type == IVTV_DEC_STREAM_TYPE_VBI)) { + write_dec_sync(0, s->dma_offset - IVTV_DECODER_OFFSET); + } + else { + write_enc_sync(0, s->dma_offset); + } + if (offset) { + buf->bytesused -= offset; + memcpy(buf->buf, buf->buf + offset, buf->bytesused + offset); + } + *u32buf = cpu_to_le32(s->dma_backup); + } + x++; + /* flag byteswap ABCD -> DCBA for MPG & VBI data outside irq */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG || + s->type == IVTV_ENC_STREAM_TYPE_VBI) + set_bit(IVTV_F_B_NEED_BUF_SWAP, &buf->b_flags); + } + if (buf) + buf->bytesused += s->dma_last_offset; + if (buf && s->type == IVTV_DEC_STREAM_TYPE_VBI) { + /* Parse and Groom VBI Data */ + s->q_dma.bytesused -= buf->bytesused; + ivtv_process_vbi_data(itv, buf, 0, s->type); + s->q_dma.bytesused += buf->bytesused; + if (s->id == -1) { + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); + return; + } + } + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_full, s->q_dma.bytesused); + if (s->id != -1) + wake_up(&s->waitq); +} + +void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock) +{ + struct ivtv *itv = s->itv; + struct ivtv_buffer *buf; + struct list_head *p; + u32 y_size = itv->params.height * itv->params.width; + u32 uv_offset = offset + IVTV_YUV_BUFFER_UV_OFFSET; + int y_done = 0; + int bytes_written = 0; + unsigned long flags = 0; + int idx = 0; + + IVTV_DEBUG_DMA("DEC PREPARE DMA %s: %08x %08x\n", s->name, s->q_predma.bytesused, offset); + buf = list_entry(s->q_predma.list.next, struct ivtv_buffer, list); + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + + /* YUV UV Offset from Y Buffer */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV && !y_done && bytes_written >= y_size) { + offset = uv_offset; + y_done = 1; + } + s->SGarray[idx].src = cpu_to_le32(buf->dma_handle); + s->SGarray[idx].dst = cpu_to_le32(offset); + s->SGarray[idx].size = cpu_to_le32(buf->bytesused); + + offset += buf->bytesused; + bytes_written += buf->bytesused; + + /* Sync SG buffers */ + ivtv_buf_sync_for_device(s, buf); + idx++; + } + s->SG_length = idx; + + /* Mark last buffer size for Interrupt flag */ + s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); + + /* Sync Hardware SG List of buffers */ + ivtv_stream_sync_for_device(s); + if (lock) + spin_lock_irqsave(&itv->dma_reg_lock, flags); + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) { + ivtv_dma_dec_start(s); + } + else { + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } + if (lock) + spin_unlock_irqrestore(&itv->dma_reg_lock, flags); +} + +/* start the encoder DMA */ +static void ivtv_dma_enc_start(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + struct ivtv_stream *s_vbi = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + int i; + + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + s->SGarray[s->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s->SGarray[s->SG_length - 1].size) + 256); + + /* If this is an MPEG stream, and VBI data is also pending, then append the + VBI DMA to the MPEG DMA and transfer both sets of data at once. + + VBI DMA is a second class citizen compared to MPEG and mixing them together + will confuse the firmware (the end of a VBI DMA is seen as the end of a + MPEG DMA, thus effectively dropping an MPEG frame). So instead we make + sure we only use the MPEG DMA to transfer the VBI DMA if both are in + use. This way no conflicts occur. */ + clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && s_vbi->SG_length && + s->SG_length + s_vbi->SG_length <= s->buffers) { + ivtv_queue_move(s_vbi, &s_vbi->q_predma, NULL, &s_vbi->q_dma, s_vbi->q_predma.bytesused); + s_vbi->SGarray[s_vbi->SG_length - 1].size = cpu_to_le32(le32_to_cpu(s_vbi->SGarray[s->SG_length - 1].size) + 256); + for (i = 0; i < s_vbi->SG_length; i++) { + s->SGarray[s->SG_length++] = s_vbi->SGarray[i]; + } + itv->vbi.dma_offset = s_vbi->dma_offset; + s_vbi->SG_length = 0; + set_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags); + IVTV_DEBUG_DMA("include DMA for %s\n", s->name); + } + + /* Mark last buffer size for Interrupt flag */ + s->SGarray[s->SG_length - 1].size |= cpu_to_le32(0x80000000); + + /* Sync Hardware SG List of buffers */ + ivtv_stream_sync_for_device(s); + write_reg(s->SG_handle, IVTV_REG_ENCDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x02, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = s->type; + itv->dma_timer.expires = jiffies + HZ / 10; + add_timer(&itv->dma_timer); +} + +static void ivtv_dma_dec_start(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + IVTV_DEBUG_DMA("start DMA for %s\n", s->name); + /* put SG Handle into register 0x0c */ + write_reg(s->SG_handle, IVTV_REG_DECDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = s->type; + itv->dma_timer.expires = jiffies + HZ / 10; + add_timer(&itv->dma_timer); +} + +static void ivtv_irq_dma_read(struct ivtv *itv) +{ + struct ivtv_stream *s = NULL; + struct ivtv_buffer *buf; + int hw_stream_type; + + IVTV_DEBUG_IRQ("DEC DMA READ\n"); + del_timer(&itv->dma_timer); + if (read_reg(IVTV_REG_DMASTATUS) & 0x14) { + IVTV_DEBUG_WARN("DEC DMA ERROR %x\n", read_reg(IVTV_REG_DMASTATUS)); + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + } + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags)) { + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + hw_stream_type = 2; + } + else { + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + hw_stream_type = 0; + } + IVTV_DEBUG_DMA("DEC DATA READ %s: %d\n", s->name, s->q_dma.bytesused); + + ivtv_stream_sync_for_cpu(s); + + /* For some reason must kick the firmware, like PIO mode, + I think this tells the firmware we are done and the size + of the xfer so it can calculate what we need next. + I think we can do this part ourselves but would have to + fully calculate xfer info ourselves and not use interrupts + */ + ivtv_vapi(itv, CX2341X_DEC_SCHED_DMA_FROM_HOST, 3, 0, s->q_dma.bytesused, + hw_stream_type); + + /* Free last DMA call */ + while ((buf = ivtv_dequeue(s, &s->q_dma)) != NULL) { + ivtv_buf_sync_for_cpu(s, buf); + ivtv_enqueue(s, buf, &s->q_free); + } + wake_up(&s->waitq); + } + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_enc_dma_complete(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + del_timer(&itv->dma_timer); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + IVTV_DEBUG_IRQ("ENC DMA COMPLETE %x %d\n", data[0], data[1]); + if (test_and_clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags)) + data[1] = 3; + else if (data[1] > 2) + return; + s = &itv->streams[ivtv_stream_map[data[1]]]; + if (data[0] & 0x18) { + IVTV_DEBUG_WARN("ENC DMA ERROR %x\n", data[0]); + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[1]); + } + s->SG_length = 0; + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + dma_post(s); + ivtv_stream_sync_for_cpu(s); + if (test_and_clear_bit(IVTV_F_S_DMA_HAS_VBI, &s->s_flags)) { + u32 tmp; + + s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + tmp = s->dma_offset; + s->dma_offset = itv->vbi.dma_offset; + dma_post(s); + s->dma_offset = tmp; + } + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_dma_err(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + + del_timer(&itv->dma_timer); + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA_END, data); + IVTV_DEBUG_WARN("DMA ERROR %08x %08x %08x %d\n", data[0], data[1], + read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); + if (!test_bit(IVTV_F_I_UDMA, &itv->i_flags) && + itv->cur_dma_stream >= 0 && itv->cur_dma_stream < IVTV_MAX_STREAMS) { + struct ivtv_stream *s = &itv->streams[itv->cur_dma_stream]; + + /* retry */ + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) + ivtv_dma_dec_start(s); + else + ivtv_dma_enc_start(s); + return; + } + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} + +static void ivtv_irq_enc_start_cap(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + /* Get DMA destination and size arguments from card */ + ivtv_api_get_data(&itv->enc_mbox, IVTV_MBOX_DMA, data); + IVTV_DEBUG_IRQ("ENC START CAP %d: %08x %08x\n", data[0], data[1], data[2]); + + if (data[0] > 2 || data[1] == 0 || data[2] == 0) { + IVTV_DEBUG_WARN("Unknown input: %08x %08x %08x\n", + data[0], data[1], data[2]); + return; + } + clear_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); + s = &itv->streams[ivtv_stream_map[data[0]]]; + if (!stream_enc_dma_append(s, data)) { + if (ivtv_use_pio(s)) { + dma_post(s); + ivtv_vapi(itv, CX2341X_ENC_SCHED_DMA_TO_HOST, 3, 0, 0, data[0]); + } + else { + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } + } +} + +static void ivtv_irq_enc_vbi_cap(struct ivtv *itv) +{ + struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG]; + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + IVTV_DEBUG_IRQ("ENC START VBI CAP\n"); + s = &itv->streams[IVTV_ENC_STREAM_TYPE_VBI]; + + if (ivtv_use_pio(s)) { + if (stream_enc_dma_append(s, data)) + return; + if (s->q_predma.bytesused) + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_dma, s->q_predma.bytesused); + s->SG_length = 0; + dma_post(s); + return; + } + /* If more than two VBI buffers are pending, then + clear the old ones and start with this new one. + This can happen during transition stages when MPEG capturing is + started, but the first interrupts haven't arrived yet. During + that period VBI requests can accumulate without being able to + DMA the data. Since at most four VBI DMA buffers are available, + we just drop the old requests when there are already three + requests queued. */ + if (s->SG_length > 2) { + struct list_head *p; + list_for_each(p, &s->q_predma.list) { + struct ivtv_buffer *buf = list_entry(p, struct ivtv_buffer, list); + ivtv_buf_sync_for_cpu(s, buf); + } + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); + s->SG_length = 0; + } + /* if we can append the data, and the MPEG stream isn't capturing, + then start a DMA request for just the VBI data. */ + if (!stream_enc_dma_append(s, data) && + !test_bit(IVTV_F_S_STREAMING, &s_mpg->s_flags)) { + set_bit(IVTV_F_I_ENC_VBI, &itv->i_flags); + set_bit(IVTV_F_S_DMA_PENDING, &s->s_flags); + } +} + +static void ivtv_irq_dev_vbi_reinsert(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s = &itv->streams[IVTV_DEC_STREAM_TYPE_VBI]; + + IVTV_DEBUG_IRQ("DEC VBI REINSERT\n"); + if (test_bit(IVTV_F_S_CLAIMED, &s->s_flags) && + !stream_enc_dma_append(s, data)) { + dma_post(s); + } +} + +static void ivtv_irq_dec_data_req(struct ivtv *itv) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv_stream *s; + + /* YUV or MPG */ + ivtv_api_get_data(&itv->dec_mbox, IVTV_MBOX_DMA, data); + + if (test_bit(IVTV_F_I_DEC_YUV, &itv->i_flags)) { + itv->dma_data_req_size = itv->params.width * itv->params.height * 3 / 2; + itv->dma_data_req_offset = data[1] ? data[1] : yuv_offset[0]; + s = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + } + else { + itv->dma_data_req_size = data[2] >= 0x10000 ? 0x10000 : data[2]; + itv->dma_data_req_offset = data[1]; + s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; + } + IVTV_DEBUG_IRQ("DEC DATA REQ %s: %d %08x %u\n", s->name, s->q_full.bytesused, + itv->dma_data_req_offset, itv->dma_data_req_size); + if (itv->dma_data_req_size == 0 || s->q_full.bytesused < itv->dma_data_req_size) { + set_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + } + else { + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_predma, itv->dma_data_req_size); + ivtv_dma_stream_dec_prepare(s, itv->dma_data_req_offset + IVTV_DECODER_OFFSET, 0); + } +} + +static void ivtv_irq_vsync(struct ivtv *itv) +{ + /* The vsync interrupt is unusual in that it won't clear until + * the end of the first line for the current field, at which + * point it clears itself. This can result in repeated vsync + * interrupts, or a missed vsync. Read some of the registers + * to determine the line being displayed and ensure we handle + * one vsync per frame. + */ + unsigned int frame = read_reg(0x28c0) & 1; + int last_dma_frame = atomic_read(&itv->yuv_info.next_dma_frame); + + if (0) IVTV_DEBUG_IRQ("DEC VSYNC\n"); + + if (((frame ^ itv->yuv_info.lace_sync_field) == 0 && ((itv->lastVsyncFrame & 1) ^ itv->yuv_info.lace_sync_field)) || + (frame != (itv->lastVsyncFrame & 1) && !itv->yuv_info.frame_interlaced)) { + int next_dma_frame = last_dma_frame; + + if (next_dma_frame >= 0 && next_dma_frame != atomic_read(&itv->yuv_info.next_fill_frame)) { + write_reg(yuv_offset[next_dma_frame] >> 4, 0x82c); + write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); + write_reg(yuv_offset[next_dma_frame] >> 4, 0x834); + write_reg((yuv_offset[next_dma_frame] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); + next_dma_frame = (next_dma_frame + 1) & 0x3; + atomic_set(&itv->yuv_info.next_dma_frame, next_dma_frame); + } + } + if (frame != (itv->lastVsyncFrame & 1)) { + struct ivtv_stream *s = ivtv_get_output_stream(itv); + + itv->lastVsyncFrame += 1; + if (frame == 0) { + clear_bit(IVTV_F_I_VALID_DEC_TIMINGS, &itv->i_flags); + clear_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); + } + else { + set_bit(IVTV_F_I_EV_VSYNC_FIELD, &itv->i_flags); + } + if (test_bit(IVTV_F_I_EV_VSYNC_ENABLED, &itv->i_flags)) { + set_bit(IVTV_F_I_EV_VSYNC, &itv->i_flags); + wake_up(&itv->event_waitq); + } + wake_up(&itv->vsync_waitq); + if (s) + wake_up(&s->waitq); + + /* Send VBI to saa7127 */ + if (frame) + vbi_schedule_work(itv); + + /* Check if we need to update the yuv registers */ + if ((itv->yuv_info.yuv_forced_update || itv->yuv_info.new_frame_info[last_dma_frame].update) && last_dma_frame != -1) { + if (!itv->yuv_info.new_frame_info[last_dma_frame].update) + last_dma_frame = (last_dma_frame - 1) & 3; + + if (itv->yuv_info.new_frame_info[last_dma_frame].src_w) { + itv->yuv_info.update_frame = last_dma_frame; + itv->yuv_info.new_frame_info[last_dma_frame].update = 0; + itv->yuv_info.yuv_forced_update = 0; + queue_work(itv->yuv_info.work_queues, &itv->yuv_info.work_queue); + } + } + } +} + +#define IVTV_IRQ_DMA (IVTV_IRQ_DMA_READ | IVTV_IRQ_ENC_DMA_COMPLETE | IVTV_IRQ_DMA_ERR | IVTV_IRQ_ENC_START_CAP | IVTV_IRQ_ENC_VBI_CAP | IVTV_IRQ_DEC_DATA_REQ) + +irqreturn_t ivtv_irq_handler(int irq, void *dev_id) +{ + struct ivtv *itv = (struct ivtv *)dev_id; + u32 combo; + u32 stat; + int i; + u8 vsync_force = 0; + + spin_lock(&itv->dma_reg_lock); + /* get contents of irq status register */ + stat = read_reg(IVTV_REG_IRQSTATUS); + + combo = ~itv->irqmask & stat; + + /* Clear out IRQ */ + if (combo) write_reg(combo, IVTV_REG_IRQSTATUS); + + if (0 == combo) { + /* The vsync interrupt is unusual and clears itself. If we + * took too long, we may have missed it. Do some checks + */ + if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { + /* vsync is enabled, see if we're in a new field */ + if ((itv->lastVsyncFrame & 1) != (read_reg(0x28c0) & 1)) { + /* New field, looks like we missed it */ + IVTV_DEBUG_YUV("VSync interrupt missed %d\n",read_reg(0x28c0)>>16); + vsync_force = 1; + } + } + + if (!vsync_force) { + /* No Vsync expected, wasn't for us */ + spin_unlock(&itv->dma_reg_lock); + return IRQ_NONE; + } + } + + /* Exclude interrupts noted below from the output, otherwise the log is flooded with + these messages */ + if (combo & ~0xff6d0400) + IVTV_DEBUG_IRQ("======= valid IRQ bits: 0x%08x ======\n", combo); + + if (combo & IVTV_IRQ_DEC_DMA_COMPLETE) { + IVTV_DEBUG_IRQ("DEC DMA COMPLETE\n"); + } + + if (combo & IVTV_IRQ_DMA_READ) { + ivtv_irq_dma_read(itv); + } + + if (combo & IVTV_IRQ_ENC_DMA_COMPLETE) { + ivtv_irq_enc_dma_complete(itv); + } + + if (combo & IVTV_IRQ_DMA_ERR) { + ivtv_irq_dma_err(itv); + } + + if (combo & IVTV_IRQ_ENC_START_CAP) { + ivtv_irq_enc_start_cap(itv); + } + + if (combo & IVTV_IRQ_ENC_VBI_CAP) { + ivtv_irq_enc_vbi_cap(itv); + } + + if (combo & IVTV_IRQ_DEC_VBI_RE_INSERT) { + ivtv_irq_dev_vbi_reinsert(itv); + } + + if (combo & IVTV_IRQ_ENC_EOS) { + IVTV_DEBUG_IRQ("ENC EOS\n"); + set_bit(IVTV_F_I_EOS, &itv->i_flags); + wake_up(&itv->cap_w); + } + + if (combo & IVTV_IRQ_DEC_DATA_REQ) { + ivtv_irq_dec_data_req(itv); + } + + /* Decoder Vertical Sync - We can't rely on 'combo', so check if vsync enabled */ + if (~itv->irqmask & IVTV_IRQ_DEC_VSYNC) { + ivtv_irq_vsync(itv); + } + + if (combo & IVTV_IRQ_ENC_VIM_RST) { + IVTV_DEBUG_IRQ("VIM RST\n"); + /*ivtv_vapi(itv, CX2341X_ENC_REFRESH_INPUT, 0); */ + } + + if (combo & IVTV_IRQ_DEC_AUD_MODE_CHG) { + IVTV_DEBUG_INFO("Stereo mode changed\n"); + } + + if ((combo & IVTV_IRQ_DMA) && !test_bit(IVTV_F_I_DMA, &itv->i_flags)) { + for (i = 0; i < IVTV_MAX_STREAMS; i++) { + int idx = (i + itv->irq_rr_idx++) % IVTV_MAX_STREAMS; + struct ivtv_stream *s = &itv->streams[idx]; + + if (!test_and_clear_bit(IVTV_F_S_DMA_PENDING, &s->s_flags)) + continue; + if (s->type >= IVTV_DEC_STREAM_TYPE_MPG) + ivtv_dma_dec_start(s); + else + ivtv_dma_enc_start(s); + break; + } + if (i == IVTV_MAX_STREAMS && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) { + ivtv_udma_start(itv); + } + } + + spin_unlock(&itv->dma_reg_lock); + + /* If we've just handled a 'forced' vsync, it's safest to say it + * wasn't ours. Another device may have triggered it at just + * the right time. + */ + return vsync_force ? IRQ_NONE : IRQ_HANDLED; +} + +void ivtv_unfinished_dma(unsigned long arg) +{ + struct ivtv *itv = (struct ivtv *)arg; + + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + return; + IVTV_ERR("DMA TIMEOUT %08x %d\n", read_reg(IVTV_REG_DMASTATUS), itv->cur_dma_stream); + + write_reg(read_reg(IVTV_REG_DMASTATUS) & 3, IVTV_REG_DMASTATUS); + clear_bit(IVTV_F_I_UDMA, &itv->i_flags); + clear_bit(IVTV_F_I_DMA, &itv->i_flags); + itv->cur_dma_stream = -1; + wake_up(&itv->dma_waitq); +} diff --git a/drivers/media/video/ivtv/ivtv-irq.h b/drivers/media/video/ivtv/ivtv-irq.h new file mode 100644 index 000000000000..ed96205e87a2 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-irq.h @@ -0,0 +1,24 @@ +/* + interrupt handling + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +irqreturn_t ivtv_irq_handler(int irq, void *dev_id); +void ivtv_dma_stream_dec_prepare(struct ivtv_stream *s, u32 offset, int lock); +void ivtv_unfinished_dma(unsigned long arg); diff --git a/drivers/media/video/ivtv/ivtv-mailbox.c b/drivers/media/video/ivtv/ivtv-mailbox.c new file mode 100644 index 000000000000..6ae42a3b03cc --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-mailbox.c @@ -0,0 +1,360 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "ivtv-driver.h" +#include "ivtv-mailbox.h" + +/* Firmware mailbox flags*/ +#define IVTV_MBOX_FIRMWARE_DONE 0x00000004 +#define IVTV_MBOX_DRIVER_DONE 0x00000002 +#define IVTV_MBOX_DRIVER_BUSY 0x00000001 +#define IVTV_MBOX_FREE 0x00000000 + +/* Firmware mailbox standard timeout */ +#define IVTV_API_STD_TIMEOUT 0x02000000 + +#define API_CACHE (1 << 0) /* Allow the command to be stored in the cache */ +#define API_RESULT (1 << 1) /* Allow 1 second for this cmd to end */ +#define API_FAST_RESULT (3 << 1) /* Allow 0.1 second for this cmd to end */ +#define API_DMA (1 << 3) /* DMA mailbox, has special handling */ +#define API_NO_WAIT_MB (1 << 4) /* Command may not wait for a free mailbox */ +#define API_NO_WAIT_RES (1 << 5) /* Command may not wait for the result */ + +struct ivtv_api_info { + int flags; /* Flags, see above */ + const char *name; /* The name of the command */ +}; + +#define API_ENTRY(x, f) [x] = { (f), #x } + +static const struct ivtv_api_info api_info[256] = { + /* MPEG encoder API */ + API_ENTRY(CX2341X_ENC_PING_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_START_CAPTURE, API_RESULT), + API_ENTRY(CX2341X_ENC_STOP_CAPTURE, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_AUDIO_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_VIDEO_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_PCR_ID, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_FRAME_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_FRAME_SIZE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_BIT_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_GOP_PROPERTIES, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_ASPECT_RATIO, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_MODE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_DNR_FILTER_PROPS, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_CORING_LEVELS, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_SPATIAL_FILTER_TYPE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_VBI_LINE, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_STREAM_TYPE, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_OUTPUT_PORT, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_AUDIO_PROPERTIES, API_CACHE), + API_ENTRY(CX2341X_ENC_HALT_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_GET_VERSION, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SET_GOP_CLOSURE, API_CACHE), + API_ENTRY(CX2341X_ENC_GET_SEQ_END, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_PGM_INDEX_INFO, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SET_VBI_CONFIG, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_DMA_BLOCK_SIZE, API_CACHE), + API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_10, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_GET_PREV_DMA_INFO_MB_9, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_SCHED_DMA_TO_HOST, API_DMA), + API_ENTRY(CX2341X_ENC_INITIALIZE_INPUT, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_FRAME_DROP_RATE, API_CACHE), + API_ENTRY(CX2341X_ENC_PAUSE_ENCODER, API_RESULT), + API_ENTRY(CX2341X_ENC_REFRESH_INPUT, API_NO_WAIT_MB), + API_ENTRY(CX2341X_ENC_SET_COPYRIGHT, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_EVENT_NOTIFICATION, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_NUM_VSYNC_LINES, API_CACHE), + API_ENTRY(CX2341X_ENC_SET_PLACEHOLDER, API_CACHE), + API_ENTRY(CX2341X_ENC_MUTE_VIDEO, API_RESULT), + API_ENTRY(CX2341X_ENC_MUTE_AUDIO, API_RESULT), + API_ENTRY(CX2341X_ENC_SET_VERT_CROP_LINE, API_FAST_RESULT), + API_ENTRY(CX2341X_ENC_MISC, API_FAST_RESULT), + /* Obsolete PULLDOWN API command */ + API_ENTRY(0xb1, API_CACHE), + + /* MPEG decoder API */ + API_ENTRY(CX2341X_DEC_PING_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_START_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_STOP_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_PLAYBACK_SPEED, API_RESULT), + API_ENTRY(CX2341X_DEC_STEP_VIDEO, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DMA_BLOCK_SIZE, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_XFER_INFO, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_GET_DMA_STATUS, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SCHED_DMA_FROM_HOST, API_DMA), + API_ENTRY(CX2341X_DEC_PAUSE_PLAYBACK, API_RESULT), + API_ENTRY(CX2341X_DEC_HALT_FW, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_STANDARD, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_VERSION, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_STREAM_INPUT, API_CACHE), + API_ENTRY(CX2341X_DEC_GET_TIMING_INFO, API_RESULT /*| API_NO_WAIT_RES*/), + API_ENTRY(CX2341X_DEC_SET_AUDIO_MODE, API_CACHE), + API_ENTRY(CX2341X_DEC_SET_EVENT_NOTIFICATION, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DISPLAY_BUFFERS, API_CACHE), + API_ENTRY(CX2341X_DEC_EXTRACT_VBI, API_RESULT), + API_ENTRY(CX2341X_DEC_SET_DECODER_SOURCE, API_FAST_RESULT), + API_ENTRY(CX2341X_DEC_SET_PREBUFFERING, API_CACHE), + + /* OSD API */ + API_ENTRY(CX2341X_OSD_GET_FRAMEBUFFER, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_GET_PIXEL_FORMAT, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_PIXEL_FORMAT, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_STATE, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_STATE, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_OSD_COORDS, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_OSD_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_SCREEN_COORDS, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_SCREEN_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_GLOBAL_ALPHA, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_GLOBAL_ALPHA, API_CACHE), + API_ENTRY(CX2341X_OSD_SET_BLEND_COORDS, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_FLICKER_STATE, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_FLICKER_STATE, API_CACHE), + API_ENTRY(CX2341X_OSD_BLT_COPY, API_RESULT), + API_ENTRY(CX2341X_OSD_BLT_FILL, API_RESULT), + API_ENTRY(CX2341X_OSD_BLT_TEXT, API_RESULT), + API_ENTRY(CX2341X_OSD_SET_FRAMEBUFFER_WINDOW, API_CACHE), + API_ENTRY(CX2341X_OSD_SET_CHROMA_KEY, API_CACHE), + API_ENTRY(CX2341X_OSD_GET_ALPHA_CONTENT_INDEX, API_FAST_RESULT), + API_ENTRY(CX2341X_OSD_SET_ALPHA_CONTENT_INDEX, API_CACHE) +}; + +static int try_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int mb) +{ + u32 flags = readl(&mbdata->mbox[mb].flags); + int is_free = flags == IVTV_MBOX_FREE || (flags & IVTV_MBOX_FIRMWARE_DONE); + + /* if the mailbox is free, then try to claim it */ + if (is_free && !test_and_set_bit(mb, &mbdata->busy)) { + write_sync(IVTV_MBOX_DRIVER_BUSY, &mbdata->mbox[mb].flags); + return 1; + } + return 0; +} + +/* Try to find a free mailbox. Note mailbox 0 is reserved for DMA and so is not + attempted here. */ +static int get_mailbox(struct ivtv *itv, struct ivtv_mailbox_data *mbdata, int flags) +{ + unsigned long then = jiffies; + int i, mb; + int max_mbox = mbdata->max_mbox; + int retries = 100; + + /* All slow commands use the same mailbox, serializing them and also + leaving the other mailbox free for simple fast commands. */ + if ((flags & API_FAST_RESULT) == API_RESULT) + max_mbox = 1; + + /* find free non-DMA mailbox */ + for (i = 0; i < retries; i++) { + for (mb = 1; mb <= max_mbox; mb++) + if (try_mailbox(itv, mbdata, mb)) + return mb; + + /* Sleep before a retry, if not atomic */ + if (!(flags & API_NO_WAIT_MB)) { + if (jiffies - then > retries * HZ / 100) + break; + ivtv_sleep_timeout(HZ / 100, 0); + } + } + return -ENODEV; +} + +static void write_mailbox(volatile struct ivtv_mailbox __iomem *mbox, int cmd, int args, u32 data[]) +{ + int i; + + write_sync(cmd, &mbox->cmd); + write_sync(IVTV_API_STD_TIMEOUT, &mbox->timeout); + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + write_sync(data[i], &mbox->data[i]); + + write_sync(IVTV_MBOX_DRIVER_DONE | IVTV_MBOX_DRIVER_BUSY, &mbox->flags); +} + +static void clear_all_mailboxes(struct ivtv *itv, struct ivtv_mailbox_data *mbdata) +{ + int i; + + for (i = 0; i <= mbdata->max_mbox; i++) { + IVTV_DEBUG_WARN("Clearing mailbox %d: cmd 0x%08x flags 0x%08x\n", + i, readl(&mbdata->mbox[i].cmd), readl(&mbdata->mbox[i].flags)); + write_sync(0, &mbdata->mbox[i].flags); + clear_bit(i, &mbdata->busy); + } +} + +static int ivtv_api_call(struct ivtv *itv, int cmd, int args, u32 data[]) +{ + struct ivtv_mailbox_data *mbdata = (cmd >= 128) ? &itv->enc_mbox : &itv->dec_mbox; + volatile struct ivtv_mailbox __iomem *mbox; + int api_timeout = HZ; + int flags, mb, i; + unsigned long then; + + /* sanity checks */ + if (NULL == mbdata) { + IVTV_ERR("No mailbox allocated\n"); + return -ENODEV; + } + if (args < 0 || args > CX2341X_MBOX_MAX_DATA || + cmd < 0 || cmd > 255 || api_info[cmd].name == NULL) { + IVTV_ERR("Invalid API call: cmd = 0x%02x, args = %d\n", cmd, args); + return -EINVAL; + } + + IVTV_DEBUG_API("API Call: %s\n", api_info[cmd].name); + + /* clear possibly uninitialized part of data array */ + for (i = args; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = 0; + + /* If this command was issued within the last 30 minutes and with identical + data, then just return 0 as there is no need to issue this command again. + Just an optimization to prevent unnecessary use of mailboxes. */ + if (itv->api_cache[cmd].last_jiffies && + jiffies - itv->api_cache[cmd].last_jiffies < HZ * 1800 && + !memcmp(data, itv->api_cache[cmd].data, sizeof(itv->api_cache[cmd].data))) { + itv->api_cache[cmd].last_jiffies = jiffies; + return 0; + } + + flags = api_info[cmd].flags; + + if (flags & API_DMA) { + for (i = 0; i < 100; i++) { + mb = i % (mbdata->max_mbox + 1); + if (try_mailbox(itv, mbdata, mb)) { + write_mailbox(&mbdata->mbox[mb], cmd, args, data); + clear_bit(mb, &mbdata->busy); + return 0; + } + IVTV_DEBUG_WARN("%s: mailbox %d not free %08x\n", + api_info[cmd].name, mb, readl(&mbdata->mbox[mb].flags)); + } + IVTV_WARN("Could not find free DMA mailbox for %s\n", api_info[cmd].name); + clear_all_mailboxes(itv, mbdata); + return -EBUSY; + } + + if ((flags & API_FAST_RESULT) == API_FAST_RESULT) + api_timeout = HZ / 10; + + mb = get_mailbox(itv, mbdata, flags); + if (mb < 0) { + IVTV_DEBUG_WARN("No free mailbox found (%s)\n", api_info[cmd].name); + clear_all_mailboxes(itv, mbdata); + return -EBUSY; + } + mbox = &mbdata->mbox[mb]; + write_mailbox(mbox, cmd, args, data); + if (flags & API_CACHE) { + memcpy(itv->api_cache[cmd].data, data, sizeof(itv->api_cache[cmd].data)); + itv->api_cache[cmd].last_jiffies = jiffies; + } + if ((flags & API_RESULT) == 0) { + clear_bit(mb, &mbdata->busy); + return 0; + } + + /* Get results */ + then = jiffies; + + while (!(readl(&mbox->flags) & IVTV_MBOX_FIRMWARE_DONE)) { + if (jiffies - then > api_timeout) { + IVTV_DEBUG_WARN("Could not get result (%s)\n", api_info[cmd].name); + /* reset the mailbox, but it is likely too late already */ + write_sync(0, &mbox->flags); + clear_bit(mb, &mbdata->busy); + return -EIO; + } + if (flags & API_NO_WAIT_RES) + mdelay(1); + else + ivtv_sleep_timeout(HZ / 100, 0); + } + if (jiffies - then > HZ / 10) + IVTV_DEBUG_WARN("%s took %lu jiffies (%d per HZ)\n", + api_info[cmd].name, jiffies - then, HZ); + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = readl(&mbox->data[i]); + write_sync(0, &mbox->flags); + clear_bit(mb, &mbdata->busy); + return 0; +} + +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]) +{ + int res = ivtv_api_call(itv, cmd, args, data); + + /* Allow a single retry, probably already too late though. + If there is no free mailbox then that is usually an indication + of a more serious problem. */ + return (res == -EBUSY) ? ivtv_api_call(itv, cmd, args, data) : res; +} + +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]) +{ + return ivtv_api(priv, cmd, in, data); +} + +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...) +{ + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) { + data[i] = va_arg(ap, u32); + } + va_end(ap); + return ivtv_api(itv, cmd, args, data); +} + +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + va_list ap; + int i; + + va_start(ap, args); + for (i = 0; i < args; i++) { + data[i] = va_arg(ap, u32); + } + va_end(ap); + return ivtv_api(itv, cmd, args, data); +} + +/* This one is for stuff that can't sleep.. irq handlers, etc.. */ +void ivtv_api_get_data(struct ivtv_mailbox_data *mbdata, int mb, u32 data[]) +{ + int i; + + for (i = 0; i < CX2341X_MBOX_MAX_DATA; i++) + data[i] = readl(&mbdata->mbox[mb].data[i]); +} diff --git a/drivers/media/video/ivtv/ivtv-mailbox.h b/drivers/media/video/ivtv/ivtv-mailbox.h new file mode 100644 index 000000000000..79b8aec14370 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-mailbox.h @@ -0,0 +1,25 @@ +/* + mailbox functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_api_get_data(struct ivtv_mailbox_data *mbox, int mb, u32 data[]); +int ivtv_api(struct ivtv *itv, int cmd, int args, u32 data[]); +int ivtv_vapi_result(struct ivtv *itv, u32 data[CX2341X_MBOX_MAX_DATA], int cmd, int args, ...); +int ivtv_vapi(struct ivtv *itv, int cmd, int args, ...); +int ivtv_api_func(void *priv, int cmd, int in, int out, u32 data[CX2341X_MBOX_MAX_DATA]); diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c new file mode 100644 index 000000000000..ccfcef1ad91a --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-queue.c @@ -0,0 +1,262 @@ +/* + buffer queues. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-streams.h" +#include "ivtv-queue.h" +#include "ivtv-mailbox.h" + +int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes) +{ + if (s->buf_size - buf->bytesused < copybytes) + copybytes = s->buf_size - buf->bytesused; + if (copy_from_user(buf->buf + buf->bytesused, src, copybytes)) { + return -EFAULT; + } + buf->bytesused += copybytes; + return copybytes; +} + +void ivtv_buf_swap(struct ivtv_buffer *buf) +{ + int i; + + for (i = 0; i < buf->bytesused; i += 4) + swab32s((u32 *)(buf->buf + i)); +} + +void ivtv_queue_init(struct ivtv_queue *q) +{ + INIT_LIST_HEAD(&q->list); + q->buffers = 0; + q->length = 0; + q->bytesused = 0; +} + +void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q) +{ + unsigned long flags = 0; + + /* clear the buffer if it is going to be enqueued to the free queue */ + if (q == &s->q_free) { + buf->bytesused = 0; + buf->readpos = 0; + buf->b_flags = 0; + } + spin_lock_irqsave(&s->qlock, flags); + list_add_tail(&buf->list, &q->list); + q->buffers++; + q->length += s->buf_size; + q->bytesused += buf->bytesused - buf->readpos; + spin_unlock_irqrestore(&s->qlock, flags); +} + +struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q) +{ + struct ivtv_buffer *buf = NULL; + unsigned long flags = 0; + + spin_lock_irqsave(&s->qlock, flags); + if (!list_empty(&q->list)) { + buf = list_entry(q->list.next, struct ivtv_buffer, list); + list_del_init(q->list.next); + q->buffers--; + q->length -= s->buf_size; + q->bytesused -= buf->bytesused - buf->readpos; + } + spin_unlock_irqrestore(&s->qlock, flags); + return buf; +} + +static void ivtv_queue_move_buf(struct ivtv_stream *s, struct ivtv_queue *from, + struct ivtv_queue *to, int clear, int full) +{ + struct ivtv_buffer *buf = list_entry(from->list.next, struct ivtv_buffer, list); + + list_move_tail(from->list.next, &to->list); + from->buffers--; + from->length -= s->buf_size; + from->bytesused -= buf->bytesused - buf->readpos; + /* special handling for q_free */ + if (clear) + buf->bytesused = buf->readpos = buf->b_flags = 0; + else if (full) { + /* special handling for stolen buffers, assume + all bytes are used. */ + buf->bytesused = s->buf_size; + buf->readpos = buf->b_flags = 0; + } + to->buffers++; + to->length += s->buf_size; + to->bytesused += buf->bytesused - buf->readpos; +} + +/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'. + If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'. + If 'steal' != NULL, then buffers may also taken from that queue if + needed. + + The buffer is automatically cleared if it goes to the free queue. It is + also cleared if buffers need to be taken from the 'steal' queue and + the 'from' queue is the free queue. + + When 'from' is q_free, then needed_bytes is compared to the total + available buffer length, otherwise needed_bytes is compared to the + bytesused value. For the 'steal' queue the total available buffer + length is always used. + + -ENOMEM is returned if the buffers could not be obtained, 0 if all + buffers where obtained from the 'from' list and if non-zero then + the number of stolen buffers is returned. */ +int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, + struct ivtv_queue *to, int needed_bytes) +{ + unsigned long flags; + int rc = 0; + int from_free = from == &s->q_free; + int to_free = to == &s->q_free; + int bytes_available; + + spin_lock_irqsave(&s->qlock, flags); + if (needed_bytes == 0) { + from_free = 1; + needed_bytes = from->length; + } + + bytes_available = from_free ? from->length : from->bytesused; + bytes_available += steal ? steal->length : 0; + + if (bytes_available < needed_bytes) { + spin_unlock_irqrestore(&s->qlock, flags); + return -ENOMEM; + } + if (from_free) { + u32 old_length = to->length; + + while (to->length - old_length < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + ivtv_queue_move_buf(s, from, to, 1, 0); + } + } + else { + u32 old_bytesused = to->bytesused; + + while (to->bytesused - old_bytesused < needed_bytes) { + if (list_empty(&from->list)) + from = steal; + if (from == steal) + rc++; /* keep track of 'stolen' buffers */ + ivtv_queue_move_buf(s, from, to, to_free, rc); + } + } + spin_unlock_irqrestore(&s->qlock, flags); + return rc; +} + +void ivtv_flush_queues(struct ivtv_stream *s) +{ + ivtv_queue_move(s, &s->q_io, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_full, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_dma, NULL, &s->q_free, 0); + ivtv_queue_move(s, &s->q_predma, NULL, &s->q_free, 0); +} + +int ivtv_stream_alloc(struct ivtv_stream *s) +{ + struct ivtv *itv = s->itv; + int SGsize = sizeof(struct ivtv_SG_element) * s->buffers; + int i; + + if (s->buffers == 0) + return 0; + + IVTV_DEBUG_INFO("Allocate %s%s stream: %d x %d buffers (%dkB total)\n", + s->dma != PCI_DMA_NONE ? "DMA " : "", + s->name, s->buffers, s->buf_size, s->buffers * s->buf_size / 1024); + + /* Allocate DMA SG Arrays */ + if (s->dma != PCI_DMA_NONE) { + s->SGarray = (struct ivtv_SG_element *)kzalloc(SGsize, GFP_KERNEL); + if (s->SGarray == NULL) { + IVTV_ERR("Could not allocate SGarray for %s stream\n", s->name); + return -ENOMEM; + } + s->SG_length = 0; + s->SG_handle = pci_map_single(itv->dev, s->SGarray, SGsize, s->dma); + ivtv_stream_sync_for_cpu(s); + } + + /* allocate stream buffers. Initially all buffers are in q_free. */ + for (i = 0; i < s->buffers; i++) { + struct ivtv_buffer *buf = kzalloc(sizeof(struct ivtv_buffer), GFP_KERNEL); + + if (buf == NULL) + break; + buf->buf = kmalloc(s->buf_size + 256, GFP_KERNEL); + if (buf->buf == NULL) { + kfree(buf); + break; + } + INIT_LIST_HEAD(&buf->list); + if (s->dma != PCI_DMA_NONE) { + buf->dma_handle = pci_map_single(s->itv->dev, + buf->buf, s->buf_size + 256, s->dma); + ivtv_buf_sync_for_cpu(s, buf); + } + ivtv_enqueue(s, buf, &s->q_free); + } + if (i == s->buffers) + return 0; + IVTV_ERR("Couldn't allocate buffers for %s stream\n", s->name); + ivtv_stream_free(s); + return -ENOMEM; +} + +void ivtv_stream_free(struct ivtv_stream *s) +{ + struct ivtv_buffer *buf; + + /* move all buffers to q_free */ + ivtv_flush_queues(s); + + /* empty q_free */ + while ((buf = ivtv_dequeue(s, &s->q_free))) { + if (s->dma != PCI_DMA_NONE) + pci_unmap_single(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); + kfree(buf->buf); + kfree(buf); + } + + /* Free SG Array/Lists */ + if (s->SGarray != NULL) { + if (s->SG_handle != IVTV_DMA_UNMAPPED) { + pci_unmap_single(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); + s->SG_handle = IVTV_DMA_UNMAPPED; + } + s->SGarray = NULL; + s->SG_length = 0; + } +} diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h new file mode 100644 index 000000000000..903edd4b4381 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-queue.h @@ -0,0 +1,64 @@ +/* + buffer queues. + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define IVTV_DMA_UNMAPPED ((u32) -1) + +/* ivtv_buffer utility functions */ +static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf) +{ + if (s->dma != PCI_DMA_NONE) + pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); +} + +static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf) +{ + if (s->dma != PCI_DMA_NONE) + pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle, + s->buf_size + 256, s->dma); +} + +int ivtv_buf_copy_from_user(struct ivtv_stream *s, struct ivtv_buffer *buf, const char __user *src, int copybytes); +void ivtv_buf_swap(struct ivtv_buffer *buf); + +/* ivtv_queue utility functions */ +void ivtv_queue_init(struct ivtv_queue *q); +void ivtv_enqueue(struct ivtv_stream *s, struct ivtv_buffer *buf, struct ivtv_queue *q); +struct ivtv_buffer *ivtv_dequeue(struct ivtv_stream *s, struct ivtv_queue *q); +int ivtv_queue_move(struct ivtv_stream *s, struct ivtv_queue *from, struct ivtv_queue *steal, + struct ivtv_queue *to, int needed_bytes); +void ivtv_flush_queues(struct ivtv_stream *s); + +/* ivtv_stream utility functions */ +int ivtv_stream_alloc(struct ivtv_stream *s); +void ivtv_stream_free(struct ivtv_stream *s); + +static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s) +{ + pci_dma_sync_single_for_cpu(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); +} + +static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s) +{ + pci_dma_sync_single_for_device(s->itv->dev, s->SG_handle, + sizeof(struct ivtv_SG_element) * s->buffers, PCI_DMA_TODEVICE); +} diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c new file mode 100644 index 000000000000..73a1c933d8f7 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -0,0 +1,977 @@ +/* + init/start/stop/exit stream functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* License: GPL + * Author: Kevin Thayer + * + * This file will hold API related functions, both internal (firmware api) + * and external (v4l2, etc) + * + * ----- + * MPG600/MPG160 support by T.Adachi + * and Takeru KOMORIYA + * + * AVerMedia M179 GPIO info by Chris Pinkham + * using information provided by Jiun-Kuei Jung @ AVerMedia. + */ + +#include "ivtv-driver.h" +#include "ivtv-fileops.h" +#include "ivtv-i2c.h" +#include "ivtv-queue.h" +#include "ivtv-mailbox.h" +#include "ivtv-audio.h" +#include "ivtv-video.h" +#include "ivtv-vbi.h" +#include "ivtv-ioctl.h" +#include "ivtv-irq.h" +#include "ivtv-streams.h" +#include "ivtv-cards.h" + +static struct file_operations ivtv_v4l2_enc_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_v4l2_enc_poll, +}; + +static struct file_operations ivtv_v4l2_dec_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_v4l2_dec_poll, +}; + +struct { + const char *name; + int vfl_type; + int minor_offset; + int dma, pio; + enum v4l2_buf_type buf_type; + struct file_operations *fops; +} ivtv_stream_info[] = { + { /* IVTV_ENC_STREAM_TYPE_MPG */ + "encoder MPEG", + VFL_TYPE_GRABBER, 0, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_YUV */ + "encoder YUV", + VFL_TYPE_GRABBER, IVTV_V4L2_ENC_YUV_OFFSET, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VIDEO_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_VBI */ + "encoder VBI", + VFL_TYPE_VBI, 0, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_VBI_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_PCM */ + "encoder PCM audio", + VFL_TYPE_GRABBER, IVTV_V4L2_ENC_PCM_OFFSET, + PCI_DMA_FROMDEVICE, 0, V4L2_BUF_TYPE_PRIVATE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_ENC_STREAM_TYPE_RAD */ + "encoder radio", + VFL_TYPE_RADIO, 0, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_PRIVATE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_DEC_STREAM_TYPE_MPG */ + "decoder MPEG", + VFL_TYPE_GRABBER, IVTV_V4L2_DEC_MPG_OFFSET, + PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + &ivtv_v4l2_dec_fops + }, + { /* IVTV_DEC_STREAM_TYPE_VBI */ + "decoder VBI", + VFL_TYPE_VBI, IVTV_V4L2_DEC_VBI_OFFSET, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_CAPTURE, + &ivtv_v4l2_enc_fops + }, + { /* IVTV_DEC_STREAM_TYPE_VOUT */ + "decoder VOUT", + VFL_TYPE_VBI, IVTV_V4L2_DEC_VOUT_OFFSET, + PCI_DMA_NONE, 1, V4L2_BUF_TYPE_VBI_OUTPUT, + &ivtv_v4l2_dec_fops + }, + { /* IVTV_DEC_STREAM_TYPE_YUV */ + "decoder YUV", + VFL_TYPE_GRABBER, IVTV_V4L2_DEC_YUV_OFFSET, + PCI_DMA_TODEVICE, 0, V4L2_BUF_TYPE_VIDEO_OUTPUT, + &ivtv_v4l2_dec_fops + } +}; + +static void ivtv_stream_init(struct ivtv *itv, int type) +{ + struct ivtv_stream *s = &itv->streams[type]; + struct video_device *dev = s->v4l2dev; + + /* we need to keep v4l2dev, so restore it afterwards */ + memset(s, 0, sizeof(*s)); + s->v4l2dev = dev; + + /* initialize ivtv_stream fields */ + s->itv = itv; + s->type = type; + s->name = ivtv_stream_info[type].name; + + if (ivtv_stream_info[type].pio) + s->dma = PCI_DMA_NONE; + else + s->dma = ivtv_stream_info[type].dma; + s->buf_size = itv->stream_buf_size[type]; + if (s->buf_size) + s->buffers = itv->options.megabytes[type] * 1024 * 1024 / s->buf_size; + spin_lock_init(&s->qlock); + init_waitqueue_head(&s->waitq); + s->id = -1; + s->SG_handle = IVTV_DMA_UNMAPPED; + ivtv_queue_init(&s->q_free); + ivtv_queue_init(&s->q_full); + ivtv_queue_init(&s->q_dma); + ivtv_queue_init(&s->q_predma); + ivtv_queue_init(&s->q_io); +} + +static int ivtv_reg_dev(struct ivtv *itv, int type) +{ + struct ivtv_stream *s = &itv->streams[type]; + int vfl_type = ivtv_stream_info[type].vfl_type; + int minor_offset = ivtv_stream_info[type].minor_offset; + int minor; + + /* These four fields are always initialized. If v4l2dev == NULL, then + this stream is not in use. In that case no other fields but these + four can be used. */ + s->v4l2dev = NULL; + s->itv = itv; + s->type = type; + s->name = ivtv_stream_info[type].name; + + /* Check whether the radio is supported */ + if (type == IVTV_ENC_STREAM_TYPE_RAD && !(itv->v4l2_cap & V4L2_CAP_RADIO)) + return 0; + if (type >= IVTV_DEC_STREAM_TYPE_MPG && !(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return 0; + + if (minor_offset >= 0) + /* card number + user defined offset + device offset */ + minor = itv->num + ivtv_first_minor + minor_offset; + else + minor = -1; + + /* User explicitly selected 0 buffers for these streams, so don't + create them. */ + if (minor >= 0 && ivtv_stream_info[type].dma != PCI_DMA_NONE && + itv->options.megabytes[type] == 0) { + IVTV_INFO("Disabled %s device\n", ivtv_stream_info[type].name); + return 0; + } + + ivtv_stream_init(itv, type); + + /* allocate and initialize the v4l2 video device structure */ + s->v4l2dev = video_device_alloc(); + if (s->v4l2dev == NULL) { + IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name); + return -ENOMEM; + } + + s->v4l2dev->type = VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT | + VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER; + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + s->v4l2dev->type |= VID_TYPE_MPEG_DECODER; + } + snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "ivtv%d %s", + itv->num, s->name); + + s->v4l2dev->minor = minor; + s->v4l2dev->dev = &itv->dev->dev; + s->v4l2dev->fops = ivtv_stream_info[type].fops; + s->v4l2dev->release = video_device_release; + + if (minor >= 0) { + /* Register device. First try the desired minor, then any free one. */ + if (video_register_device(s->v4l2dev, vfl_type, minor) && + video_register_device(s->v4l2dev, vfl_type, -1)) { + IVTV_ERR("Couldn't register v4l2 device for %s minor %d\n", + s->name, minor); + video_device_release(s->v4l2dev); + s->v4l2dev = NULL; + return -ENOMEM; + } + } + else { + /* Don't register a 'hidden' stream (OSD) */ + IVTV_INFO("Created framebuffer stream for %s\n", s->name); + return 0; + } + + switch (vfl_type) { + case VFL_TYPE_GRABBER: + IVTV_INFO("Registered device video%d for %s (%d MB)\n", + s->v4l2dev->minor, s->name, itv->options.megabytes[type]); + break; + case VFL_TYPE_RADIO: + IVTV_INFO("Registered device radio%d for %s\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_RADIO_MIN, s->name); + break; + case VFL_TYPE_VBI: + if (itv->options.megabytes[type]) + IVTV_INFO("Registered device vbi%d for %s (%d MB)\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, + s->name, itv->options.megabytes[type]); + else + IVTV_INFO("Registered device vbi%d for %s\n", + s->v4l2dev->minor - MINOR_VFL_TYPE_VBI_MIN, s->name); + break; + } + return 0; +} + +/* Initialize v4l2 variables and register v4l2 devices */ +int ivtv_streams_setup(struct ivtv *itv) +{ + int type; + + /* Setup V4L2 Devices */ + for (type = 0; type < IVTV_MAX_STREAMS; type++) { + /* Register Device */ + if (ivtv_reg_dev(itv, type)) + break; + + if (itv->streams[type].v4l2dev == NULL) + continue; + + /* Allocate Stream */ + if (ivtv_stream_alloc(&itv->streams[type])) + break; + } + if (type == IVTV_MAX_STREAMS) { + return 0; + } + + /* One or more streams could not be initialized. Clean 'em all up. */ + ivtv_streams_cleanup(itv); + return -ENOMEM; +} + +/* Unregister v4l2 devices */ +void ivtv_streams_cleanup(struct ivtv *itv) +{ + int type; + + /* Teardown all streams */ + for (type = 0; type < IVTV_MAX_STREAMS; type++) { + struct video_device *vdev = itv->streams[type].v4l2dev; + + itv->streams[type].v4l2dev = NULL; + if (vdev == NULL) + continue; + + ivtv_stream_free(&itv->streams[type]); + /* Free Device */ + if (vdev->minor == -1) /* 'Hidden' never registered stream (OSD) */ + video_device_release(vdev); + else /* All others, just unregister. */ + video_unregister_device(vdev); + } +} + +static void ivtv_vbi_setup(struct ivtv *itv) +{ + int raw = itv->vbi.sliced_in->service_set == 0; + u32 data[CX2341X_MBOX_MAX_DATA]; + int lines; + int i; + + /* If Embed then streamtype must be Program */ + /* TODO: should we really do this? */ + if (0 && !raw && itv->vbi.insert_mpeg) { + itv->params.stream_type = 0; + + /* assign stream type */ + ivtv_vapi(itv, CX2341X_ENC_SET_STREAM_TYPE, 1, itv->params.stream_type); + } + + /* Reset VBI */ + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, 0xffff , 0, 0, 0, 0); + + if (itv->is_60hz) { + itv->vbi.count = 12; + itv->vbi.start[0] = 10; + itv->vbi.start[1] = 273; + } else { /* PAL/SECAM */ + itv->vbi.count = 18; + itv->vbi.start[0] = 6; + itv->vbi.start[1] = 318; + } + + /* setup VBI registers */ + itv->video_dec_func(itv, VIDIOC_S_FMT, &itv->vbi.in); + + /* determine number of lines and total number of VBI bytes. + A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1 + The '- 1' byte is probably an unused U or V byte. Or something... + A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal + header, 42 data bytes + checksum (to be confirmed) */ + if (raw) { + lines = itv->vbi.count * 2; + } else { + lines = itv->is_60hz ? 24 : 38; + if (itv->is_60hz && (itv->hw_flags & IVTV_HW_CX25840)) + lines += 2; + } + + itv->vbi.enc_size = lines * (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); + + /* Note: sliced vs raw flag doesn't seem to have any effect + TODO: check mode (0x02) value with older ivtv versions. */ + data[0] = raw | 0x02 | (0xbd << 8); + + /* Every X number of frames a VBI interrupt arrives (frames as in 25 or 30 fps) */ + data[1] = 1; + /* The VBI frames are stored in a ringbuffer with this size (with a VBI frame as unit) */ + data[2] = raw ? 4 : 8; + /* The start/stop codes determine which VBI lines end up in the raw VBI data area. + The codes are from table 24 in the saa7115 datasheet. Each raw/sliced/video line + is framed with codes FF0000XX where XX is the SAV/EAV (Start/End of Active Video) + code. These values for raw VBI are obtained from a driver disassembly. The sliced + start/stop codes was deduced from this, but they do not appear in the driver. + Other code pairs that I found are: 0x250E6249/0x13545454 and 0x25256262/0x38137F54. + However, I have no idea what these values are for. */ + if (itv->hw_flags & IVTV_HW_CX25840) { + /* Setup VBI for the cx25840 digitizer */ + if (raw) { + data[3] = 0x20602060; + data[4] = 0x30703070; + } else { + data[3] = 0xB0F0B0F0; + data[4] = 0xA0E0A0E0; + } + /* Lines per frame */ + data[5] = lines; + /* bytes per line */ + data[6] = (raw ? itv->vbi.raw_size : itv->vbi.sliced_size); + } else { + /* Setup VBI for the saa7115 digitizer */ + if (raw) { + data[3] = 0x25256262; + data[4] = 0x387F7F7F; + } else { + data[3] = 0xABABECEC; + data[4] = 0xB6F1F1F1; + } + /* Lines per frame */ + data[5] = lines; + /* bytes per line */ + data[6] = itv->vbi.enc_size / lines; + } + + IVTV_DEBUG_INFO( + "Setup VBI API header 0x%08x pkts %d buffs %d ln %d sz %d\n", + data[0], data[1], data[2], data[5], data[6]); + + ivtv_api(itv, CX2341X_ENC_SET_VBI_CONFIG, 7, data); + + /* returns the VBI encoder memory area. */ + itv->vbi.enc_start = data[2]; + itv->vbi.fpi = data[0]; + if (!itv->vbi.fpi) + itv->vbi.fpi = 1; + + IVTV_DEBUG_INFO("Setup VBI start 0x%08x frames %d fpi %d lines 0x%08x\n", + itv->vbi.enc_start, data[1], itv->vbi.fpi, itv->digitizer); + + /* select VBI lines. + Note that the sliced argument seems to have no effect. */ + for (i = 2; i <= 24; i++) { + int valid; + + if (itv->is_60hz) { + valid = i >= 10 && i < 22; + } else { + valid = i >= 6 && i < 24; + } + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, i - 1, + valid, 0 , 0, 0); + ivtv_vapi(itv, CX2341X_ENC_SET_VBI_LINE, 5, (i - 1) | 0x80000000, + valid, 0, 0, 0); + } + + /* Remaining VBI questions: + - Is it possible to select particular VBI lines only for inclusion in the MPEG + stream? Currently you can only get the first X lines. + - Is mixed raw and sliced VBI possible? + - What's the meaning of the raw/sliced flag? + - What's the meaning of params 2, 3 & 4 of the Select VBI command? */ +} + +int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv *itv = s->itv; + int captype = 0, subtype = 0; + int enable_passthrough = 0; + + if (s->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name); + + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_MPG: + captype = 0; + subtype = 3; + + /* Stop Passthrough */ + if (itv->output_mode == OUT_PASSTHROUGH) { + ivtv_passthrough_mode(itv, 0); + enable_passthrough = 1; + } + itv->mpg_data_received = itv->vbi_data_inserted = 0; + itv->dualwatch_jiffies = jiffies; + itv->dualwatch_stereo_mode = itv->params.audio_properties & 0x0300; + itv->search_pack_header = 0; + break; + + case IVTV_ENC_STREAM_TYPE_YUV: + if (itv->output_mode == OUT_PASSTHROUGH) { + captype = 2; + subtype = 11; /* video+audio+decoder */ + break; + } + captype = 1; + subtype = 1; + break; + case IVTV_ENC_STREAM_TYPE_PCM: + captype = 1; + subtype = 2; + break; + case IVTV_ENC_STREAM_TYPE_VBI: + captype = 1; + subtype = 4; + + itv->vbi.frame = 0; + itv->vbi.inserted_frame = 0; + memset(itv->vbi.sliced_mpeg_size, + 0, sizeof(itv->vbi.sliced_mpeg_size)); + break; + default: + return -EINVAL; + } + s->subtype = subtype; + s->buffers_stolen = 0; + + /* mute/unmute video */ + ivtv_vapi(itv, CX2341X_ENC_MUTE_VIDEO, 1, test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? 1 : 0); + + /* Clear Streamoff flags in case left from last capture */ + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + if (atomic_read(&itv->capturing) == 0) { + /* Always use frame based mode. Experiments have demonstrated that byte + stream based mode results in dropped frames and corruption. Not often, + but occasionally. Many thanks go to Leonard Orb who spent a lot of + effort and time trying to trace the cause of the drop outs. */ + /* 1 frame per DMA */ + /*ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 128, 0); */ + ivtv_vapi(itv, CX2341X_ENC_SET_DMA_BLOCK_SIZE, 2, 1, 1); + + /* Stuff from Windows, we don't know what it is */ + ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, 0); + /* According to the docs, this should be correct. However, this is + untested. I don't dare enable this without having tested it. + Only very few old cards actually have this hardware combination. + ivtv_vapi(itv, CX2341X_ENC_SET_VERT_CROP_LINE, 1, + ((itv->hw_flags & IVTV_HW_SAA7114) && itv->is_60hz) ? 10001 : 0); + */ + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 3, !itv->has_cx23415); + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 8, 0); + ivtv_vapi(itv, CX2341X_ENC_MISC, 2, 4, 1); + ivtv_vapi(itv, CX2341X_ENC_MISC, 1, 12); + + /* assign placeholder */ + ivtv_vapi(itv, CX2341X_ENC_SET_PLACEHOLDER, 12, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + + ivtv_vapi(itv, CX2341X_ENC_SET_NUM_VSYNC_LINES, 2, itv->digitizer, itv->digitizer); + + /* Setup VBI */ + if (itv->v4l2_cap & V4L2_CAP_VBI_CAPTURE) { + ivtv_vbi_setup(itv); + } + + /* assign program index info. Mask 7: select I/P/B, Num_req: 400 max */ + ivtv_vapi_result(itv, data, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 7, 400); + itv->pgm_info_offset = data[0]; + itv->pgm_info_num = data[1]; + itv->pgm_info_write_idx = 0; + itv->pgm_info_read_idx = 0; + + IVTV_DEBUG_INFO("PGM Index at 0x%08x with %d elements\n", + itv->pgm_info_offset, itv->pgm_info_num); + + /* Setup API for Stream */ + cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + } + + /* Vsync Setup */ + if (itv->has_cx23415 && !test_and_set_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* event notification (on) */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + + if (atomic_read(&itv->capturing) == 0) { + /* Clear all Pending Interrupts */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + clear_bit(IVTV_F_I_EOS, &itv->i_flags); + + /* Initialize Digitizer for Capture */ + ivtv_vapi(itv, CX2341X_ENC_INITIALIZE_INPUT, 0); + + ivtv_sleep_timeout(HZ / 10, 0); + } + + /* begin_capture */ + if (ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, captype, subtype)) + { + IVTV_DEBUG_WARN( "Error starting capture!\n"); + return -EINVAL; + } + + /* Start Passthrough */ + if (enable_passthrough) { + ivtv_passthrough_mode(itv, 1); + } + + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) + ivtv_clear_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); + else + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + /* you're live! sit back and await interrupts :) */ + atomic_inc(&itv->capturing); + return 0; +} + +static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s) +{ + u32 data[CX2341X_MBOX_MAX_DATA]; + struct ivtv *itv = s->itv; + int datatype; + + if (s->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("Setting some initial decoder settings\n"); + + /* disable VBI signals, if the MPEG stream contains VBI data, + then that data will be processed automatically for you. */ + ivtv_disable_vbi(itv); + + /* set audio mode to left/stereo for dual/stereo mode. */ + ivtv_vapi(itv, CX2341X_DEC_SET_AUDIO_MODE, 2, itv->audio_bilingual_mode, itv->audio_stereo_mode); + + /* set number of internal decoder buffers */ + ivtv_vapi(itv, CX2341X_DEC_SET_DISPLAY_BUFFERS, 1, 0); + + /* prebuffering */ + ivtv_vapi(itv, CX2341X_DEC_SET_PREBUFFERING, 1, 1); + + /* extract from user packets */ + ivtv_vapi_result(itv, data, CX2341X_DEC_EXTRACT_VBI, 1, 1); + itv->vbi.dec_start = data[0]; + + IVTV_DEBUG_INFO("Decoder VBI RE-Insert start 0x%08x size 0x%08x\n", + itv->vbi.dec_start, data[1]); + + /* set decoder source settings */ + /* Data type: 0 = mpeg from host, + 1 = yuv from encoder, + 2 = yuv_from_host */ + switch (s->type) { + case IVTV_DEC_STREAM_TYPE_YUV: + datatype = itv->output_mode == OUT_PASSTHROUGH ? 1 : 2; + IVTV_DEBUG_INFO("Setup DEC YUV Stream data[0] = %d\n", datatype); + break; + case IVTV_DEC_STREAM_TYPE_MPG: + default: + datatype = 0; + break; + } + if (ivtv_vapi(itv, CX2341X_DEC_SET_DECODER_SOURCE, 4, datatype, + itv->params.width, itv->params.height, itv->params.audio_properties)) { + IVTV_DEBUG_WARN("COULDN'T INITIALIZE DECODER SOURCE\n"); + } + return 0; +} + +int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset) +{ + struct ivtv *itv = s->itv; + + if (s->v4l2dev == NULL) + return -EINVAL; + + if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags)) + return 0; /* already started */ + + IVTV_DEBUG_INFO("Starting decode stream %s (gop_offset %d)\n", s->name, gop_offset); + + /* Clear Streamoff */ + if (s->type == IVTV_DEC_STREAM_TYPE_YUV) { + /* Initialize Decoder */ + /* Reprogram Decoder YUV Buffers for YUV */ + write_reg(yuv_offset[0] >> 4, 0x82c); + write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x830); + write_reg(yuv_offset[0] >> 4, 0x834); + write_reg((yuv_offset[0] + IVTV_YUV_BUFFER_UV_OFFSET) >> 4, 0x838); + + write_reg_sync(0x00000000 | (0x0c << 16) | (0x0b << 8), 0x2d24); + + write_reg_sync(0x00108080, 0x2898); + /* Enable YUV decoder output */ + write_reg_sync(0x01, IVTV_REG_VDM); + } + + ivtv_setup_v4l2_decode_stream(s); + + /* set dma size to 65536 bytes */ + ivtv_vapi(itv, CX2341X_DEC_SET_DMA_BLOCK_SIZE, 1, 65536); + + clear_bit(IVTV_F_S_STREAMOFF, &s->s_flags); + + /* Zero out decoder counters */ + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[0]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[1]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[2]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_FIELD_DISPLAYED].data[3]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[0]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[1]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[2]); + writel(0, &itv->dec_mbox.mbox[IVTV_MBOX_DMA].data[3]); + + /* turn on notification of dual/stereo mode change */ + ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 1, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); + + /* start playback */ + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, gop_offset, 0); + + /* Clear the following Interrupt mask bits for decoding */ + ivtv_clear_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + IVTV_DEBUG_IRQ("IRQ Mask is now: 0x%08x\n", itv->irqmask); + + /* you're live! sit back and await interrupts :) */ + atomic_inc(&itv->decoding); + return 0; +} + +void ivtv_stop_all_captures(struct ivtv *itv) +{ + int i; + + for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) { + struct ivtv_stream *s = &itv->streams[i]; + + if (s->v4l2dev == NULL) + continue; + if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + ivtv_stop_v4l2_encode_stream(s, 0); + } + } +} + +int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end) +{ + struct ivtv *itv = s->itv; + DECLARE_WAITQUEUE(wait, current); + int cap_type; + unsigned long then; + int stopmode; + u32 data[CX2341X_MBOX_MAX_DATA]; + + if (s->v4l2dev == NULL) + return -EINVAL; + + /* This function assumes that you are allowed to stop the capture + and that we are actually capturing */ + + IVTV_DEBUG_INFO("Stop Capture\n"); + + if (s->type == IVTV_DEC_STREAM_TYPE_VOUT) + return 0; + if (atomic_read(&itv->capturing) == 0) + return 0; + + switch (s->type) { + case IVTV_ENC_STREAM_TYPE_YUV: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_PCM: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_VBI: + cap_type = 1; + break; + case IVTV_ENC_STREAM_TYPE_MPG: + default: + cap_type = 0; + break; + } + + /* Stop Capture Mode */ + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { + stopmode = 0; + } else { + stopmode = 1; + } + + /* end_capture */ + /* when: 0 = end of GOP 1 = NOW!, type: 0 = mpeg, subtype: 3 = video+audio */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, stopmode, cap_type, s->subtype); + + /* only run these if we're shutting down the last cap */ + if (atomic_read(&itv->capturing) - 1 == 0) { + /* event notification (off) */ + if (test_and_clear_bit(IVTV_F_I_DIG_RST, &itv->i_flags)) { + /* type: 0 = refresh */ + /* on/off: 0 = off, intr: 0x10000000, mbox_id: -1: none */ + ivtv_vapi(itv, CX2341X_ENC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_ENC_VIM_RST, -1); + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VIM_RST); + } + } + + then = jiffies; + + if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { + if (s->type == IVTV_ENC_STREAM_TYPE_MPG && gop_end) { + /* only run these if we're shutting down the last cap */ + unsigned long duration; + + then = jiffies; + 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 / 100); + } + + /* To convert jiffies to ms, we must multiply by 1000 + * and divide by HZ. To avoid runtime division, we + * convert this to multiplication by 1000/HZ. + * Since integer division truncates, we get the best + * accuracy if we do a rounding calculation of the constant. + * Think of the case where HZ is 1024. + */ + duration = ((1000 + HZ / 2) / HZ) * (jiffies - then); + + if (!test_bit(IVTV_F_I_EOS, &itv->i_flags)) { + IVTV_DEBUG_WARN("%s: EOS interrupt not received! stopping anyway.\n", s->name); + IVTV_DEBUG_WARN("%s: waited %lu ms.\n", s->name, duration); + } else { + IVTV_DEBUG_INFO("%s: EOS took %lu ms to occur.\n", s->name, duration); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&itv->cap_w, &wait); + } + + then = jiffies; + /* Make sure DMA is complete */ + add_wait_queue(&s->waitq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + do { + /* check if DMA is pending */ + if ((s->type == IVTV_ENC_STREAM_TYPE_MPG) && /* MPG Only */ + (read_reg(IVTV_REG_DMASTATUS) & 0x02)) { + /* Check for last DMA */ + ivtv_vapi_result(itv, data, CX2341X_ENC_GET_SEQ_END, 2, 0, 0); + + if (data[0] == 1) { + IVTV_DEBUG_DMA("%s: Last DMA of size 0x%08x\n", s->name, data[1]); + break; + } + } else if (read_reg(IVTV_REG_DMASTATUS) & 0x02) { + break; + } + + ivtv_sleep_timeout(HZ / 100, 1); + } while (then + HZ * 2 > jiffies); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->waitq, &wait); + } + + atomic_dec(&itv->capturing); + + /* Clear capture and no-read bits */ + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + + if (s->type == IVTV_ENC_STREAM_TYPE_VBI) + ivtv_set_irq_mask(itv, IVTV_IRQ_ENC_VBI_CAP); + + if (atomic_read(&itv->capturing) > 0) { + return 0; + } + + /* Set the following Interrupt mask bits for capture */ + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_CAPTURE); + + wake_up(&s->waitq); + + return 0; +} + +int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts) +{ + struct ivtv *itv = s->itv; + + if (s->v4l2dev == NULL) + return -EINVAL; + + if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG) + return -EINVAL; + + if (!test_bit(IVTV_F_S_STREAMING, &s->s_flags)) + return 0; + + IVTV_DEBUG_INFO("Stop Decode at %llu, flags: %x\n", pts, flags); + + /* Stop Decoder */ + if (!(flags & VIDEO_CMD_STOP_IMMEDIATELY) || pts) { + u32 tmp = 0; + + /* Wait until the decoder is no longer running */ + if (pts) { + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, + 0, (u32)(pts & 0xffffffff), (u32)(pts >> 32)); + } + while (1) { + u32 data[CX2341X_MBOX_MAX_DATA]; + ivtv_vapi_result(itv, data, CX2341X_DEC_GET_XFER_INFO, 0); + if (s->q_full.buffers + s->q_dma.buffers == 0) { + if (tmp == data[3]) + break; + tmp = data[3]; + } + if (ivtv_sleep_timeout(HZ/10, 1)) + break; + } + } + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, flags & VIDEO_CMD_STOP_TO_BLACK, 0, 0); + + /* turn off notification of dual/stereo mode change */ + ivtv_vapi(itv, CX2341X_DEC_SET_EVENT_NOTIFICATION, 4, 0, 0, IVTV_IRQ_DEC_AUD_MODE_CHG, -1); + + ivtv_set_irq_mask(itv, IVTV_IRQ_MASK_DECODE); + + clear_bit(IVTV_F_S_NEEDS_DATA, &s->s_flags); + clear_bit(IVTV_F_S_STREAMING, &s->s_flags); + ivtv_flush_queues(s); + + if (!test_bit(IVTV_F_S_PASSTHROUGH, &s->s_flags)) { + /* disable VBI on TV-out */ + ivtv_disable_vbi(itv); + } + + /* decrement decoding */ + atomic_dec(&itv->decoding); + + set_bit(IVTV_F_I_EV_DEC_STOPPED, &itv->i_flags); + wake_up(&itv->event_waitq); + + /* wake up wait queues */ + wake_up(&s->waitq); + + return 0; +} + +int ivtv_passthrough_mode(struct ivtv *itv, int enable) +{ + struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV]; + struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV]; + + if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL) + return -EINVAL; + + IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n"); + + /* Prevent others from starting/stopping streams while we + initiate/terminate passthrough mode */ + if (enable) { + if (itv->output_mode == OUT_PASSTHROUGH) { + return 0; + } + if (ivtv_set_output_mode(itv, OUT_PASSTHROUGH) != OUT_PASSTHROUGH) + return -EBUSY; + + /* Fully initialize stream, and then unflag init */ + set_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); + set_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); + + /* Setup YUV Decoder */ + ivtv_setup_v4l2_decode_stream(dec_stream); + + /* Start Decoder */ + ivtv_vapi(itv, CX2341X_DEC_START_PLAYBACK, 2, 0, 1); + atomic_inc(&itv->decoding); + + /* Setup capture if not already done */ + if (atomic_read(&itv->capturing) == 0) { + cx2341x_update(itv, ivtv_api_func, NULL, &itv->params); + } + + /* Start Passthrough Mode */ + ivtv_vapi(itv, CX2341X_ENC_START_CAPTURE, 2, 2, 11); + atomic_inc(&itv->capturing); + return 0; + } + + if (itv->output_mode != OUT_PASSTHROUGH) + return 0; + + /* Stop Passthrough Mode */ + ivtv_vapi(itv, CX2341X_ENC_STOP_CAPTURE, 3, 1, 2, 11); + ivtv_vapi(itv, CX2341X_DEC_STOP_PLAYBACK, 3, 1, 0, 0); + + atomic_dec(&itv->capturing); + atomic_dec(&itv->decoding); + clear_bit(IVTV_F_S_PASSTHROUGH, &dec_stream->s_flags); + clear_bit(IVTV_F_S_STREAMING, &dec_stream->s_flags); + itv->output_mode = OUT_NONE; + + return 0; +} diff --git a/drivers/media/video/ivtv/ivtv-streams.h b/drivers/media/video/ivtv/ivtv-streams.h new file mode 100644 index 000000000000..8597b75384a7 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-streams.h @@ -0,0 +1,31 @@ +/* + init/start/stop/exit stream functions + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_streams_setup(struct ivtv *itv); +void ivtv_streams_cleanup(struct ivtv *itv); + +/* Capture related */ +int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s); +int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end); +int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset); +int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts); + +void ivtv_stop_all_captures(struct ivtv *itv); +int ivtv_passthrough_mode(struct ivtv *itv, int enable); diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c new file mode 100644 index 000000000000..bd642e1aafc3 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-udma.c @@ -0,0 +1,200 @@ +/* + User DMA + + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-streams.h" +#include "ivtv-udma.h" + +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size) +{ + dma_page->uaddr = first & PAGE_MASK; + dma_page->offset = first & ~PAGE_MASK; + dma_page->tail = 1 + ((first+size-1) & ~PAGE_MASK); + dma_page->first = (first & PAGE_MASK) >> PAGE_SHIFT; + dma_page->last = ((first+size-1) & PAGE_MASK) >> PAGE_SHIFT; + dma_page->page_count = dma_page->last - dma_page->first + 1; + if (dma_page->page_count == 1) dma_page->tail -= dma_page->offset; +} + +int ivtv_udma_fill_sg_list (struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset) +{ + int i, offset; + + offset = dma_page->offset; + + /* Fill SG Array with new values */ + for (i = 0; i < dma_page->page_count; i++) { + if (i == dma_page->page_count - 1) { + dma->SGlist[map_offset].length = dma_page->tail; + } + else { + dma->SGlist[map_offset].length = PAGE_SIZE - offset; + } + dma->SGlist[map_offset].offset = offset; + dma->SGlist[map_offset].page = dma->map[map_offset]; + offset = 0; + map_offset++; + } + return map_offset; +} + +void ivtv_udma_fill_sg_array (struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split) { + int i; + struct scatterlist *sg; + + for (i = 0, sg = dma->SGlist; i < dma->SG_length; i++, sg++) { + dma->SGarray[i].size = cpu_to_le32(sg_dma_len(sg)); + dma->SGarray[i].src = cpu_to_le32(sg_dma_address(sg)); + dma->SGarray[i].dst = cpu_to_le32(buffer_offset); + buffer_offset += sg_dma_len(sg); + + split -= sg_dma_len(sg); + if (split == 0) + buffer_offset = buffer_offset_2; + } +} + +/* User DMA Buffers */ +void ivtv_udma_alloc(struct ivtv *itv) +{ + if (itv->udma.SG_handle == 0) { + /* Map DMA Page Array Buffer */ + itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + ivtv_udma_sync_for_cpu(itv); + } +} + +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes) +{ + struct ivtv_dma_page_info user_dma; + struct ivtv_user_dma *dma = &itv->udma; + int err; + + IVTV_DEBUG_DMA("ivtv_udma_setup, dst: 0x%08x\n", (unsigned int)ivtv_dest_addr); + + /* Still in USE */ + if (dma->SG_length || dma->page_count) { + IVTV_DEBUG_WARN("ivtv_udma_setup: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); + return -EBUSY; + } + + ivtv_udma_get_page_info(&user_dma, (unsigned long)userbuf, size_in_bytes); + + if (user_dma.page_count <= 0) { + IVTV_DEBUG_WARN("ivtv_udma_setup: Error %d page_count from %d bytes %d offset\n", + user_dma.page_count, size_in_bytes, user_dma.offset); + return -EINVAL; + } + + /* Get user pages for DMA Xfer */ + down_read(¤t->mm->mmap_sem); + err = get_user_pages(current, current->mm, + user_dma.uaddr, user_dma.page_count, 0, 1, dma->map, NULL); + up_read(¤t->mm->mmap_sem); + + if (user_dma.page_count != err) { + IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", + err, user_dma.page_count); + return -EINVAL; + } + + dma->page_count = user_dma.page_count; + + /* Fill SG List with new values */ + ivtv_udma_fill_sg_list(dma, &user_dma, 0); + + /* Map SG List */ + dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + + /* Fill SG Array with new values */ + ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1); + + /* Tag SG Array with Interrupt Bit */ + dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); + + ivtv_udma_sync_for_device(itv); + return dma->page_count; +} + +void ivtv_udma_unmap(struct ivtv *itv) +{ + struct ivtv_user_dma *dma = &itv->udma; + int i; + + IVTV_DEBUG_INFO("ivtv_unmap_user_dma\n"); + + /* Nothing to free */ + if (dma->page_count == 0) + return; + + /* Unmap Scatterlist */ + if (dma->SG_length) { + pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + dma->SG_length = 0; + } + /* sync DMA */ + ivtv_udma_sync_for_cpu(itv); + + /* Release User Pages */ + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; +} + +void ivtv_udma_free(struct ivtv *itv) +{ + /* Unmap SG Array */ + if (itv->udma.SG_handle) { + pci_unmap_single(itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); + } + + /* Unmap Scatterlist */ + if (itv->udma.SG_length) { + pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE); + } +} + +void ivtv_udma_start(struct ivtv *itv) +{ + IVTV_DEBUG_DMA("start UDMA\n"); + write_reg(itv->udma.SG_handle, IVTV_REG_DECDMAADDR); + write_reg_sync(read_reg(IVTV_REG_DMAXFER) | 0x01, IVTV_REG_DMAXFER); + set_bit(IVTV_F_I_DMA, &itv->i_flags); + set_bit(IVTV_F_I_UDMA, &itv->i_flags); +} + +void ivtv_udma_prepare(struct ivtv *itv) +{ + unsigned long flags; + + spin_lock_irqsave(&itv->dma_reg_lock, flags); + if (!test_bit(IVTV_F_I_DMA, &itv->i_flags)) + ivtv_udma_start(itv); + else + set_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags); + spin_unlock_irqrestore(&itv->dma_reg_lock, flags); +} diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h new file mode 100644 index 000000000000..e131bccedec0 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-udma.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004 Chris Kennedy + Copyright (C) 2006-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* User DMA functions */ +void ivtv_udma_get_page_info(struct ivtv_dma_page_info *dma_page, unsigned long first, unsigned long size); +int ivtv_udma_fill_sg_list(struct ivtv_user_dma *dma, struct ivtv_dma_page_info *dma_page, int map_offset); +void ivtv_udma_fill_sg_array(struct ivtv_user_dma *dma, u32 buffer_offset, u32 buffer_offset_2, u32 split); +int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr, + void __user *userbuf, int size_in_bytes); +void ivtv_udma_unmap(struct ivtv *itv); +void ivtv_udma_free(struct ivtv *itv); +void ivtv_udma_alloc(struct ivtv *itv); +void ivtv_udma_prepare(struct ivtv *itv); +void ivtv_udma_start(struct ivtv *itv); + +static inline void ivtv_udma_sync_for_device(struct ivtv *itv) +{ + pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} + +static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv) +{ + pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle, + sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE); +} diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c new file mode 100644 index 000000000000..b53ca508dacc --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-vbi.c @@ -0,0 +1,545 @@ +/* + Vertical Blank Interval support functions + Copyright (C) 2004-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-video.h" +#include "ivtv-vbi.h" +#include "ivtv-ioctl.h" +#include "ivtv-queue.h" + +static int odd_parity(u8 c) +{ + c ^= (c >> 4); + c ^= (c >> 2); + c ^= (c >> 1); + + return c & 1; +} + +void vbi_schedule_work(struct ivtv *itv) +{ + queue_work(itv->vbi.work_queues, &itv->vbi.work_queue); +} + +static void passthrough_vbi_data(struct ivtv *itv, int cnt) +{ + int wss = 0; + u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 }; + u8 vps[13]; + int found_cc = 0; + int found_wss = 0; + int found_vps = 0; + int cc_pos = itv->vbi.cc_pos; + int i; + + for (i = 0; i < cnt; i++) { + struct v4l2_sliced_vbi_data *d = itv->vbi.sliced_dec_data + i; + + if (d->id == V4L2_SLICED_CAPTION_525 && d->line == 21) { + found_cc = 1; + if (d->field) { + cc[2] = d->data[0]; + cc[3] = d->data[1]; + } else { + cc[0] = d->data[0]; + cc[1] = d->data[1]; + } + } + else if (d->id == V4L2_SLICED_VPS && d->line == 16 && d->field == 0) { + memcpy(vps, d->data, sizeof(vps)); + found_vps = 1; + } + else if (d->id == V4L2_SLICED_WSS_625 && d->line == 23 && d->field == 0) { + wss = d->data[0] | d->data[1] << 8; + found_wss = 1; + } + } + + if (itv->vbi.wss_found != found_wss || itv->vbi.wss != wss) { + itv->vbi.wss = wss; + itv->vbi.wss_found = found_wss; + set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + } + + if (found_vps || itv->vbi.vps_found) { + itv->vbi.vps[0] = vps[2]; + itv->vbi.vps[1] = vps[8]; + itv->vbi.vps[2] = vps[9]; + itv->vbi.vps[3] = vps[10]; + itv->vbi.vps[4] = vps[11]; + itv->vbi.vps_found = found_vps; + set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + } + + if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) { + itv->vbi.cc_data_odd[cc_pos] = cc[0]; + itv->vbi.cc_data_odd[cc_pos + 1] = cc[1]; + itv->vbi.cc_data_even[cc_pos] = cc[2]; + itv->vbi.cc_data_even[cc_pos + 1] = cc[3]; + itv->vbi.cc_pos = cc_pos + 2; + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + } +} + +static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp) +{ + int line = 0; + int i; + u32 linemask[2] = { 0, 0 }; + unsigned short size; + static const u8 mpeg_hdr_data[] = { + 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66, + 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff, + 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80, + 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff + }; + const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */ + int idx = itv->vbi.frame % IVTV_VBI_FRAMES; + u8 *dst = &itv->vbi.sliced_mpeg_data[idx][0]; + + for (i = 0; i < lines; i++) { + int f, l; + + if (itv->vbi.sliced_data[i].id == 0) + continue; + + l = itv->vbi.sliced_data[i].line - 6; + f = itv->vbi.sliced_data[i].field; + if (f) + l += 18; + if (l < 32) + linemask[0] |= (1 << l); + else + linemask[1] |= (1 << (l - 32)); + dst[sd + 12 + line * 43] = service2vbi(itv->vbi.sliced_data[i].id); + memcpy(dst + sd + 12 + line * 43 + 1, itv->vbi.sliced_data[i].data, 42); + line++; + } + memcpy(dst, mpeg_hdr_data, sizeof(mpeg_hdr_data)); + if (line == 36) { + /* All lines are used, so there is no space for the linemask + (the max size of the VBI data is 36 * 43 + 4 bytes). + So in this case we use the magic number 'ITV0'. */ + memcpy(dst + sd, "ITV0", 4); + memcpy(dst + sd + 4, dst + sd + 12, line * 43); + size = 4 + ((43 * line + 3) & ~3); + } else { + memcpy(dst + sd, "itv0", 4); + memcpy(dst + sd + 4, &linemask[0], 8); + size = 12 + ((43 * line + 3) & ~3); + } + dst[4+16] = (size + 10) >> 8; + dst[5+16] = (size + 10) & 0xff; + dst[9+16] = 0x21 | ((pts_stamp >> 29) & 0x6); + dst[10+16] = (pts_stamp >> 22) & 0xff; + dst[11+16] = 1 | ((pts_stamp >> 14) & 0xff); + dst[12+16] = (pts_stamp >> 7) & 0xff; + dst[13+16] = 1 | ((pts_stamp & 0x7f) << 1); + itv->vbi.sliced_mpeg_size[idx] = sd + size; +} + +static int ivtv_convert_ivtv_vbi(struct ivtv *itv, u8 *p) +{ + u32 linemask[2]; + int i, l, id2; + int line = 0; + + if (!memcmp(p, "itv0", 4)) { + memcpy(linemask, p + 4, 8); + p += 12; + } else if (!memcmp(p, "ITV0", 4)) { + linemask[0] = 0xffffffff; + linemask[1] = 0xf; + p += 4; + } else { + /* unknown VBI data stream */ + return 0; + } + for (i = 0; i < 36; i++) { + int err = 0; + + if (i < 32 && !(linemask[0] & (1 << i))) + continue; + if (i >= 32 && !(linemask[1] & (1 << (i - 32)))) + continue; + id2 = *p & 0xf; + switch (id2) { + case IVTV_SLICED_TYPE_TELETEXT_B: + id2 = V4L2_SLICED_TELETEXT_B; + break; + case IVTV_SLICED_TYPE_CAPTION_525: + id2 = V4L2_SLICED_CAPTION_525; + err = !odd_parity(p[1]) || !odd_parity(p[2]); + break; + case IVTV_SLICED_TYPE_VPS: + id2 = V4L2_SLICED_VPS; + break; + case IVTV_SLICED_TYPE_WSS_625: + id2 = V4L2_SLICED_WSS_625; + break; + default: + id2 = 0; + break; + } + if (err == 0) { + l = (i < 18) ? i + 6 : i - 18 + 6; + itv->vbi.sliced_dec_data[line].line = l; + itv->vbi.sliced_dec_data[line].field = i >= 18; + itv->vbi.sliced_dec_data[line].id = id2; + memcpy(itv->vbi.sliced_dec_data[line].data, p + 1, 42); + line++; + } + p += 43; + } + while (line < 36) { + itv->vbi.sliced_dec_data[line].id = 0; + itv->vbi.sliced_dec_data[line].line = 0; + itv->vbi.sliced_dec_data[line].field = 0; + line++; + } + return line * sizeof(itv->vbi.sliced_dec_data[0]); +} + +ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count) +{ + /* Should be a __user pointer, but sparse doesn't parse this bit correctly. */ + const struct v4l2_sliced_vbi_data *p = (const struct v4l2_sliced_vbi_data *)ubuf; + u8 cc[4] = { 0x80, 0x80, 0x80, 0x80 }; + int found_cc = 0; + int cc_pos = itv->vbi.cc_pos; + + if (itv->vbi.service_set_out == 0) + return -EPERM; + + while (count >= sizeof(struct v4l2_sliced_vbi_data)) { + switch (p->id) { + case V4L2_SLICED_CAPTION_525: + if (p->id == V4L2_SLICED_CAPTION_525 && + p->line == 21 && + (itv->vbi.service_set_out & + V4L2_SLICED_CAPTION_525) == 0) { + break; + } + found_cc = 1; + if (p->field) { + cc[2] = p->data[0]; + cc[3] = p->data[1]; + } else { + cc[0] = p->data[0]; + cc[1] = p->data[1]; + } + break; + + case V4L2_SLICED_VPS: + if (p->line == 16 && p->field == 0 && + (itv->vbi.service_set_out & V4L2_SLICED_VPS)) { + itv->vbi.vps[0] = p->data[2]; + itv->vbi.vps[1] = p->data[8]; + itv->vbi.vps[2] = p->data[9]; + itv->vbi.vps[3] = p->data[10]; + itv->vbi.vps[4] = p->data[11]; + itv->vbi.vps_found = 1; + set_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + } + break; + + case V4L2_SLICED_WSS_625: + if (p->line == 23 && p->field == 0 && + (itv->vbi.service_set_out & V4L2_SLICED_WSS_625)) { + /* No lock needed for WSS */ + itv->vbi.wss = p->data[0] | (p->data[1] << 8); + itv->vbi.wss_found = 1; + set_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + } + break; + + default: + break; + } + count -= sizeof(*p); + p++; + } + + if (found_cc && cc_pos < sizeof(itv->vbi.cc_data_even)) { + itv->vbi.cc_data_odd[cc_pos] = cc[0]; + itv->vbi.cc_data_odd[cc_pos + 1] = cc[1]; + itv->vbi.cc_data_even[cc_pos] = cc[2]; + itv->vbi.cc_data_even[cc_pos + 1] = cc[3]; + itv->vbi.cc_pos = cc_pos + 2; + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + } + + return (const char __user *)p - ubuf; +} + +/* Compress raw VBI format, removes leading SAV codes and surplus space after the + field. + Returns new compressed size. */ +static u32 compress_raw_buf(struct ivtv *itv, u8 *buf, u32 size) +{ + u32 line_size = itv->vbi.raw_decoder_line_size; + u32 lines = itv->vbi.count; + u8 sav1 = itv->vbi.raw_decoder_sav_odd_field; + u8 sav2 = itv->vbi.raw_decoder_sav_even_field; + u8 *q = buf; + u8 *p; + int i; + + for (i = 0; i < lines; i++) { + p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || (p[3] != sav1 && p[3] != sav2)) { + break; + } + memcpy(q, p + 4, line_size - 4); + q += line_size - 4; + } + return lines * (line_size - 4); +} + + +/* Compressed VBI format, all found sliced blocks put next to one another + Returns new compressed size */ +static u32 compress_sliced_buf(struct ivtv *itv, u32 line, u8 *buf, u32 size, u8 sav) +{ + u32 line_size = itv->vbi.sliced_decoder_line_size; + struct v4l2_decode_vbi_line vbi; + int i; + + /* find the first valid line */ + for (i = 0; i < size; i++, buf++) { + if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav) + break; + } + + size -= i; + if (size < line_size) { + return line; + } + for (i = 0; i < size / line_size; i++) { + u8 *p = buf + i * line_size; + + /* Look for SAV code */ + if (p[0] != 0xff || p[1] || p[2] || p[3] != sav) { + continue; + } + vbi.p = p + 4; + itv->video_dec_func(itv, VIDIOC_INT_DECODE_VBI_LINE, &vbi); + if (vbi.type) { + itv->vbi.sliced_data[line].id = vbi.type; + itv->vbi.sliced_data[line].field = vbi.is_second_field; + itv->vbi.sliced_data[line].line = vbi.line; + memcpy(itv->vbi.sliced_data[line].data, vbi.p, 42); + line++; + } + } + return line; +} + +void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, + u64 pts_stamp, int streamtype) +{ + u8 *p = (u8 *) buf->buf; + u32 size = buf->bytesused; + int y; + + /* Raw VBI data */ + if (streamtype == IVTV_ENC_STREAM_TYPE_VBI && itv->vbi.sliced_in->service_set == 0) { + u8 type; + + ivtv_buf_swap(buf); + + type = p[3]; + + size = buf->bytesused = compress_raw_buf(itv, p, size); + + /* second field of the frame? */ + if (type == itv->vbi.raw_decoder_sav_even_field) { + /* Dirty hack needed for backwards + compatibility of old VBI software. */ + p += size - 4; + memcpy(p, &itv->vbi.frame, 4); + itv->vbi.frame++; + } + return; + } + + /* Sliced VBI data with data insertion */ + if (streamtype == IVTV_ENC_STREAM_TYPE_VBI) { + int lines; + + ivtv_buf_swap(buf); + + /* first field */ + lines = compress_sliced_buf(itv, 0, p, size / 2, + itv->vbi.sliced_decoder_sav_odd_field); + /* second field */ + /* experimentation shows that the second half does not always begin + at the exact address. So start a bit earlier (hence 32). */ + lines = compress_sliced_buf(itv, lines, p + size / 2 - 32, size / 2 + 32, + itv->vbi.sliced_decoder_sav_even_field); + /* always return at least one empty line */ + if (lines == 0) { + itv->vbi.sliced_data[0].id = 0; + itv->vbi.sliced_data[0].line = 0; + itv->vbi.sliced_data[0].field = 0; + lines = 1; + } + buf->bytesused = size = lines * sizeof(itv->vbi.sliced_data[0]); + memcpy(p, &itv->vbi.sliced_data[0], size); + + if (itv->vbi.insert_mpeg) { + copy_vbi_data(itv, lines, pts_stamp); + } + itv->vbi.frame++; + return; + } + + /* Sliced VBI re-inserted from an MPEG stream */ + if (streamtype == IVTV_DEC_STREAM_TYPE_VBI) { + /* If the size is not 4-byte aligned, then the starting address + for the swapping is also shifted. After swapping the data the + real start address of the VBI data is exactly 4 bytes after the + original start. It's a bit fiddly but it works like a charm. + Non-4-byte alignment happens when an lseek is done on the input + mpeg file to a non-4-byte aligned position. So on arrival here + the VBI data is also non-4-byte aligned. */ + int offset = size & 3; + int cnt; + + if (offset) { + p += 4 - offset; + } + /* Swap Buffer */ + for (y = 0; y < size; y += 4) { + swab32s((u32 *)(p + y)); + } + + cnt = ivtv_convert_ivtv_vbi(itv, p + offset); + memcpy(buf->buf, itv->vbi.sliced_dec_data, cnt); + buf->bytesused = cnt; + + passthrough_vbi_data(itv, cnt / sizeof(itv->vbi.sliced_dec_data[0])); + return; + } +} + +void ivtv_disable_vbi(struct ivtv *itv) +{ + clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags); + clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags); + clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + ivtv_set_wss(itv, 0, 0); + ivtv_set_cc(itv, 0, 0, 0, 0, 0); + ivtv_set_vps(itv, 0, 0, 0, 0, 0, 0); + itv->vbi.vps_found = itv->vbi.wss_found = 0; + itv->vbi.wss = 0; + itv->vbi.cc_pos = 0; +} + +void vbi_work_handler(struct work_struct *work) +{ + struct vbi_info *info = container_of(work, struct vbi_info, work_queue); + struct ivtv *itv = container_of(info, struct ivtv, vbi); + struct v4l2_sliced_vbi_data data; + DEFINE_WAIT(wait); + + /* Lock */ + if (itv->output_mode == OUT_PASSTHROUGH) { + /* Note: currently only the saa7115 is used in a PVR350, + so these commands are for now saa7115 specific. */ + if (itv->is_50hz) { + data.id = V4L2_SLICED_WSS_625; + data.field = 0; + + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + ivtv_set_wss(itv, 1, data.data[0] & 0xf); + itv->vbi.wss_no_update = 0; + } else if (itv->vbi.wss_no_update == 4) { + ivtv_set_wss(itv, 1, 0x8); /* 4x3 full format */ + } else { + itv->vbi.wss_no_update++; + } + } + else { + u8 c1 = 0, c2 = 0, c3 = 0, c4 = 0; + int mode = 0; + + data.id = V4L2_SLICED_CAPTION_525; + data.field = 0; + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + mode |= 1; + c1 = data.data[0]; + c2 = data.data[1]; + } + data.field = 1; + if (itv->video_dec_func(itv, VIDIOC_INT_G_VBI_DATA, &data) == 0) { + mode |= 2; + c3 = data.data[0]; + c4 = data.data[1]; + } + if (mode) { + itv->vbi.cc_no_update = 0; + ivtv_set_cc(itv, mode, c1, c2, c3, c4); + } else if (itv->vbi.cc_no_update == 4) { + ivtv_set_cc(itv, 0, 0, 0, 0, 0); + } else { + itv->vbi.cc_no_update++; + } + } + return; + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_WSS, &itv->i_flags)) { + /* Lock */ + ivtv_set_wss(itv, itv->vbi.wss_found, itv->vbi.wss & 0xf); + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags)) { + if (itv->vbi.cc_pos == 0) { + ivtv_set_cc(itv, 3, 0x80, 0x80, 0x80, 0x80); + } + while (itv->vbi.cc_pos) { + u8 cc_odd0 = itv->vbi.cc_data_odd[0]; + u8 cc_odd1 = itv->vbi.cc_data_odd[1]; + u8 cc_even0 = itv->vbi.cc_data_even[0]; + u8 cc_even1 = itv->vbi.cc_data_even[1]; + + memcpy(itv->vbi.cc_data_odd, itv->vbi.cc_data_odd + 2, sizeof(itv->vbi.cc_data_odd) - 2); + memcpy(itv->vbi.cc_data_even, itv->vbi.cc_data_even + 2, sizeof(itv->vbi.cc_data_even) - 2); + itv->vbi.cc_pos -= 2; + if (itv->vbi.cc_pos && cc_odd0 == 0x80 && cc_odd1 == 0x80) + continue; + + /* Send to Saa7127 */ + ivtv_set_cc(itv, 3, cc_odd0, cc_odd1, cc_even0, cc_even1); + if (itv->vbi.cc_pos == 0) + set_bit(IVTV_F_I_UPDATE_CC, &itv->i_flags); + break; + } + } + + if (test_and_clear_bit(IVTV_F_I_UPDATE_VPS, &itv->i_flags)) { + /* Lock */ + ivtv_set_vps(itv, itv->vbi.vps_found, + itv->vbi.vps[0], itv->vbi.vps[1], + itv->vbi.vps[2], itv->vbi.vps[3], itv->vbi.vps[4]); + } +} diff --git a/drivers/media/video/ivtv/ivtv-vbi.h b/drivers/media/video/ivtv/ivtv-vbi.h new file mode 100644 index 000000000000..c897e9bd4f92 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-vbi.h @@ -0,0 +1,27 @@ +/* + Vertical Blank Interval support functions + Copyright (C) 2004-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +ssize_t ivtv_write_vbi(struct ivtv *itv, const char __user *ubuf, size_t count); +void ivtv_process_vbi_data(struct ivtv *itv, struct ivtv_buffer *buf, + u64 pts_stamp, int streamtype); +int ivtv_used_line(struct ivtv *itv, int line, int field); +void ivtv_disable_vbi(struct ivtv *itv); +void ivtv_set_vbi(unsigned long arg); +void vbi_work_handler(struct work_struct *work); +void vbi_schedule_work(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h new file mode 100644 index 000000000000..85530a3cd369 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-version.h @@ -0,0 +1,26 @@ +/* + ivtv driver version information + Copyright (C) 2005-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define IVTV_DRIVER_NAME "ivtv" +#define IVTV_DRIVER_VERSION_MAJOR 1 +#define IVTV_DRIVER_VERSION_MINOR 0 +#define IVTV_DRIVER_VERSION_PATCHLEVEL 0 + +#define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL) +#define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/ivtv/ivtv-video.c b/drivers/media/video/ivtv/ivtv-video.c new file mode 100644 index 000000000000..77e42d13cdee --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-video.c @@ -0,0 +1,150 @@ +/* + saa7127 interface functions + Copyright (C) 2004-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-video.h" +#include "ivtv-i2c.h" +#include "ivtv-gpio.h" +#include "ivtv-cards.h" +#include +#include + +void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3, + u8 vps4, u8 vps5) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + data.id = V4L2_SLICED_VPS; + data.field = 0; + data.line = enabled ? 16 : 0; + data.data[4] = vps1; + data.data[10] = vps2; + data.data[11] = vps3; + data.data[12] = vps4; + data.data[13] = vps5; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + data.id = V4L2_SLICED_CAPTION_525; + data.field = 0; + data.line = (mode & 1) ? 21 : 0; + data.data[0] = cc1; + data.data[1] = cc2; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); + data.field = 1; + data.line = (mode & 2) ? 21 : 0; + data.data[0] = cc3; + data.data[1] = cc4; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_set_wss(struct ivtv *itv, int enabled, int mode) +{ + struct v4l2_sliced_vbi_data data; + + if (!(itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) + return; + /* When using a 50 Hz system, always turn on the + wide screen signal with 4x3 ratio as the default. + Turning this signal on and off can confuse certain + TVs. As far as I can tell there is no reason not to + transmit this signal. */ + if ((itv->std & V4L2_STD_625_50) && !enabled) { + enabled = 1; + mode = 0x08; /* 4x3 full format */ + } + data.id = V4L2_SLICED_WSS_625; + data.field = 0; + data.line = enabled ? 23 : 0; + data.data[0] = mode & 0xff; + data.data[1] = (mode >> 8) & 0xff; + ivtv_saa7127(itv, VIDIOC_INT_S_VBI_DATA, &data); +} + +void ivtv_encoder_enable(struct ivtv *itv, int enabled) +{ + if (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT) { + ivtv_saa7127(itv, enabled ? VIDIOC_STREAMON : VIDIOC_STREAMOFF, + &enabled); + } +} + +void ivtv_video_set_io(struct ivtv *itv) +{ + struct v4l2_routing route; + int inp = itv->active_input; + u32 type; + + route.input = itv->card->video_inputs[inp].video_input; + route.output = 0; + itv->video_dec_func(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + + type = itv->card->video_inputs[inp].video_type; + + if (type == IVTV_CARD_INPUT_VID_TUNER) { + route.input = 0; /* Tuner */ + } else if (type < IVTV_CARD_INPUT_COMPOSITE1) { + route.input = 2; /* S-Video */ + } else { + route.input = 1; /* Composite */ + } + + if (itv->card->hw_video & IVTV_HW_GPIO) + ivtv_gpio(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + + if (itv->card->hw_video & IVTV_HW_UPD64031A) { + if (type == IVTV_CARD_INPUT_VID_TUNER || + type >= IVTV_CARD_INPUT_COMPOSITE1) { + /* Composite: GR on, connect to 3DYCS */ + route.input = UPD64031A_GR_ON | UPD64031A_3DYCS_COMPOSITE; + } else { + /* S-Video: GR bypassed, turn it off */ + route.input = UPD64031A_GR_OFF | UPD64031A_3DYCS_DISABLE; + } + route.input |= itv->card->gr_config; + + ivtv_upd64031a(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + } + + if (itv->card->hw_video & IVTV_HW_UPD6408X) { + route.input = UPD64083_YCS_MODE; + if (type > IVTV_CARD_INPUT_VID_TUNER && + type < IVTV_CARD_INPUT_COMPOSITE1) { + /* S-Video uses YCNR mode and internal Y-ADC, the upd64031a + is not used. */ + route.input |= UPD64083_YCNR_MODE; + } + else if (itv->card->hw_video & IVTV_HW_UPD64031A) { + /* Use upd64031a output for tuner and composite(CX23416GYC only) inputs */ + if ((type == IVTV_CARD_INPUT_VID_TUNER)|| + (itv->card->type == IVTV_CARD_CX23416GYC)) { + route.input |= UPD64083_EXT_Y_ADC; + } + } + ivtv_upd64083(itv, VIDIOC_INT_S_VIDEO_ROUTING, &route); + } +} diff --git a/drivers/media/video/ivtv/ivtv-video.h b/drivers/media/video/ivtv/ivtv-video.h new file mode 100644 index 000000000000..5efedebe317b --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-video.h @@ -0,0 +1,25 @@ +/* + saa7127 interface functions + Copyright (C) 2004-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +void ivtv_set_wss(struct ivtv *itv, int enabled, int mode); +void ivtv_set_cc(struct ivtv *itv, int mode, u8 cc1, u8 cc2, u8 cc3, u8 cc4); +void ivtv_set_vps(struct ivtv *itv, int enabled, u8 vps1, u8 vps2, u8 vps3, + u8 vps4, u8 vps5); +void ivtv_encoder_enable(struct ivtv *itv, int enabled); +void ivtv_video_set_io(struct ivtv *itv); diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c new file mode 100644 index 000000000000..e49ecef93046 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-yuv.c @@ -0,0 +1,1129 @@ +/* + yuv support + + Copyright (C) 2007 Ian Armstrong + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "ivtv-driver.h" +#include "ivtv-queue.h" +#include "ivtv-udma.h" +#include "ivtv-irq.h" + +static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma, + struct ivtv_dma_frame *args) +{ + struct ivtv_dma_page_info y_dma; + struct ivtv_dma_page_info uv_dma; + + int i; + int y_pages, uv_pages; + + unsigned long y_buffer_offset, uv_buffer_offset; + int y_decode_height, uv_decode_height, y_size; + int frame = atomic_read(&itv->yuv_info.next_fill_frame); + + y_buffer_offset = IVTV_DEC_MEM_START + yuv_offset[frame]; + uv_buffer_offset = y_buffer_offset + IVTV_YUV_BUFFER_UV_OFFSET; + + y_decode_height = uv_decode_height = args->src.height + args->src.top; + + if (y_decode_height < 512-16) + y_buffer_offset += 720 * 16; + + if (y_decode_height & 15) + y_decode_height = (y_decode_height + 16) & ~15; + + if (uv_decode_height & 31) + uv_decode_height = (uv_decode_height + 32) & ~31; + + y_size = 720 * y_decode_height; + + /* Still in USE */ + if (dma->SG_length || dma->page_count) { + IVTV_DEBUG_WARN("prep_user_dma: SG_length %d page_count %d still full?\n", + dma->SG_length, dma->page_count); + return -EBUSY; + } + + ivtv_udma_get_page_info (&y_dma, (unsigned long)args->y_source, 720 * y_decode_height); + ivtv_udma_get_page_info (&uv_dma, (unsigned long)args->uv_source, 360 * uv_decode_height); + + /* Get user pages for DMA Xfer */ + down_read(¤t->mm->mmap_sem); + y_pages = get_user_pages(current, current->mm, y_dma.uaddr, y_dma.page_count, 0, 1, &dma->map[0], NULL); + uv_pages = get_user_pages(current, current->mm, uv_dma.uaddr, uv_dma.page_count, 0, 1, &dma->map[y_pages], NULL); + up_read(¤t->mm->mmap_sem); + + dma->page_count = y_dma.page_count + uv_dma.page_count; + + if (y_pages + uv_pages != dma->page_count) { + IVTV_DEBUG_WARN("failed to map user pages, returned %d instead of %d\n", + y_pages + uv_pages, dma->page_count); + + for (i = 0; i < dma->page_count; i++) { + put_page(dma->map[i]); + } + dma->page_count = 0; + return -EINVAL; + } + + /* Fill & map SG List */ + ivtv_udma_fill_sg_list (dma, &uv_dma, ivtv_udma_fill_sg_list (dma, &y_dma, 0)); + dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE); + + /* Fill SG Array with new values */ + ivtv_udma_fill_sg_array (dma, y_buffer_offset, uv_buffer_offset, y_size); + + /* If we've offset the y plane, ensure top area is blanked */ + if (args->src.height + args->src.top < 512-16) { + if (itv->yuv_info.blanking_dmaptr) { + dma->SGarray[dma->SG_length].size = cpu_to_le32(720*16); + dma->SGarray[dma->SG_length].src = cpu_to_le32(itv->yuv_info.blanking_dmaptr); + dma->SGarray[dma->SG_length].dst = cpu_to_le32(IVTV_DEC_MEM_START + yuv_offset[frame]); + dma->SG_length++; + } + } + + /* Tag SG Array with Interrupt Bit */ + dma->SGarray[dma->SG_length - 1].size |= cpu_to_le32(0x80000000); + + ivtv_udma_sync_for_device(itv); + return 0; +} + +/* We rely on a table held in the firmware - Quick check. */ +int ivtv_yuv_filter_check(struct ivtv *itv) +{ + int i, offset_y, offset_uv; + + for (i=0, offset_y = 16, offset_uv = 4; i<16; i++, offset_y += 24, offset_uv += 12) { + if ((read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + offset_y) != i << 16) || + (read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + offset_uv) != i << 16)) { + IVTV_WARN ("YUV filter table not found in firmware.\n"); + return -1; + } + } + return 0; +} + +static void ivtv_yuv_filter(struct ivtv *itv, int h_filter, int v_filter_1, int v_filter_2) +{ + int filter_index, filter_line; + + /* If any filter is -1, then don't update it */ + if (h_filter > -1) { + if (h_filter > 4) h_filter = 4; + filter_index = h_filter * 384; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02804); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0281c); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02808); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02820); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0280c); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02824); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02810); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02828); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x02814); + write_reg(read_dec(IVTV_YUV_HORIZONTAL_FILTER_OFFSET + filter_index), 0x0282c); + filter_index += 8; + write_reg(0, 0x02818); + write_reg(0, 0x02830); + filter_line ++; + } + IVTV_DEBUG_YUV("h_filter -> %d\n",h_filter); + } + + if (v_filter_1 > -1) { + if (v_filter_1 > 4) v_filter_1 = 4; + filter_index = v_filter_1 * 192; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02900); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02904); + filter_index += 8; + write_reg(0, 0x02908); + filter_line ++; + } + IVTV_DEBUG_YUV("v_filter_1 -> %d\n",v_filter_1); + } + + if (v_filter_2 > -1) { + if (v_filter_2 > 4) v_filter_2 = 4; + filter_index = v_filter_2 * 192; + filter_line = 0; + while (filter_line < 16) { + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x0290c); + filter_index += 4; + write_reg(read_dec(IVTV_YUV_VERTICAL_FILTER_OFFSET + filter_index), 0x02910); + filter_index += 8; + write_reg(0, 0x02914); + filter_line ++; + } + IVTV_DEBUG_YUV("v_filter_2 -> %d\n",v_filter_2); + } +} + +static void ivtv_yuv_handle_horizontal(struct ivtv *itv, struct yuv_frame_info *window) +{ + u32 reg_2834, reg_2838, reg_283c; + u32 reg_2844, reg_2854, reg_285c; + u32 reg_2864, reg_2874, reg_2890; + u32 reg_2870, reg_2870_base, reg_2870_offset; + int x_cutoff; + int h_filter; + u32 master_width; + + IVTV_DEBUG_WARN( "Need to adjust to width %d src_w %d dst_w %d src_x %d dst_x %d\n", + window->tru_w, window->src_w, window->dst_w,window->src_x, window->dst_x); + + /* How wide is the src image */ + x_cutoff = window->src_w + window->src_x; + + /* Set the display width */ + reg_2834 = window->dst_w; + reg_2838 = reg_2834; + + /* Set the display position */ + reg_2890 = window->dst_x; + + /* Index into the image horizontally */ + reg_2870 = 0; + + /* 2870 is normally fudged to align video coords with osd coords. + If running full screen, it causes an unwanted left shift + Remove the fudge if we almost fill the screen. + Gradually adjust the offset to avoid the video 'snapping' + left/right if it gets dragged through this region. + Only do this if osd is full width. */ + if (window->vis_w == 720) { + if ((window->tru_x - window->pan_x > -1) && (window->tru_x - window->pan_x <= 40) && (window->dst_w >= 680)){ + reg_2870 = 10 - (window->tru_x - window->pan_x) / 4; + } + else if ((window->tru_x - window->pan_x < 0) && (window->tru_x - window->pan_x >= -20) && (window->dst_w >= 660)) { + reg_2870 = (10 + (window->tru_x - window->pan_x) / 2); + } + + if (window->dst_w >= window->src_w) + reg_2870 = reg_2870 << 16 | reg_2870; + else + reg_2870 = ((reg_2870 & ~1) << 15) | (reg_2870 & ~1); + } + + if (window->dst_w < window->src_w) + reg_2870 = 0x000d000e - reg_2870; + else + reg_2870 = 0x0012000e - reg_2870; + + /* We're also using 2870 to shift the image left (src_x & negative dst_x) */ + reg_2870_offset = (window->src_x*((window->dst_w << 21)/window->src_w))>>19; + + if (window->dst_w >= window->src_w) { + x_cutoff &= ~1; + master_width = (window->src_w * 0x00200000) / (window->dst_w); + if (master_width * window->dst_w != window->src_w * 0x00200000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 2; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + + /* We also need to factor in the scaling + (src_w - dst_w) / (src_w / 4) */ + if (window->dst_w > window->src_w) + reg_2870_base = ((window->dst_w - window->src_w)<<16) / (window->src_w <<14); + else + reg_2870_base = 0; + + reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 2) + (reg_2870_base << 17 | reg_2870_base); + reg_2874 = 0; + } + else if (window->dst_w < window->src_w / 2) { + master_width = (window->src_w * 0x00080000) / window->dst_w; + if (master_width * window->dst_w != window->src_w * 0x00080000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 1; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + reg_2870 += (((reg_2870_offset << 15) & 0xFFFF0000) | reg_2870_offset); + reg_2870 += (5 - (((window->src_w + window->src_w / 2) - 1) / window->dst_w)) << 16; + reg_2874 = 0x00000012; + } + else { + master_width = (window->src_w * 0x00100000) / window->dst_w; + if (master_width * window->dst_w != window->src_w * 0x00100000) master_width ++; + reg_2834 = (reg_2834 << 16) | x_cutoff; + reg_2838 = (reg_2838 << 16) | x_cutoff; + reg_283c = master_width >> 2; + reg_2844 = master_width >> 1; + reg_2854 = master_width; + reg_285c = master_width >> 1; + reg_2864 = master_width >> 1; + reg_2870 += (((reg_2870_offset << 14) & 0xFFFF0000) | reg_2870_offset >> 1); + reg_2870 += (5 - (((window->src_w * 3) - 1) / window->dst_w)) << 16; + reg_2874 = 0x00000001; + } + + /* Select the horizontal filter */ + if (window->src_w == window->dst_w) { + /* An exact size match uses filter 0 */ + h_filter = 0; + } + else { + /* Figure out which filter to use */ + h_filter = ((window->src_w << 16) / window->dst_w) >> 15; + h_filter = (h_filter >> 1) + (h_filter & 1); + /* Only an exact size match can use filter 0 */ + if (h_filter == 0) h_filter = 1; + } + + write_reg(reg_2834, 0x02834); + write_reg(reg_2838, 0x02838); + IVTV_DEBUG_YUV("Update reg 0x2834 %08x->%08x 0x2838 %08x->%08x\n",itv->yuv_info.reg_2834, reg_2834, itv->yuv_info.reg_2838, reg_2838); + + write_reg(reg_283c, 0x0283c); + write_reg(reg_2844, 0x02844); + + IVTV_DEBUG_YUV("Update reg 0x283c %08x->%08x 0x2844 %08x->%08x\n",itv->yuv_info.reg_283c, reg_283c, itv->yuv_info.reg_2844, reg_2844); + + write_reg(0x00080514, 0x02840); + write_reg(0x00100514, 0x02848); + IVTV_DEBUG_YUV("Update reg 0x2840 %08x->%08x 0x2848 %08x->%08x\n",itv->yuv_info.reg_2840, 0x00080514, itv->yuv_info.reg_2848, 0x00100514); + + write_reg(reg_2854, 0x02854); + IVTV_DEBUG_YUV("Update reg 0x2854 %08x->%08x \n",itv->yuv_info.reg_2854, reg_2854); + + write_reg(reg_285c, 0x0285c); + write_reg(reg_2864, 0x02864); + IVTV_DEBUG_YUV("Update reg 0x285c %08x->%08x 0x2864 %08x->%08x\n",itv->yuv_info.reg_285c, reg_285c, itv->yuv_info.reg_2864, reg_2864); + + write_reg(reg_2874, 0x02874); + IVTV_DEBUG_YUV("Update reg 0x2874 %08x->%08x\n",itv->yuv_info.reg_2874, reg_2874); + + write_reg(reg_2870, 0x02870); + IVTV_DEBUG_YUV("Update reg 0x2870 %08x->%08x\n",itv->yuv_info.reg_2870, reg_2870); + + write_reg( reg_2890,0x02890); + IVTV_DEBUG_YUV("Update reg 0x2890 %08x->%08x\n",itv->yuv_info.reg_2890, reg_2890); + + /* Only update the filter if we really need to */ + if (h_filter != itv->yuv_info.h_filter) { + ivtv_yuv_filter (itv,h_filter,-1,-1); + itv->yuv_info.h_filter = h_filter; + } +} + +static void ivtv_yuv_handle_vertical(struct ivtv *itv, struct yuv_frame_info *window) +{ + u32 master_height; + u32 reg_2918, reg_291c, reg_2920, reg_2928; + u32 reg_2930, reg_2934, reg_293c; + u32 reg_2940, reg_2944, reg_294c; + u32 reg_2950, reg_2954, reg_2958, reg_295c; + u32 reg_2960, reg_2964, reg_2968, reg_296c; + u32 reg_289c; + u32 src_y_major_y, src_y_minor_y; + u32 src_y_major_uv, src_y_minor_uv; + u32 reg_2964_base, reg_2968_base; + int v_filter_1, v_filter_2; + + IVTV_DEBUG_WARN("Need to adjust to height %d src_h %d dst_h %d src_y %d dst_y %d\n", + window->tru_h, window->src_h, window->dst_h,window->src_y, window->dst_y); + + /* What scaling mode is being used... */ + if (window->interlaced_y) { + IVTV_DEBUG_YUV("Scaling mode Y: Interlaced\n"); + } + else { + IVTV_DEBUG_YUV("Scaling mode Y: Progressive\n"); + } + + if (window->interlaced_uv) { + IVTV_DEBUG_YUV("Scaling mode UV: Interlaced\n"); + } + else { + IVTV_DEBUG_YUV("Scaling mode UV: Progressive\n"); + } + + /* What is the source video being treated as... */ + if (itv->yuv_info.frame_interlaced) { + IVTV_DEBUG_WARN("Source video: Interlaced\n"); + } + else { + IVTV_DEBUG_WARN("Source video: Non-interlaced\n"); + } + + /* We offset into the image using two different index methods, so split + the y source coord into two parts. */ + if (window->src_y < 8) { + src_y_minor_uv = window->src_y; + src_y_major_uv = 0; + } + else { + src_y_minor_uv = 8; + src_y_major_uv = window->src_y - 8; + } + + src_y_minor_y = src_y_minor_uv; + src_y_major_y = src_y_major_uv; + + if (window->offset_y) src_y_minor_y += 16; + + if (window->interlaced_y) + reg_2918 = (window->dst_h << 16) | (window->src_h + src_y_minor_y); + else + reg_2918 = (window->dst_h << 16) | ((window->src_h + src_y_minor_y) << 1); + + if (window->interlaced_uv) + reg_291c = (window->dst_h << 16) | ((window->src_h + src_y_minor_uv) >> 1); + else + reg_291c = (window->dst_h << 16) | (window->src_h + src_y_minor_uv); + + reg_2964_base = (src_y_minor_y * ((window->dst_h << 16)/window->src_h)) >> 14; + reg_2968_base = (src_y_minor_uv * ((window->dst_h << 16)/window->src_h)) >> 14; + + if (window->dst_h / 2 >= window->src_h && !window->interlaced_y) { + master_height = (window->src_h * 0x00400000) / window->dst_h; + if ((window->src_h * 0x00400000) - (master_height * window->dst_h) >= window->dst_h / 2) master_height ++; + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 3; + reg_2930 = master_height; + reg_2940 = master_height >> 1; + reg_2964_base >>= 3; + reg_2968_base >>= 3; + reg_296c = 0x00000000; + } + else if (window->dst_h >= window->src_h) { + master_height = (window->src_h * 0x00400000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height >> 1; + reg_296c = 0x00000000; + if (window->interlaced_y) { + reg_2964_base >>= 3; + } + else { + reg_296c ++; + reg_2964_base >>= 2; + } + if (window->interlaced_uv) reg_2928 >>= 1; + reg_2968_base >>= 3; + } + else if (window->dst_h >= window->src_h / 2) { + master_height = (window->src_h * 0x00200000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height; + reg_296c = 0x00000101; + if (window->interlaced_y) { + reg_2964_base >>= 2; + } + else { + reg_296c ++; + reg_2964_base >>= 1; + } + if (window->interlaced_uv) reg_2928 >>= 1; + reg_2968_base >>= 2; + } + else { + master_height = (window->src_h * 0x00100000) / window->dst_h; + master_height = (master_height >> 1) + (master_height & 1); + reg_2920 = master_height >> 2; + reg_2928 = master_height >> 2; + reg_2930 = master_height; + reg_2940 = master_height; + reg_2964_base >>= 1; + reg_2968_base >>= 2; + reg_296c = 0x00000102; + } + + /* FIXME These registers change depending on scaled / unscaled output + We really need to work out what they should be */ + if (window->src_h == window->dst_h){ + reg_2934 = 0x00020000; + reg_293c = 0x00100000; + reg_2944 = 0x00040000; + reg_294c = 0x000b0000; + } + else { + reg_2934 = 0x00000FF0; + reg_293c = 0x00000FF0; + reg_2944 = 0x00000FF0; + reg_294c = 0x00000FF0; + } + + /* The first line to be displayed */ + reg_2950 = 0x00010000 + src_y_major_y; + if (window->interlaced_y) reg_2950 += 0x00010000; + reg_2954 = reg_2950 + 1; + + reg_2958 = 0x00010000 + (src_y_major_y >> 1); + if (window->interlaced_uv) reg_2958 += 0x00010000; + reg_295c = reg_2958 + 1; + + if (itv->yuv_info.decode_height == 480) + reg_289c = 0x011e0017; + else + reg_289c = 0x01500017; + + if (window->dst_y < 0) + reg_289c = (reg_289c - ((window->dst_y & ~1)<<15))-(window->dst_y >>1); + else + reg_289c = (reg_289c + ((window->dst_y & ~1)<<15))+(window->dst_y >>1); + + /* How much of the source to decode. + Take into account the source offset */ + reg_2960 = ((src_y_minor_y + window->src_h + src_y_major_y) - 1 ) | + ((((src_y_minor_uv + window->src_h + src_y_major_uv) - 1) & ~1) << 15); + + /* Calculate correct value for register 2964 */ + if (window->src_h == window->dst_h) + reg_2964 = 1; + else { + reg_2964 = 2 + ((window->dst_h << 1) / window->src_h); + reg_2964 = (reg_2964 >> 1) + (reg_2964 & 1); + } + reg_2968 = (reg_2964 << 16) + reg_2964 + (reg_2964 >> 1); + reg_2964 = (reg_2964 << 16) + reg_2964 + (reg_2964 * 46 / 94); + + /* Okay, we've wasted time working out the correct value, + but if we use it, it fouls the the window alignment. + Fudge it to what we want... */ + reg_2964 = 0x00010001 + ((reg_2964 & 0x0000FFFF) - (reg_2964 >> 16)); + reg_2968 = 0x00010001 + ((reg_2968 & 0x0000FFFF) - (reg_2968 >> 16)); + + /* Deviate further from what it should be. I find the flicker headache + inducing so try to reduce it slightly. Leave 2968 as-is otherwise + colours foul. */ + if ((reg_2964 != 0x00010001) && (window->dst_h / 2 <= window->src_h)) + reg_2964 = (reg_2964 & 0xFFFF0000) + ((reg_2964 & 0x0000FFFF)/2); + + if (!window->interlaced_y) reg_2964 -= 0x00010001; + if (!window->interlaced_uv) reg_2968 -= 0x00010001; + + reg_2964 += ((reg_2964_base << 16) | reg_2964_base); + reg_2968 += ((reg_2968_base << 16) | reg_2968_base); + + /* Select the vertical filter */ + if (window->src_h == window->dst_h) { + /* An exact size match uses filter 0/1 */ + v_filter_1 = 0; + v_filter_2 = 1; + } + else { + /* Figure out which filter to use */ + v_filter_1 = ((window->src_h << 16) / window->dst_h) >> 15; + v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); + /* Only an exact size match can use filter 0 */ + if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_2 = v_filter_1; + } + + write_reg(reg_2934, 0x02934); + write_reg(reg_293c, 0x0293c); + IVTV_DEBUG_YUV("Update reg 0x2934 %08x->%08x 0x293c %08x->%08x\n",itv->yuv_info.reg_2934, reg_2934, itv->yuv_info.reg_293c, reg_293c); + write_reg(reg_2944, 0x02944); + write_reg(reg_294c, 0x0294c); + IVTV_DEBUG_YUV("Update reg 0x2944 %08x->%08x 0x294c %08x->%08x\n",itv->yuv_info.reg_2944, reg_2944, itv->yuv_info.reg_294c, reg_294c); + + /* Ensure 2970 is 0 (does it ever change ?) */ +/* write_reg(0,0x02970); */ +/* IVTV_DEBUG_YUV("Update reg 0x2970 %08x->%08x\n",itv->yuv_info.reg_2970, 0); */ + + write_reg(reg_2930, 0x02938); + write_reg(reg_2930, 0x02930); + IVTV_DEBUG_YUV("Update reg 0x2930 %08x->%08x 0x2938 %08x->%08x\n",itv->yuv_info.reg_2930, reg_2930, itv->yuv_info.reg_2938, reg_2930); + + write_reg(reg_2928, 0x02928); + write_reg(reg_2928+0x514, 0x0292C); + IVTV_DEBUG_YUV("Update reg 0x2928 %08x->%08x 0x292c %08x->%08x\n",itv->yuv_info.reg_2928, reg_2928, itv->yuv_info.reg_292c, reg_2928+0x514); + + write_reg(reg_2920, 0x02920); + write_reg(reg_2920+0x514, 0x02924); + IVTV_DEBUG_YUV("Update reg 0x2920 %08x->%08x 0x2924 %08x->%08x\n",itv->yuv_info.reg_2920, reg_2920, itv->yuv_info.reg_2924, 0x514+reg_2920); + + write_reg (reg_2918,0x02918); + write_reg (reg_291c,0x0291C); + IVTV_DEBUG_YUV("Update reg 0x2918 %08x->%08x 0x291C %08x->%08x\n",itv->yuv_info.reg_2918,reg_2918,itv->yuv_info.reg_291c,reg_291c); + + write_reg(reg_296c, 0x0296c); + IVTV_DEBUG_YUV("Update reg 0x296c %08x->%08x\n",itv->yuv_info.reg_296c, reg_296c); + + write_reg(reg_2940, 0x02948); + write_reg(reg_2940, 0x02940); + IVTV_DEBUG_YUV("Update reg 0x2940 %08x->%08x 0x2948 %08x->%08x\n",itv->yuv_info.reg_2940, reg_2940, itv->yuv_info.reg_2948, reg_2940); + + write_reg(reg_2950, 0x02950); + write_reg(reg_2954, 0x02954); + IVTV_DEBUG_YUV("Update reg 0x2950 %08x->%08x 0x2954 %08x->%08x\n",itv->yuv_info.reg_2950, reg_2950, itv->yuv_info.reg_2954, reg_2954); + + write_reg(reg_2958, 0x02958); + write_reg(reg_295c, 0x0295C); + IVTV_DEBUG_YUV("Update reg 0x2958 %08x->%08x 0x295C %08x->%08x\n",itv->yuv_info.reg_2958, reg_2958, itv->yuv_info.reg_295c, reg_295c); + + write_reg(reg_2960, 0x02960); + IVTV_DEBUG_YUV("Update reg 0x2960 %08x->%08x \n",itv->yuv_info.reg_2960, reg_2960); + + write_reg(reg_2964, 0x02964); + write_reg(reg_2968, 0x02968); + IVTV_DEBUG_YUV("Update reg 0x2964 %08x->%08x 0x2968 %08x->%08x\n",itv->yuv_info.reg_2964, reg_2964, itv->yuv_info.reg_2968, reg_2968); + + write_reg( reg_289c,0x0289c); + IVTV_DEBUG_YUV("Update reg 0x289c %08x->%08x\n",itv->yuv_info.reg_289c, reg_289c); + + /* Only update filter 1 if we really need to */ + if (v_filter_1 != itv->yuv_info.v_filter_1) { + ivtv_yuv_filter (itv,-1,v_filter_1,-1); + itv->yuv_info.v_filter_1 = v_filter_1; + } + + /* Only update filter 2 if we really need to */ + if (v_filter_2 != itv->yuv_info.v_filter_2) { + ivtv_yuv_filter (itv,-1,-1,v_filter_2); + itv->yuv_info.v_filter_2 = v_filter_2; + } + + itv->yuv_info.frame_interlaced_last = itv->yuv_info.frame_interlaced; + itv->yuv_info.lace_threshold_last = itv->yuv_info.lace_threshold; +} + +/* Modify the supplied coordinate information to fit the visible osd area */ +static u32 ivtv_yuv_window_setup (struct ivtv *itv, struct yuv_frame_info *window) +{ + int osd_crop; + u32 osd_scale; + u32 yuv_update = 0; + + /* Work out the lace settings */ + switch (itv->yuv_info.lace_mode) { + case IVTV_YUV_MODE_PROGRESSIVE: /* Progressive mode */ + itv->yuv_info.frame_interlaced = 0; + if (window->tru_h < 512 || (window->tru_h > 576 && window->tru_h < 1021)) + window->interlaced_y = 0; + else + window->interlaced_y = 1; + + if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) + window->interlaced_uv = 0; + else + window->interlaced_uv = 1; + break; + + case IVTV_YUV_MODE_AUTO: + if (window->tru_h <= itv->yuv_info.lace_threshold || window->tru_h > 576 || window->tru_w > 720){ + itv->yuv_info.frame_interlaced = 0; + if ((window->tru_h < 512) || + (window->tru_h > 576 && window->tru_h < 1021) || + (window->tru_w > 720 && window->tru_h < 1021)) + window->interlaced_y = 0; + else + window->interlaced_y = 1; + + if (window->tru_h < 1021 && (window->dst_h >= window->src_h /2)) + window->interlaced_uv = 0; + else + window->interlaced_uv = 1; + } + else { + itv->yuv_info.frame_interlaced = 1; + window->interlaced_y = 1; + window->interlaced_uv = 1; + } + break; + + case IVTV_YUV_MODE_INTERLACED: /* Interlace mode */ + default: + itv->yuv_info.frame_interlaced = 1; + window->interlaced_y = 1; + window->interlaced_uv = 1; + break; + } + + /* Sorry, but no negative coords for src */ + if (window->src_x < 0) window->src_x = 0; + if (window->src_y < 0) window->src_y = 0; + + /* Can only reduce width down to 1/4 original size */ + if ((osd_crop = window->src_w - ( 4 * window->dst_w )) > 0) { + window->src_x += osd_crop / 2; + window->src_w = (window->src_w - osd_crop) & ~3; + window->dst_w = window->src_w / 4; + window->dst_w += window->dst_w & 1; + } + + /* Can only reduce height down to 1/4 original size */ + if (window->src_h / window->dst_h >= 2) { + /* Overflow may be because we're running progressive, so force mode switch */ + window->interlaced_y = 1; + /* Make sure we're still within limits for interlace */ + if ((osd_crop = window->src_h - ( 4 * window->dst_h )) > 0) { + /* If we reach here we'll have to force the height. */ + window->src_y += osd_crop / 2; + window->src_h = (window->src_h - osd_crop) & ~3; + window->dst_h = window->src_h / 4; + window->dst_h += window->dst_h & 1; + } + } + + /* If there's nothing to safe to display, we may as well stop now */ + if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + return 0; + } + + /* Ensure video remains inside OSD area */ + osd_scale = (window->src_h << 16) / window->dst_h; + + if ((osd_crop = window->pan_y - window->dst_y) > 0) { + /* Falls off the upper edge - crop */ + window->src_y += (osd_scale * osd_crop) >> 16; + window->src_h -= (osd_scale * osd_crop) >> 16; + window->dst_h -= osd_crop; + window->dst_y = 0; + } + else { + window->dst_y -= window->pan_y; + } + + if ((osd_crop = window->dst_h + window->dst_y - window->vis_h) > 0) { + /* Falls off the lower edge - crop */ + window->dst_h -= osd_crop; + window->src_h -= (osd_scale * osd_crop) >> 16; + } + + osd_scale = (window->src_w << 16) / window->dst_w; + + if ((osd_crop = window->pan_x - window->dst_x) > 0) { + /* Fall off the left edge - crop */ + window->src_x += (osd_scale * osd_crop) >> 16; + window->src_w -= (osd_scale * osd_crop) >> 16; + window->dst_w -= osd_crop; + window->dst_x = 0; + } + else { + window->dst_x -= window->pan_x; + } + + if ((osd_crop = window->dst_w + window->dst_x - window->vis_w) > 0) { + /* Falls off the right edge - crop */ + window->dst_w -= osd_crop; + window->src_w -= (osd_scale * osd_crop) >> 16; + } + + /* The OSD can be moved. Track to it */ + window->dst_x += itv->yuv_info.osd_x_offset; + window->dst_y += itv->yuv_info.osd_y_offset; + + /* Width & height for both src & dst must be even. + Same for coordinates. */ + window->dst_w &= ~1; + window->dst_x &= ~1; + + window->src_w += window->src_x & 1; + window->src_x &= ~1; + + window->src_w &= ~1; + window->dst_w &= ~1; + + window->dst_h &= ~1; + window->dst_y &= ~1; + + window->src_h += window->src_y & 1; + window->src_y &= ~1; + + window->src_h &= ~1; + window->dst_h &= ~1; + + /* Due to rounding, we may have reduced the output size to <1/4 of the source + Check again, but this time just resize. Don't change source coordinates */ + if (window->dst_w < window->src_w / 4) { + window->src_w &= ~3; + window->dst_w = window->src_w / 4; + window->dst_w += window->dst_w & 1; + } + if (window->dst_h < window->src_h / 4) { + window->src_h &= ~3; + window->dst_h = window->src_h / 4; + window->dst_h += window->dst_h & 1; + } + + /* Check again. If there's nothing to safe to display, stop now */ + if ((int)window->dst_w <= 2 || (int)window->dst_h <= 2 || (int)window->src_w <= 2 || (int)window->src_h <= 2) { + return 0; + } + + /* Both x offset & width are linked, so they have to be done together */ + if ((itv->yuv_info.old_frame_info.dst_w != window->dst_w) || + (itv->yuv_info.old_frame_info.src_w != window->src_w) || + (itv->yuv_info.old_frame_info.dst_x != window->dst_x) || + (itv->yuv_info.old_frame_info.src_x != window->src_x) || + (itv->yuv_info.old_frame_info.pan_x != window->pan_x) || + (itv->yuv_info.old_frame_info.vis_w != window->vis_w)) { + yuv_update |= IVTV_YUV_UPDATE_HORIZONTAL; + } + + if ((itv->yuv_info.old_frame_info.src_h != window->src_h) || + (itv->yuv_info.old_frame_info.dst_h != window->dst_h) || + (itv->yuv_info.old_frame_info.dst_y != window->dst_y) || + (itv->yuv_info.old_frame_info.src_y != window->src_y) || + (itv->yuv_info.old_frame_info.pan_y != window->pan_y) || + (itv->yuv_info.old_frame_info.vis_h != window->vis_h) || + (itv->yuv_info.old_frame_info.interlaced_y != window->interlaced_y) || + (itv->yuv_info.old_frame_info.interlaced_uv != window->interlaced_uv)) { + yuv_update |= IVTV_YUV_UPDATE_VERTICAL; + } + + return yuv_update; +} + +/* Update the scaling register to the requested value */ +void ivtv_yuv_work_handler (struct work_struct *work) +{ + struct yuv_playback_info *info = container_of(work, struct yuv_playback_info, work_queue); + struct ivtv *itv = container_of(info, struct ivtv, yuv_info); + DEFINE_WAIT(wait); + + struct yuv_frame_info window; + u32 yuv_update; + + int frame = itv->yuv_info.update_frame; + +/* IVTV_DEBUG_YUV("Update yuv registers for frame %d\n",frame); */ + memcpy(&window, &itv->yuv_info.new_frame_info[frame], sizeof (window)); + + /* Update the osd pan info */ + window.pan_x = itv->yuv_info.osd_x_pan; + window.pan_y = itv->yuv_info.osd_y_pan; + window.vis_w = itv->yuv_info.osd_vis_w; + window.vis_h = itv->yuv_info.osd_vis_h; + + /* Calculate the display window coordinates. Exit if nothing left */ + if (!(yuv_update = ivtv_yuv_window_setup (itv, &window))) + return; + + /* Update horizontal settings */ + if (yuv_update & IVTV_YUV_UPDATE_HORIZONTAL) + ivtv_yuv_handle_horizontal(itv, &window); + + if (yuv_update & IVTV_YUV_UPDATE_VERTICAL) + ivtv_yuv_handle_vertical(itv, &window); + + memcpy(&itv->yuv_info.old_frame_info, &window, sizeof (itv->yuv_info.old_frame_info)); +} + +static void ivtv_yuv_init (struct ivtv *itv) +{ + IVTV_DEBUG_YUV("ivtv_yuv_init\n"); + + /* Take a snapshot of the current register settings */ + itv->yuv_info.reg_2834 = read_reg(0x02834); + itv->yuv_info.reg_2838 = read_reg(0x02838); + itv->yuv_info.reg_283c = read_reg(0x0283c); + itv->yuv_info.reg_2840 = read_reg(0x02840); + itv->yuv_info.reg_2844 = read_reg(0x02844); + itv->yuv_info.reg_2848 = read_reg(0x02848); + itv->yuv_info.reg_2854 = read_reg(0x02854); + itv->yuv_info.reg_285c = read_reg(0x0285c); + itv->yuv_info.reg_2864 = read_reg(0x02864); + itv->yuv_info.reg_2870 = read_reg(0x02870); + itv->yuv_info.reg_2874 = read_reg(0x02874); + itv->yuv_info.reg_2898 = read_reg(0x02898); + itv->yuv_info.reg_2890 = read_reg(0x02890); + + itv->yuv_info.reg_289c = read_reg(0x0289c); + itv->yuv_info.reg_2918 = read_reg(0x02918); + itv->yuv_info.reg_291c = read_reg(0x0291c); + itv->yuv_info.reg_2920 = read_reg(0x02920); + itv->yuv_info.reg_2924 = read_reg(0x02924); + itv->yuv_info.reg_2928 = read_reg(0x02928); + itv->yuv_info.reg_292c = read_reg(0x0292c); + itv->yuv_info.reg_2930 = read_reg(0x02930); + itv->yuv_info.reg_2934 = read_reg(0x02934); + itv->yuv_info.reg_2938 = read_reg(0x02938); + itv->yuv_info.reg_293c = read_reg(0x0293c); + itv->yuv_info.reg_2940 = read_reg(0x02940); + itv->yuv_info.reg_2944 = read_reg(0x02944); + itv->yuv_info.reg_2948 = read_reg(0x02948); + itv->yuv_info.reg_294c = read_reg(0x0294c); + itv->yuv_info.reg_2950 = read_reg(0x02950); + itv->yuv_info.reg_2954 = read_reg(0x02954); + itv->yuv_info.reg_2958 = read_reg(0x02958); + itv->yuv_info.reg_295c = read_reg(0x0295c); + itv->yuv_info.reg_2960 = read_reg(0x02960); + itv->yuv_info.reg_2964 = read_reg(0x02964); + itv->yuv_info.reg_2968 = read_reg(0x02968); + itv->yuv_info.reg_296c = read_reg(0x0296c); + itv->yuv_info.reg_2970 = read_reg(0x02970); + + itv->yuv_info.v_filter_1 = -1; + itv->yuv_info.v_filter_2 = -1; + itv->yuv_info.h_filter = -1; + + /* Set some valid size info */ + itv->yuv_info.osd_x_offset = read_reg(0x02a04) & 0x00000FFF; + itv->yuv_info.osd_y_offset = (read_reg(0x02a04) >> 16) & 0x00000FFF; + + /* Bit 2 of reg 2878 indicates current decoder output format + 0 : NTSC 1 : PAL */ + if (read_reg(0x2878) & 4) + itv->yuv_info.decode_height = 576; + else + itv->yuv_info.decode_height = 480; + + /* If no visible size set, assume full size */ + if (!itv->yuv_info.osd_vis_w) itv->yuv_info.osd_vis_w = 720 - itv->yuv_info.osd_x_offset; + if (!itv->yuv_info.osd_vis_h) itv->yuv_info.osd_vis_h = itv->yuv_info.decode_height - itv->yuv_info.osd_y_offset; + + /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */ + itv->yuv_info.blanking_ptr = kzalloc(720*16,GFP_KERNEL); + if (itv->yuv_info.blanking_ptr) { + itv->yuv_info.blanking_dmaptr = pci_map_single(itv->dev, itv->yuv_info.blanking_ptr, 720*16, PCI_DMA_TODEVICE); + } + else { + itv->yuv_info.blanking_dmaptr = 0; + IVTV_DEBUG_WARN ("Failed to allocate yuv blanking buffer\n"); + } + + IVTV_DEBUG_WARN("Enable video output\n"); + write_reg_sync(0x00108080, 0x2898); + + /* Enable YUV decoder output */ + write_reg_sync(0x01, IVTV_REG_VDM); + + set_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); + atomic_set(&itv->yuv_info.next_dma_frame,0); +} + +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args) +{ + DEFINE_WAIT(wait); + int rc = 0; + int got_sig = 0; + int frame, next_fill_frame, last_fill_frame; + + IVTV_DEBUG_INFO("yuv_prep_frame\n"); + + if (atomic_read(&itv->yuv_info.next_dma_frame) == -1) ivtv_yuv_init(itv); + + frame = atomic_read(&itv->yuv_info.next_fill_frame); + next_fill_frame = (frame + 1) & 0x3; + last_fill_frame = (atomic_read(&itv->yuv_info.next_dma_frame)+1) & 0x3; + + if (next_fill_frame != last_fill_frame && last_fill_frame != frame) { + /* Buffers are full - Overwrite the last frame */ + next_fill_frame = frame; + frame = (frame - 1) & 3; + } + + /* Take a snapshot of the yuv coordinate information */ + itv->yuv_info.new_frame_info[frame].src_x = args->src.left; + itv->yuv_info.new_frame_info[frame].src_y = args->src.top; + itv->yuv_info.new_frame_info[frame].src_w = args->src.width; + itv->yuv_info.new_frame_info[frame].src_h = args->src.height; + itv->yuv_info.new_frame_info[frame].dst_x = args->dst.left; + itv->yuv_info.new_frame_info[frame].dst_y = args->dst.top; + itv->yuv_info.new_frame_info[frame].dst_w = args->dst.width; + itv->yuv_info.new_frame_info[frame].dst_h = args->dst.height; + itv->yuv_info.new_frame_info[frame].tru_x = args->dst.left; + itv->yuv_info.new_frame_info[frame].tru_w = args->src_width; + itv->yuv_info.new_frame_info[frame].tru_h = args->src_height; + + /* Are we going to offset the Y plane */ + if (args->src.height + args->src.top < 512-16) + itv->yuv_info.new_frame_info[frame].offset_y = 1; + else + itv->yuv_info.new_frame_info[frame].offset_y = 0; + + /* Snapshot the osd pan info */ + itv->yuv_info.new_frame_info[frame].pan_x = itv->yuv_info.osd_x_pan; + itv->yuv_info.new_frame_info[frame].pan_y = itv->yuv_info.osd_y_pan; + itv->yuv_info.new_frame_info[frame].vis_w = itv->yuv_info.osd_vis_w; + itv->yuv_info.new_frame_info[frame].vis_h = itv->yuv_info.osd_vis_h; + + itv->yuv_info.new_frame_info[frame].update = 0; + itv->yuv_info.new_frame_info[frame].interlaced_y = 0; + itv->yuv_info.new_frame_info[frame].interlaced_uv = 0; + + if (memcmp (&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], + sizeof (itv->yuv_info.new_frame_info[frame]))) { + memcpy(&itv->yuv_info.old_frame_info_args, &itv->yuv_info.new_frame_info[frame], sizeof (itv->yuv_info.old_frame_info_args)); + itv->yuv_info.new_frame_info[frame].update = 1; +/* IVTV_DEBUG_YUV ("Requesting register update for frame %d\n",frame); */ + } + + /* DMA the frame */ + mutex_lock(&itv->udma.lock); + + if ((rc = ivtv_yuv_prep_user_dma(itv, &itv->udma, args)) != 0) { + mutex_unlock(&itv->udma.lock); + return rc; + } + + ivtv_udma_prepare(itv); + prepare_to_wait(&itv->dma_waitq, &wait, TASK_INTERRUPTIBLE); + /* if no UDMA is pending and no UDMA is in progress, then the DMA + is finished */ + while (itv->i_flags & (IVTV_F_I_UDMA_PENDING | IVTV_F_I_UDMA)) { + /* don't interrupt if the DMA is in progress but break off + a still pending DMA. */ + got_sig = signal_pending(current); + if (got_sig && test_and_clear_bit(IVTV_F_I_UDMA_PENDING, &itv->i_flags)) + break; + got_sig = 0; + schedule(); + } + finish_wait(&itv->dma_waitq, &wait); + + /* Unmap Last DMA Xfer */ + ivtv_udma_unmap(itv); + + if (got_sig) { + IVTV_DEBUG_INFO("User stopped YUV UDMA\n"); + mutex_unlock(&itv->udma.lock); + return -EINTR; + } + + atomic_set(&itv->yuv_info.next_fill_frame, next_fill_frame); + + mutex_unlock(&itv->udma.lock); + return rc; +} + +void ivtv_yuv_close(struct ivtv *itv) +{ + int h_filter, v_filter_1, v_filter_2; + + IVTV_DEBUG_YUV("ivtv_yuv_close\n"); + ivtv_waitq(&itv->vsync_waitq); + + atomic_set(&itv->yuv_info.next_dma_frame, -1); + atomic_set(&itv->yuv_info.next_fill_frame, 0); + + /* Reset registers we have changed so mpeg playback works */ + + /* If we fully restore this register, the display may remain active. + Restore, but set one bit to blank the video. Firmware will always + clear this bit when needed, so not a problem. */ + write_reg(itv->yuv_info.reg_2898 | 0x01000000, 0x2898); + + write_reg(itv->yuv_info.reg_2834, 0x02834); + write_reg(itv->yuv_info.reg_2838, 0x02838); + write_reg(itv->yuv_info.reg_283c, 0x0283c); + write_reg(itv->yuv_info.reg_2840, 0x02840); + write_reg(itv->yuv_info.reg_2844, 0x02844); + write_reg(itv->yuv_info.reg_2848, 0x02848); + write_reg(itv->yuv_info.reg_2854, 0x02854); + write_reg(itv->yuv_info.reg_285c, 0x0285c); + write_reg(itv->yuv_info.reg_2864, 0x02864); + write_reg(itv->yuv_info.reg_2870, 0x02870); + write_reg(itv->yuv_info.reg_2874, 0x02874); + write_reg(itv->yuv_info.reg_2890, 0x02890); + write_reg(itv->yuv_info.reg_289c, 0x0289c); + + write_reg(itv->yuv_info.reg_2918, 0x02918); + write_reg(itv->yuv_info.reg_291c, 0x0291c); + write_reg(itv->yuv_info.reg_2920, 0x02920); + write_reg(itv->yuv_info.reg_2924, 0x02924); + write_reg(itv->yuv_info.reg_2928, 0x02928); + write_reg(itv->yuv_info.reg_292c, 0x0292c); + write_reg(itv->yuv_info.reg_2930, 0x02930); + write_reg(itv->yuv_info.reg_2934, 0x02934); + write_reg(itv->yuv_info.reg_2938, 0x02938); + write_reg(itv->yuv_info.reg_293c, 0x0293c); + write_reg(itv->yuv_info.reg_2940, 0x02940); + write_reg(itv->yuv_info.reg_2944, 0x02944); + write_reg(itv->yuv_info.reg_2948, 0x02948); + write_reg(itv->yuv_info.reg_294c, 0x0294c); + write_reg(itv->yuv_info.reg_2950, 0x02950); + write_reg(itv->yuv_info.reg_2954, 0x02954); + write_reg(itv->yuv_info.reg_2958, 0x02958); + write_reg(itv->yuv_info.reg_295c, 0x0295c); + write_reg(itv->yuv_info.reg_2960, 0x02960); + write_reg(itv->yuv_info.reg_2964, 0x02964); + write_reg(itv->yuv_info.reg_2968, 0x02968); + write_reg(itv->yuv_info.reg_296c, 0x0296c); + write_reg(itv->yuv_info.reg_2970, 0x02970); + + /* Prepare to restore filters */ + + /* First the horizontal filter */ + if ((itv->yuv_info.reg_2834 & 0x0000FFFF) == (itv->yuv_info.reg_2834 >> 16)) { + /* An exact size match uses filter 0 */ + h_filter = 0; + } + else { + /* Figure out which filter to use */ + h_filter = ((itv->yuv_info.reg_2834 << 16) / (itv->yuv_info.reg_2834 >> 16)) >> 15; + h_filter = (h_filter >> 1) + (h_filter & 1); + /* Only an exact size match can use filter 0. */ + if (h_filter < 1) h_filter = 1; + } + + /* Now the vertical filter */ + if ((itv->yuv_info.reg_2918 & 0x0000FFFF) == (itv->yuv_info.reg_2918 >> 16)) { + /* An exact size match uses filter 0/1 */ + v_filter_1 = 0; + v_filter_2 = 1; + } + else { + /* Figure out which filter to use */ + v_filter_1 = ((itv->yuv_info.reg_2918 << 16) / (itv->yuv_info.reg_2918 >> 16)) >> 15; + v_filter_1 = (v_filter_1 >> 1) + (v_filter_1 & 1); + /* Only an exact size match can use filter 0 */ + if (v_filter_1 == 0) v_filter_1 = 1; + v_filter_2 = v_filter_1; + } + + /* Now restore the filters */ + ivtv_yuv_filter (itv,h_filter,v_filter_1,v_filter_2); + + /* and clear a few registers */ + write_reg(0, 0x02814); + write_reg(0, 0x0282c); + write_reg(0, 0x02904); + write_reg(0, 0x02910); + + /* Release the blanking buffer */ + if (itv->yuv_info.blanking_ptr) { + kfree (itv->yuv_info.blanking_ptr); + itv->yuv_info.blanking_ptr = NULL; + pci_unmap_single(itv->dev, itv->yuv_info.blanking_dmaptr, 720*16, PCI_DMA_TODEVICE); + } + + /* Invalidate the old dimension information */ + itv->yuv_info.old_frame_info.src_w = 0; + itv->yuv_info.old_frame_info.src_h = 0; + itv->yuv_info.old_frame_info_args.src_w = 0; + itv->yuv_info.old_frame_info_args.src_h = 0; + + /* All done. */ + clear_bit(IVTV_F_I_DECODING_YUV, &itv->i_flags); +} + diff --git a/drivers/media/video/ivtv/ivtv-yuv.h b/drivers/media/video/ivtv/ivtv-yuv.h new file mode 100644 index 000000000000..31128733e784 --- /dev/null +++ b/drivers/media/video/ivtv/ivtv-yuv.h @@ -0,0 +1,24 @@ +/* + yuv support + + Copyright (C) 2007 Ian Armstrong + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +int ivtv_yuv_filter_check(struct ivtv *itv); +int ivtv_yuv_prep_frame(struct ivtv *itv, struct ivtv_dma_frame *args); +void ivtv_yuv_close(struct ivtv *itv); +void ivtv_yuv_work_handler (struct work_struct *work); diff --git a/include/media/ivtv.h b/include/media/ivtv.h new file mode 100644 index 000000000000..412b48ea8eda --- /dev/null +++ b/include/media/ivtv.h @@ -0,0 +1,65 @@ +/* + Public ivtv API header + Copyright (C) 2003-2004 Kevin Thayer + Copyright (C) 2004-2007 Hans Verkuil + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _LINUX_IVTV_H +#define _LINUX_IVTV_H + +/* ivtv knows several distinct output modes: MPEG streaming, + YUV streaming, YUV updates through user DMA and the passthrough + mode. + + In order to clearly tell the driver that we are in user DMA + YUV mode you need to call IVTV_IOC_DMA_FRAME with y_source == NULL + first (althrough if you don't then the first time + DMA_FRAME is called the mode switch is done automatically). + + When you close the file handle the user DMA mode is exited again. + + While in one mode, you cannot use another mode (EBUSY is returned). + + All this means that if you want to change the YUV interlacing + for the user DMA YUV mode you first need to do call IVTV_IOC_DMA_FRAME + with y_source == NULL before you can set the correct format using + VIDIOC_S_FMT. + + Eventually all this should be replaced with a proper V4L2 API, + but for now we have to do it this way. */ + +struct ivtv_dma_frame { + enum v4l2_buf_type type; /* V4L2_BUF_TYPE_VIDEO_OUTPUT */ + __u32 pixelformat; /* 0 == same as destination */ + void __user *y_source; /* if NULL and type == V4L2_BUF_TYPE_VIDEO_OUTPUT, + then just switch to user DMA YUV output mode */ + void __user *uv_source; /* Unused for RGB pixelformats */ + struct v4l2_rect src; + struct v4l2_rect dst; + __u32 src_width; + __u32 src_height; +}; + +#define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame) + +/* These are the VBI types as they appear in the embedded VBI private packets. */ +#define IVTV_SLICED_TYPE_TELETEXT_B (1) +#define IVTV_SLICED_TYPE_CAPTION_525 (4) +#define IVTV_SLICED_TYPE_WSS_625 (5) +#define IVTV_SLICED_TYPE_VPS (7) + +#endif /* _LINUX_IVTV_H */ -- cgit v1.2.3-59-g8ed1b From 74cab31c413c8615efe818d44ff4ac83e2a138be Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 27 Apr 2007 12:31:26 -0300 Subject: V4L/DVB (5355): Add VIDIOC_G_CHIP_IDENT to various i2c modules Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/cs53l32a.c | 4 ++ drivers/media/video/msp3400-driver.c | 5 ++ drivers/media/video/msp3400-driver.h | 1 + drivers/media/video/tvaudio.c | 4 ++ drivers/media/video/upd64031a.c | 4 ++ drivers/media/video/upd64083.c | 5 ++ drivers/media/video/wm8739.c | 4 ++ drivers/media/video/wm8775.c | 4 ++ include/media/v4l2-chip-ident.h | 91 ++++++++++++++++++++++++++++++++++-- 9 files changed, 118 insertions(+), 4 deletions(-) (limited to 'include/media') diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c index de87247c74ee..a73e285af730 100644 --- a/drivers/media/video/cs53l32a.c +++ b/drivers/media/video/cs53l32a.c @@ -28,6 +28,7 @@ #include #include #include +#include MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC"); MODULE_AUTHOR("Martin Vaughan"); @@ -103,6 +104,9 @@ static int cs53l32a_command(struct i2c_client *client, unsigned int cmd, cs53l32a_write(client, 0x05, (u8) ctrl->value); break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_CS53l32A, 0); + case VIDIOC_LOG_STATUS: { u8 v = cs53l32a_read(client, 0x01); diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index ba1af3c8525e..3bb7d6634862 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -773,6 +773,9 @@ static int msp_command(struct i2c_client *client, unsigned int cmd, void *arg) break; } + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, state->ident, (state->rev1 << 16) | state->rev2); + default: /* unknown */ return -EINVAL; @@ -872,6 +875,8 @@ static int msp_attach(struct i2c_adapter *adapter, int address, int kind) snprintf(client->name, sizeof(client->name), "MSP%d4%02d%c-%c%d", msp_family, msp_product, msp_revision, msp_hard, msp_rom); + /* Rev B=2, C=3, D=4, G=7 */ + state->ident = msp_family * 10000 + 4000 + msp_product * 10 + msp_revision - '@'; /* Has NICAM support: all mspx41x and mspx45x products have NICAM */ state->has_nicam = msp_prod_hi == 1 || msp_prod_hi == 5; diff --git a/drivers/media/video/msp3400-driver.h b/drivers/media/video/msp3400-driver.h index 7531efa1615e..ab69a290e5dc 100644 --- a/drivers/media/video/msp3400-driver.h +++ b/drivers/media/video/msp3400-driver.h @@ -50,6 +50,7 @@ extern int msp_stereo_thresh; struct msp_state { int rev1, rev2; + int ident; u8 has_nicam; u8 has_radio; u8 has_headphones; diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index d506dfaa45a9..426083c08986 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -33,6 +33,7 @@ #include #include +#include #include @@ -1775,6 +1776,9 @@ static int chip_command(struct i2c_client *client, /* the thread will call checkmode() later */ } break; + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_TVAUDIO, 0); } return 0; } diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c index 28d1133a3b7a..0b2a961efd22 100644 --- a/drivers/media/video/upd64031a.c +++ b/drivers/media/video/upd64031a.c @@ -27,6 +27,7 @@ #include #include #include +#include #include // --------------------- read registers functions define ----------------------- @@ -179,6 +180,9 @@ static int upd64031a_command(struct i2c_client *client, unsigned int cmd, void * } #endif + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64031A, 0); + default: break; } diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c index fe38224150d8..401bd21f46eb 100644 --- a/drivers/media/video/upd64083.c +++ b/drivers/media/video/upd64083.c @@ -26,6 +26,7 @@ #include #include #include +#include #include MODULE_DESCRIPTION("uPD64083 driver"); @@ -155,6 +156,10 @@ static int upd64083_command(struct i2c_client *client, unsigned int cmd, void *a break; } #endif + + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_UPD64083, 0); + default: break; } diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c index a9b59c35cd67..8f6741a28a47 100644 --- a/drivers/media/video/wm8739.c +++ b/drivers/media/video/wm8739.c @@ -29,6 +29,7 @@ #include #include #include +#include MODULE_DESCRIPTION("wm8739 driver"); MODULE_AUTHOR("T. Adachi, Hans Verkuil"); @@ -236,6 +237,9 @@ static int wm8739_command(struct i2c_client *client, unsigned int cmd, void *arg return -EINVAL; } + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8739, 0); + case VIDIOC_LOG_STATUS: v4l_info(client, "Frequency: %u Hz\n", state->clock_freq); v4l_info(client, "Volume L: %02x%s\n", state->vol_l & 0x1f, diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c index d81a88bbe43d..4df5d30d4d09 100644 --- a/drivers/media/video/wm8775.c +++ b/drivers/media/video/wm8775.c @@ -33,6 +33,7 @@ #include #include #include +#include MODULE_DESCRIPTION("wm8775 driver"); MODULE_AUTHOR("Ulf Eklund, Hans Verkuil"); @@ -124,6 +125,9 @@ static int wm8775_command(struct i2c_client *client, unsigned int cmd, wm8775_write(client, R21, 0x100 + state->input); break; + case VIDIOC_G_CHIP_IDENT: + return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_WM8775, 0); + case VIDIOC_LOG_STATUS: v4l_info(client, "Input: %d%s\n", state->input, state->muted ? " (muted)" : ""); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 7d0c65413981..09d16c4f00f7 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -31,10 +31,13 @@ enum { V4L2_IDENT_AMBIGUOUS = 1, /* Match too general, multiple chips matched */ V4L2_IDENT_UNKNOWN = 2, /* Chip found, but cannot identify */ - /* module saa7110: just ident= 100 */ + /* module tvaudio: reserved range 50-99 */ + V4L2_IDENT_TVAUDIO = 50, /* A tvaudio chip, unknown which it is exactly */ + + /* module saa7110: just ident 100 */ V4L2_IDENT_SAA7110 = 100, - /* module saa7111: just ident= 101 */ + /* module saa7111: just ident 101 */ V4L2_IDENT_SAA7111 = 101, /* module saa7115: reserved range 102-149 */ @@ -55,12 +58,92 @@ enum { V4L2_IDENT_CX25842 = 242, V4L2_IDENT_CX25843 = 243, - /* OmniVision sensors - range 250-299 */ + /* OmniVision sensors: reserved range 250-299 */ V4L2_IDENT_OV7670 = 250, - /* Conexant MPEG encoder/decoders: range 410-420 */ + /* Conexant MPEG encoder/decoders: reserved range 410-420 */ V4L2_IDENT_CX23415 = 415, V4L2_IDENT_CX23416 = 416, + + /* module wm8739: just ident 8739 */ + V4L2_IDENT_WM8739 = 8739, + + /* module wm8775: just ident 8775 */ + V4L2_IDENT_WM8775 = 8775, + + /* module cs53132a: just ident 53132 */ + V4L2_IDENT_CS53l32A = 53132, + + /* module upd64031a: just ident 64031 */ + V4L2_IDENT_UPD64031A = 64031, + + /* module upd64083: just ident 64083 */ + V4L2_IDENT_UPD64083 = 64083, + + /* module msp34xx: reserved range 34000-34999 */ + V4L2_IDENT_MSP3400B = 34002, + V4L2_IDENT_MSP3410B = 34102, + + V4L2_IDENT_MSP3400C = 34003, + V4L2_IDENT_MSP3410C = 34103, + + V4L2_IDENT_MSP3400D = 34004, + V4L2_IDENT_MSP3410D = 34104, + V4L2_IDENT_MSP3405D = 34054, + V4L2_IDENT_MSP3415D = 34154, + V4L2_IDENT_MSP3407D = 34074, + V4L2_IDENT_MSP3417D = 34174, + + V4L2_IDENT_MSP3400G = 34007, + V4L2_IDENT_MSP3410G = 34107, + V4L2_IDENT_MSP3420G = 34207, + V4L2_IDENT_MSP3430G = 34307, + V4L2_IDENT_MSP3440G = 34407, + V4L2_IDENT_MSP3450G = 34507, + V4L2_IDENT_MSP3460G = 34607, + + V4L2_IDENT_MSP3401G = 34017, + V4L2_IDENT_MSP3411G = 34117, + V4L2_IDENT_MSP3421G = 34217, + V4L2_IDENT_MSP3431G = 34317, + V4L2_IDENT_MSP3441G = 34417, + V4L2_IDENT_MSP3451G = 34517, + V4L2_IDENT_MSP3461G = 34617, + + V4L2_IDENT_MSP3402G = 34027, + V4L2_IDENT_MSP3412G = 34127, + V4L2_IDENT_MSP3422G = 34227, + V4L2_IDENT_MSP3442G = 34427, + V4L2_IDENT_MSP3452G = 34527, + + V4L2_IDENT_MSP3405G = 34057, + V4L2_IDENT_MSP3415G = 34157, + V4L2_IDENT_MSP3425G = 34257, + V4L2_IDENT_MSP3435G = 34357, + V4L2_IDENT_MSP3445G = 34457, + V4L2_IDENT_MSP3455G = 34557, + V4L2_IDENT_MSP3465G = 34657, + + V4L2_IDENT_MSP3407G = 34077, + V4L2_IDENT_MSP3417G = 34177, + V4L2_IDENT_MSP3427G = 34277, + V4L2_IDENT_MSP3437G = 34377, + V4L2_IDENT_MSP3447G = 34477, + V4L2_IDENT_MSP3457G = 34577, + V4L2_IDENT_MSP3467G = 34677, + + /* module msp44xx: reserved range 44000-44999 */ + V4L2_IDENT_MSP4400G = 44007, + V4L2_IDENT_MSP4410G = 44107, + V4L2_IDENT_MSP4420G = 44207, + V4L2_IDENT_MSP4440G = 44407, + V4L2_IDENT_MSP4450G = 44507, + + V4L2_IDENT_MSP4408G = 44087, + V4L2_IDENT_MSP4418G = 44187, + V4L2_IDENT_MSP4428G = 44287, + V4L2_IDENT_MSP4448G = 44487, + V4L2_IDENT_MSP4458G = 44587, }; #endif -- cgit v1.2.3-59-g8ed1b