diff options
author | David S. Miller <davem@davemloft.net> | 2009-09-24 15:13:11 -0700 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-09-24 15:13:11 -0700 |
commit | 8b3f6af86378d0a10ca2f1ded1da124aef13b62c (patch) | |
tree | de6ca90295730343c495be8d98be8efa322140ef /drivers/media/video/em28xx | |
parent | 139d6065c83071d5f66cd013a274a43699f8e2c1 (diff) | |
parent | 94e0fb086fc5663c38bbc0fe86d698be8314f82f (diff) |
Merge branch 'master' of /home/davem/src/GIT/linux-2.6/
Conflicts:
drivers/staging/Kconfig
drivers/staging/Makefile
drivers/staging/cpc-usb/TODO
drivers/staging/cpc-usb/cpc-usb_drv.c
drivers/staging/cpc-usb/cpc.h
drivers/staging/cpc-usb/cpc_int.h
drivers/staging/cpc-usb/cpcusb.h
Diffstat (limited to 'drivers/media/video/em28xx')
-rw-r--r-- | drivers/media/video/em28xx/Kconfig | 1 | ||||
-rw-r--r-- | drivers/media/video/em28xx/Makefile | 2 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-cards.c | 174 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-core.c | 51 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-dvb.c | 19 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-i2c.c | 1 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-reg.h | 16 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-vbi.c | 142 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx-video.c | 750 | ||||
-rw-r--r-- | drivers/media/video/em28xx/em28xx.h | 34 |
10 files changed, 956 insertions, 234 deletions
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 6524b493e03..c7be0e09782 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -36,6 +36,7 @@ config VIDEO_EM28XX_DVB depends on VIDEO_EM28XX && DVB_CORE select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE + select DVB_TDA10023 if !DVB_FE_CUSTOMISE select VIDEOBUF_DVB ---help--- This adds support for DVB cards based on the diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 8137a8c94bf..d0f093d1d0d 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -1,5 +1,5 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ - em28xx-input.o + em28xx-input.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 1c2e544eda7..bdb249bd9d5 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -170,6 +170,19 @@ static struct em28xx_reg_seq pinnacle_hybrid_pro_digital[] = { { -1, -1, -1, -1}, }; +/* eb1a:2868 Reddo DVB-C USB TV Box + GPIO4 - CU1216L NIM + Other GPIOs seems to be don't care. */ +static struct em28xx_reg_seq reddo_dvb_c_usb_box[] = { + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xde, 0xff, 10}, + {EM28XX_R08_GPIO, 0xfe, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {EM28XX_R08_GPIO, 0x7f, 0xff, 10}, + {EM28XX_R08_GPIO, 0x6f, 0xff, 10}, + {EM28XX_R08_GPIO, 0xff, 0xff, 10}, + {-1, -1, -1, -1}, +}; /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { @@ -299,6 +312,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -318,6 +332,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_USB_2] = { .name = "Pinnacle PCTV USB 2", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -342,6 +357,7 @@ struct em28xx_board em28xx_boards[] = { TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, + .has_ir_i2c = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -558,6 +574,27 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2861_BOARD_GADMEI_UTV330PLUS] = { + .name = "Gadmei UTV330+", + .tuner_type = TUNER_TNF_5335MF, + .tda9887_conf = TDA9887_PRESENT, + .ir_codes = &ir_codes_gadmei_rm008z_table, + .decoder = EM28XX_SAA711X, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = SAA7115_COMPOSITE2, + .amux = EM28XX_AMUX_VIDEO, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + } }, + }, [EM2860_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Cinergy A Hybrid XS", .valid = EM28XX_BOARD_NOT_VALIDATED, @@ -715,7 +752,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -740,7 +777,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, .mts_firmware = 1, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -766,7 +803,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -792,7 +829,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_hauppauge_new, + .ir_codes = &ir_codes_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -818,7 +855,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_pinnacle_pctv_hd, + .ir_codes = &ir_codes_pinnacle_pctv_hd_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -844,7 +881,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = ir_codes_ati_tv_wonder_hd_600, + .ir_codes = &ir_codes_ati_tv_wonder_hd_600_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -870,6 +907,8 @@ struct em28xx_board em28xx_boards[] = { .decoder = EM28XX_TVP5150, .has_dvb = 1, .dvb_gpio = default_digital, + .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -937,6 +976,7 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_TERRATEC_CINERGY_200] = { .name = "Terratec Cinergy 200 USB", .is_em2800 = 1, + .has_ir_i2c = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, @@ -1010,7 +1050,8 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker", + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker " + "/ Kworld DVD Maker 2", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { @@ -1420,7 +1461,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .decoder = EM28XX_TVP5150, .tuner_gpio = default_tuner_gpio, - .ir_codes = ir_codes_kaiomy, + .ir_codes = &ir_codes_kaiomy_table, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1520,7 +1561,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = evga_indtube_digital, - .ir_codes = ir_codes_evga_indtube, + .ir_codes = &ir_codes_evga_indtube_table, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1538,6 +1579,14 @@ struct em28xx_board em28xx_boards[] = { .gpio = evga_indtube_analog, } }, }, + /* eb1a:2868 Empia EM2870 + Philips CU1216L NIM (Philips TDA10023 + + Infineon TUA6034) */ + [EM2870_BOARD_REDDO_DVB_C_USB_BOX] = { + .name = "Reddo DVB-C USB TV Box", + .tuner_type = TUNER_ABSENT, + .has_dvb = 1, + .dvb_gpio = reddo_dvb_c_usb_box, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1565,6 +1614,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2883), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2868), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0xe300), .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U }, { USB_DEVICE(0xeb1a, 0xe303), @@ -1591,6 +1642,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_KWORLD_355U }, { USB_DEVICE(0x1b80, 0xe302), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ + { USB_DEVICE(0x1b80, 0xe304), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), @@ -1649,6 +1702,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U }, { USB_DEVICE(0x04bb, 0x0515), .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ }, + { USB_DEVICE(0xeb1a, 0x50a6), + .driver_info = EM2860_BOARD_GADMEI_UTV330 }, { }, }; MODULE_DEVICE_TABLE(usb, em28xx_id_table); @@ -1661,9 +1716,10 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x6ce05a8f, EM2820_BOARD_PROLINK_PLAYTV_USB2, TUNER_YMEC_TVF_5533MF}, {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, - {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0x166a0441, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, {0xb8846b20, EM2881_BOARD_PINNACLE_HYBRID_PRO, TUNER_XC2028}, + {0x63f653bd, EM2870_BOARD_REDDO_DVB_C_USB_BOX, TUNER_ABSENT}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1672,6 +1728,7 @@ static struct em28xx_hash_table em28xx_i2c_hash[] = { {0xf51200e3, EM2800_BOARD_VGEAR_POCKETTV, TUNER_LG_PAL_NEW_TAPC}, {0x1ba50080, EM2860_BOARD_SAA711X_REFERENCE_DESIGN, TUNER_ABSENT}, {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC}, + {0x4ba50080, EM2861_BOARD_GADMEI_UTV330PLUS, TUNER_TNF_5335MF}, }; /* I2C possible address to saa7115, tvp5150, msp3400, tvaudio */ @@ -2170,8 +2227,6 @@ static int em28xx_hint_board(struct em28xx *dev) /* ----------------------------------------------------------------------- */ void em28xx_register_i2c_ir(struct em28xx *dev) { - struct i2c_board_info info; - struct IR_i2c_init_data init_data; const unsigned short addr_list[] = { 0x30, 0x47, I2C_CLIENT_END }; @@ -2179,45 +2234,33 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (disable_ir) return; - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&init_data, 0, sizeof(struct IR_i2c_init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + memset(&dev->info, 0, sizeof(&dev->info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); /* detect & configure */ switch (dev->model) { - case (EM2800_BOARD_UNKNOWN): - break; - case (EM2820_BOARD_UNKNOWN): - break; - case (EM2800_BOARD_TERRATEC_CINERGY_200): - case (EM2820_BOARD_TERRATEC_CINERGY_250): - init_data.ir_codes = ir_codes_em_terratec; - init_data.get_key = em28xx_get_key_terratec; - init_data.name = "i2c IR (EM28XX Terratec)"; - break; - case (EM2820_BOARD_PINNACLE_USB_2): - init_data.ir_codes = ir_codes_pinnacle_grey; - init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; - break; - case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): - init_data.ir_codes = ir_codes_hauppauge_new; - init_data.get_key = em28xx_get_key_em_haup; - init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case (EM2820_BOARD_MSI_VOX_USB_2): + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = &ir_codes_em_terratec_table; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; break; - case (EM2800_BOARD_LEADTEK_WINFAST_USBII): + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; break; - case (EM2800_BOARD_KWORLD_USB2800): - break; - case (EM2800_BOARD_GRABBEEX_USB2800): + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; break; } - if (init_data.name) - info.platform_data = &init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); + if (dev->init_data.name) + dev->info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); } void em28xx_card_setup(struct em28xx *dev) @@ -2253,7 +2296,7 @@ void em28xx_card_setup(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; -#ifdef CONFIG_MODULES +#if defined(CONFIG_MODULES) && defined(MODULE) request_module("tveeprom"); #endif /* Call first TVeeprom */ @@ -2267,10 +2310,6 @@ void em28xx_card_setup(struct em28xx *dev) dev->i2s_speed = 2048000; dev->board.has_msp34xx = 1; } -#ifdef CONFIG_MODULES - if (tv.has_ir) - request_module("ir-kbd-i2c"); -#endif break; } case EM2882_BOARD_KWORLD_ATSC_315U: @@ -2311,6 +2350,10 @@ void em28xx_card_setup(struct em28xx *dev) break; } +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c && !disable_ir) + request_module("ir-kbd-i2c"); +#endif if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); @@ -2329,55 +2372,55 @@ void em28xx_card_setup(struct em28xx *dev) /* request some modules */ if (dev->board.has_msp34xx) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "msp3400", "msp3400", msp3400_addrs); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "msp3400", "msp3400", 0, msp3400_addrs); if (dev->board.decoder == EM28XX_SAA711X) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "saa7115", "saa7115_auto", saa711x_addrs); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "saa7115", "saa7115_auto", 0, saa711x_addrs); if (dev->board.decoder == EM28XX_TVP5150) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvp5150", "tvp5150", tvp5150_addrs); + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvp5150", "tvp5150", 0, tvp5150_addrs); if (dev->em28xx_sensor == EM28XX_MT9V011) { struct v4l2_subdev *sd; - sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, - &dev->i2c_adap, "mt9v011", "mt9v011", mt9v011_addrs); + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, + &dev->i2c_adap, "mt9v011", "mt9v011", 0, mt9v011_addrs); v4l2_subdev_call(sd, core, s_config, 0, &dev->sensor_xtal); } if (dev->board.adecoder == EM28XX_TVAUDIO) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tvaudio", "tvaudio", dev->board.tvaudio_addr); + "tvaudio", "tvaudio", dev->board.tvaudio_addr, NULL); if (dev->board.tuner_type != TUNER_ABSENT) { int has_demod = (dev->tda9887_conf & TDA9887_PRESENT); if (dev->board.radio.type) v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", "tuner", dev->board.radio_addr); + "tuner", "tuner", dev->board.radio_addr, NULL); if (has_demod) - v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); + 0, v4l2_i2c_tuner_addrs(ADDRS_DEMOD)); if (dev->tuner_addr == 0) { enum v4l2_i2c_tuner_type type = has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV; struct v4l2_subdev *sd; - sd = v4l2_i2c_new_probed_subdev(&dev->v4l2_dev, + sd = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, "tuner", "tuner", - v4l2_i2c_tuner_addrs(type)); + 0, v4l2_i2c_tuner_addrs(type)); if (sd) dev->tuner_addr = v4l2_i2c_subdev_addr(sd); } else { v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, - "tuner", "tuner", dev->tuner_addr); + "tuner", "tuner", dev->tuner_addr, NULL); } } @@ -2551,7 +2594,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, * Default format, used for tvp5150 or saa711x output formats */ dev->vinmode = 0x10; - dev->vinctl = 0x11; + dev->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); @@ -2570,6 +2614,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); if (dev->board.has_msp34xx) { diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 98e140b5d95..a88257a7d94 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); + /* FIXME */ #define em28xx_isocdbg(fmt, arg...) do {\ if (core_debug) \ @@ -648,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + if (disable_vbi == 1) + return 0; + + if (dev->chip_id == CHIP_ID_EM2860 || + dev->chip_id == CHIP_ID_EM2883) + return 1; + + /* Version of em28xx that does not support VBI */ + return 0; +} + int em28xx_set_outfmt(struct em28xx *dev) { int ret; + u8 vinctrl; ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, dev->format->reg | 0x20, 0xff); @@ -661,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev) if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); + vinctrl = dev->vinctl; + if (em28xx_vbi_supported(dev) == 1) { + vinctrl |= EM28XX_VINCTRL_VBI_RAW; + em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c); + } + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -732,7 +760,14 @@ int em28xx_resolution_set(struct em28xx *dev) em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + + /* If we don't set the start position to 4 in VBI mode, we end up + with line 21 being YUYV encoded instead of being in 8-bit + greyscale */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2); + else + em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } @@ -844,8 +879,7 @@ EXPORT_SYMBOL_GPL(em28xx_set_mode); */ static void em28xx_irq_callback(struct urb *urb) { - struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx *dev = urb->context; int rc, i; switch (urb->status) { @@ -930,6 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; int i; int sb_size, pipe; struct urb *urb; @@ -959,7 +994,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } dev->isoc_ctl.max_pkt_size = max_pkt_size; - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + dev->isoc_ctl.vbi_buf = NULL; sb_size = max_packets * dev->isoc_ctl.max_pkt_size; @@ -994,7 +1030,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, usb_fill_int_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, - em28xx_irq_callback, dma_q, 1); + em28xx_irq_callback, dev, 1); urb->number_of_packets = max_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -1009,6 +1045,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } init_waitqueue_head(&dma_q->wq); + init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); @@ -1094,7 +1131,7 @@ struct em28xx *em28xx_get_device(int minor, list_for_each_entry(h, &em28xx_devlist, devlist) { if (h->vdev->minor == minor) dev = h; - if (h->vbi_dev->minor == minor) { + if (h->vbi_dev && h->vbi_dev->minor == minor) { dev = h; *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index d603575431b..db749461e5c 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -33,6 +33,7 @@ #include "s5h1409.h" #include "mt352.h" #include "mt352_priv.h" /* FIXME */ +#include "tda1002x.h" MODULE_DESCRIPTION("driver for em28xx based DVB cards"); MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>"); @@ -295,6 +296,11 @@ static struct mt352_config terratec_xs_mt352_cfg = { .demod_init = mt352_terratec_xs_init, }; +static struct tda10023_config em28xx_tda10023_config = { + .demod_address = 0x0c, + .invert = 1, +}; + /* ------------------------------------------------------------------ */ static int attach_xc3028(u8 addr, struct em28xx *dev) @@ -549,6 +555,19 @@ static int dvb_init(struct em28xx *dev) } break; #endif + case EM2870_BOARD_REDDO_DVB_C_USB_BOX: + /* Philips CU1216L NIM (Philips TDA10023 + Infineon TUA6034) */ + dvb->frontend = dvb_attach(tda10023_attach, + &em28xx_tda10023_config, + &dev->i2c_adap, 0x48); + if (dvb->frontend) { + if (!dvb_attach(simple_tuner_attach, dvb->frontend, + &dev->i2c_adap, 0x60, TUNER_PHILIPS_CU1216L)) { + result = -EINVAL; + goto out_free; + } + } + break; default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" " isn't supported yet\n", diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index 27e33a287df..71474d31e15 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -459,7 +459,6 @@ static struct i2c_algorithm em28xx_algo = { static struct i2c_adapter em28xx_adap_template = { .owner = THIS_MODULE, .name = "em28xx", - .id = I2C_HW_B_EM28XX, .algo = &em28xx_algo, }; diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index 6bf84bd787d..ed12e7ffcbd 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -86,7 +86,19 @@ #define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b #define EM28XX_R10_VINMODE 0x10 + #define EM28XX_R11_VINCTRL 0x11 + +/* em28xx Video Input Control Register 0x11 */ +#define EM28XX_VINCTRL_VBI_SLICED 0x80 +#define EM28XX_VINCTRL_VBI_RAW 0x40 +#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */ +#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10 +#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */ +#define EM28XX_VINCTRL_FID_ON_HREF 0x04 +#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02 +#define EM28XX_VINCTRL_INTERLACED 0x01 + #define EM28XX_R12_VINENABLE 0x12 /* */ #define EM28XX_R14_GAMMA 0x14 @@ -135,6 +147,10 @@ #define EM28XX_R31_HSCALEHIGH 0x31 #define EM28XX_R32_VSCALELOW 0x32 #define EM28XX_R33_VSCALEHIGH 0x33 +#define EM28XX_R34_VBI_START_H 0x34 +#define EM28XX_R35_VBI_START_V 0x35 +#define EM28XX_R36_VBI_WIDTH 0x36 +#define EM28XX_R37_VBI_HEIGHT 0x37 #define EM28XX_R40_AC97LSB 0x40 #define EM28XX_R41_AC97MSB 0x41 diff --git a/drivers/media/video/em28xx/em28xx-vbi.c b/drivers/media/video/em28xx/em28xx-vbi.c new file mode 100644 index 00000000000..94943e5a152 --- /dev/null +++ b/drivers/media/video/em28xx/em28xx-vbi.c @@ -0,0 +1,142 @@ +/* + em28xx-vbi.c - VBI driver for em28xx + + Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> + + This work was sponsored by EyeMagnet Limited. + + 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., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include "em28xx.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +static unsigned int vbi_debug; +module_param(vbi_debug, int, 0644); +MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); + +#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + *size = 720 * 12 * 2; + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + int rc = 0; + unsigned int size; + + size = 720 * 12 * 2; + + buf->vb.size = size; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = 720; + buf->vb.height = 12; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops em28xx_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index ab079d9256c..3a1dfb7726f 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -124,7 +124,7 @@ static struct em28xx_fmt format[] = { /* supported controls */ /* Common to all boards */ -static struct v4l2_queryctrl em28xx_qctrl[] = { +static struct v4l2_queryctrl ac97_qctrl[] = { { .id = V4L2_CID_AUDIO_VOLUME, .type = V4L2_CTRL_TYPE_INTEGER, @@ -133,7 +133,7 @@ static struct v4l2_queryctrl em28xx_qctrl[] = { .maximum = 0x1f, .step = 0x1, .default_value = 0x1f, - .flags = 0, + .flags = V4L2_CTRL_FLAG_SLIDER, }, { .id = V4L2_CID_AUDIO_MUTE, .type = V4L2_CTRL_TYPE_BOOLEAN, @@ -163,7 +163,24 @@ static inline void buffer_filled(struct em28xx *dev, buf->vb.field_count++; do_gettimeofday(&buf->vb.ts); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void vbi_buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -239,7 +256,8 @@ static void em28xx_copy_video(struct em28xx *dev, if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n", + em28xx_isocdbg("Overflow of %zi bytes past buffer end" + "(2)\n", ((char *)startwrite + lencopy) - ((char *)outp + buf->vb.size)); lencopy = remain = (char *)outp + buf->vb.size - @@ -256,6 +274,63 @@ static void em28xx_copy_video(struct em28xx *dev, dma_q->pos += len; } +static void em28xx_copy_vbi(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *startwrite, *startread; + int offset; + int bytesperline = 720; + + if (dev == NULL) { + em28xx_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + em28xx_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) { + return; + } + if (p == NULL) { + em28xx_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + em28xx_isocdbg("outp is null\n"); + return; + } + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + if ((p[0] == 0x33 && p[1] == 0x95) || + (p[0] == 0x88 && p[1] == 0x88)) { + /* Header field, advance past it */ + p += 4; + } else { + len += 4; + } + + startread = p; + + startwrite = outp + dma_q->pos; + offset = dma_q->pos; + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) { + startwrite += bytesperline * 0x0c; + offset += bytesperline * 0x0c; + } + + memcpy(startwrite, startread, len); + dma_q->pos += len; +} + static inline void print_err_status(struct em28xx *dev, int packet, int status) { @@ -306,7 +381,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; *buf = NULL; return; } @@ -318,7 +393,34 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, outp = videobuf_to_vmalloc(&(*buf)->vb); memset(outp, 0, (*buf)->vb.size); - dev->isoc_ctl.buf = *buf; + dev->isoc_ctl.vid_buf = *buf; + + return; +} + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); + char *outp; + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); + + dev->isoc_ctl.vbi_buf = *buf; return; } @@ -329,7 +431,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx_dmaqueue *dma_q = &dev->vidq; unsigned char *outp = NULL; int i, len = 0, rc = 1; unsigned char *p; @@ -346,7 +448,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.buf; + buf = dev->isoc_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -410,6 +512,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return rc; } +/* Version of isoc handler that takes into account a mixture of video and + VBI data */ +static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf, *vbi_buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + int vbi_size; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.vid_buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* capture type 0 = vbi start + capture type 1 = video start + capture type 2 = video in progress */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + } + + /* FIXME: get rid of hard-coded value */ + vbi_size = 720 * 0x0c; + + if (dev->capture_type == 0) { + if (dev->vbi_read >= vbi_size) { + /* We've already read all the VBI data, so + treat the rest as video */ + em28xx_isocdbg("dev->vbi_read > vbi_size\n"); + } else if ((dev->vbi_read + len) < vbi_size) { + /* This entire frame is VBI data */ + if (dev->vbi_read == 0 && + (!(dev->cur_field & 1))) { + /* Brand new frame */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + } + + if (dev->vbi_read == 0) { + vbi_dma_q->pos = 0; + if (vbi_buf != NULL) { + if (dev->cur_field & 1) + vbi_buf->top_field = 0; + else + vbi_buf->top_field = 1; + } + } + + dev->vbi_read += len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + } else { + /* Some of this frame is VBI data and some is + video data */ + int vbi_data_len = vbi_size - dev->vbi_read; + dev->vbi_read += vbi_data_len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, vbi_data_len); + dev->capture_type = 1; + p += vbi_data_len; + len -= vbi_data_len; + } + } + + if (dev->capture_type == 1) { + dev->capture_type = 2; + em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], + len, (p[2] & 1) ? "odd" : "even"); + + if (dev->progressive || !(dev->cur_field & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + if (buf != NULL) { + if (dev->cur_field & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + if (buf != NULL && dev->capture_type == 2) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + + /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -421,7 +670,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) struct em28xx *dev = fh->dev; struct v4l2_frequency f; - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) + >> 3; if (0 == *count) *count = EM28XX_DEF_BUF; @@ -458,8 +708,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; + if (dev->isoc_ctl.vid_buf == buf) + dev->isoc_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -475,7 +725,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx *dev = fh->dev; int rc = 0, urb_init = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -494,9 +745,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, dev->max_pkt_size, - em28xx_isoc_copy); + if (em28xx_vbi_supported(dev) == 1) + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy_vbi); + else + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy); if (rc < 0) goto fail; } @@ -578,41 +836,89 @@ static void video_mux(struct em28xx *dev, int index) } /* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh) +static int res_get(struct em28xx_fh *fh, unsigned int bit) { struct em28xx *dev = fh->dev; - int rc = 0; - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (dev->stream_on) - return -EBUSY; + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + em28xx_videodbg("res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} - dev->stream_on = 1; - fh->stream_on = 1; - return rc; +static int res_check(struct em28xx_fh *fh, unsigned int bit) +{ + return fh->resources & bit; } -static int res_check(struct em28xx_fh *fh) +static int res_locked(struct em28xx *dev, unsigned int bit) { - return fh->stream_on; + return dev->resources & bit; } -static void res_free(struct em28xx_fh *fh) +static void res_free(struct em28xx_fh *fh, unsigned int bits) { struct em28xx *dev = fh->dev; - fh->stream_on = 0; - dev->stream_on = 0; + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + em28xx_videodbg("res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +static int get_ressource(struct em28xx_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } +} + +/* + * ac97_queryctrl() + * return the ac97 supported controls + */ +static int ac97_queryctrl(struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); + return 0; + } + } + + /* Control is not ac97 related */ + return 1; } /* - * em28xx_get_ctrl() - * return the current saturation, brightness or contrast, mute state + * ac97_get_ctrl() + * return the current values for ac97 mute and volume */ -static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) +static int ac97_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) { switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -622,29 +928,41 @@ static int em28xx_get_ctrl(struct em28xx *dev, struct v4l2_control *ctrl) ctrl->value = dev->volume; return 0; default: - return -EINVAL; + /* Control is not ac97 related */ + return 1; } } /* - * em28xx_set_ctrl() - * mute or set new saturation, brightness or contrast + * ac97_set_ctrl() + * set values for ac97 mute and volume */ -static int em28xx_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) +static int ac97_set_ctrl(struct em28xx *dev, const struct v4l2_control *ctrl) { + int i; + + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) + if (ctrl->id == ac97_qctrl[i].id) + goto handle; + + /* Announce that hasn't handle it */ + return 1; + +handle: + if (ctrl->value < ac97_qctrl[i].minimum || + ctrl->value > ac97_qctrl[i].maximum) + return -ERANGE; + switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (ctrl->value != dev->mute) { - dev->mute = ctrl->value; - return em28xx_audio_analog_set(dev); - } - return 0; + dev->mute = ctrl->value; + break; case V4L2_CID_AUDIO_VOLUME: dev->volume = ctrl->value; - return em28xx_audio_analog_set(dev); - default: - return -EINVAL; + break; } + + return em28xx_audio_analog_set(dev); } static int check_dev(struct em28xx *dev) @@ -751,7 +1069,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } else { /* width must even because of the YUYV format height must be even because of interlacing */ - v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, + 1, 0); } get_scale(dev, width, height, &hscale, &vscale); @@ -817,12 +1136,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out; } - if (dev->stream_on && !fh->stream_on) { - em28xx_errdev("%s device in use by another fh\n", __func__); - rc = -EBUSY; - goto out; - } - rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); @@ -831,6 +1144,21 @@ out: return rc; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + *norm = dev->norm; + + return 0; +} + static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; @@ -974,6 +1302,9 @@ static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + switch (a->index) { case EM28XX_AMUX_VIDEO: strcpy(a->name, "Television"); @@ -1015,6 +1346,9 @@ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a) struct em28xx *dev = fh->dev; + if (!dev->audio_mode.has_audio) + return -EINVAL; + if (a->index >= MAX_EM28XX_INPUT) return -EINVAL; if (0 == INPUT(a->index)->type) @@ -1038,7 +1372,6 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; int id = qc->id; - int i; int rc; rc = check_dev(dev); @@ -1049,15 +1382,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, qc->id = id; - if (!dev->board.has_msp34xx) { - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); - return 0; - } - } + /* enumberate AC97 controls */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) { + rc = ac97_queryctrl(qc); + if (!rc) + return 0; } + /* enumberate V4L2 device controls */ mutex_lock(&dev->lock); v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc); mutex_unlock(&dev->lock); @@ -1082,14 +1414,16 @@ static int vidioc_g_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_get_ctrl(dev, ctrl); + else + rc = 1; + + /* It were not an AC97 control. Sends it to the v4l2 dev interface */ + if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - else { - rc = em28xx_get_ctrl(dev, ctrl); - if (rc < 0) { - v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl); - rc = 0; - } + rc = 0; } mutex_unlock(&dev->lock); @@ -1101,7 +1435,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - u8 i; int rc; rc = check_dev(dev); @@ -1110,28 +1443,31 @@ static int vidioc_s_ctrl(struct file *file, void *priv, mutex_lock(&dev->lock); - if (dev->board.has_msp34xx) - v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - else { + /* Set an AC97 control */ + if (dev->audio_mode.ac97 != EM28XX_NO_AC97) + rc = ac97_set_ctrl(dev, ctrl); + else rc = 1; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (ctrl->id == em28xx_qctrl[i].id) { - if (ctrl->value < em28xx_qctrl[i].minimum || - ctrl->value > em28xx_qctrl[i].maximum) { - rc = -ERANGE; - break; - } - - rc = em28xx_set_ctrl(dev, ctrl); - break; - } - } - } - /* Control not found - try to send it to the attached devices */ + /* It isn't an AC97 control. Sends it to the v4l2 dev interface */ if (rc == 1) { v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl); - rc = 0; + + /* + * In the case of non-AC97 volume controls, we still need + * to do some setups at em28xx, in order to mute/unmute + * and to adjust audio volume. However, the value ranges + * should be checked by the corresponding V4L subdriver. + */ + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + dev->mute = ctrl->value; + rc = em28xx_audio_analog_set(dev); + break; + case V4L2_CID_AUDIO_VOLUME: + dev->volume = ctrl->value; + rc = em28xx_audio_analog_set(dev); + } } mutex_unlock(&dev->lock); @@ -1275,8 +1611,9 @@ static int vidioc_g_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1327,8 +1664,9 @@ static int vidioc_s_register(struct file *file, void *priv, v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); return 0; case V4L2_CHIP_MATCH_I2C_ADDR: - /* Not supported yet */ - return -EINVAL; + /* TODO: is this correct? */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg); + return 0; default: if (!v4l2_chip_match_host(®->match)) return -EINVAL; @@ -1372,20 +1710,25 @@ static int vidioc_streamon(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; + int rc = -EINVAL; rc = check_dev(dev); if (rc < 0) return rc; + if (unlikely(type != fh->type)) + return -EINVAL; + + em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - mutex_lock(&dev->lock); - rc = res_get(fh); + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; - if (likely(rc >= 0)) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) rc = videobuf_streamon(&fh->vb_vidq); - - mutex_unlock(&dev->lock); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); return rc; } @@ -1401,17 +1744,22 @@ static int vidioc_streamoff(struct file *file, void *priv, if (rc < 0) return rc; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; if (type != fh->type) return -EINVAL; - mutex_lock(&dev->lock); - - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); + em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } return 0; } @@ -1431,9 +1779,14 @@ static int vidioc_querycap(struct file *file, void *priv, cap->capabilities = V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + + if (dev->audio_mode.has_audio) + cap->capabilities |= V4L2_CAP_AUDIO; + if (dev->tuner_type != TUNER_ABSENT) cap->capabilities |= V4L2_CAP_TUNER; @@ -1498,6 +1851,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return 0; } +/* RAW VBI ioctls */ + +static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + format->fmt.vbi.samples_per_line = 720; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + + return 0; +} + +static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) +{ + format->fmt.vbi.samples_per_line = 720; + format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; + + return 0; +} static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) @@ -1510,7 +1902,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_reqbufs(&fh->vb_vidq, rb); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->vb_vidq, rb); + else + return videobuf_reqbufs(&fh->vb_vbiq, rb); } static int vidioc_querybuf(struct file *file, void *priv, @@ -1524,7 +1919,18 @@ static int vidioc_querybuf(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_querybuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->vb_vidq, b); + else { + /* FIXME: I'm not sure yet whether this is a bug in zvbi or + the videobuf framework, but we probably shouldn't be + returning a buffer larger than that which was asked for. + At a minimum, it causes a crash in zvbi since it does + a memcpy based on the source buffer length */ + int result = videobuf_querybuf(&fh->vb_vbiq, b); + b->length = 17280; + return result; + } } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1537,7 +1943,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_qbuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->vb_vidq, b); + else + return videobuf_qbuf(&fh->vb_vbiq, b); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1550,7 +1959,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & + O_NONBLOCK); + else + return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & + O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -1558,7 +1972,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { struct em28xx_fh *fh = priv; - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + else + return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8); } #endif @@ -1654,9 +2071,9 @@ static int radio_queryctrl(struct file *file, void *priv, qc->id >= V4L2_CID_LASTP1) return -EINVAL; - for (i = 0; i < ARRAY_SIZE(em28xx_qctrl); i++) { - if (qc->id && qc->id == em28xx_qctrl[i].id) { - memcpy(qc, &(em28xx_qctrl[i]), sizeof(*qc)); + for (i = 0; i < ARRAY_SIZE(ac97_qctrl); i++) { + if (qc->id && qc->id == ac97_qctrl[i].id) { + memcpy(qc, &(ac97_qctrl[i]), sizeof(*qc)); return 0; } } @@ -1723,8 +2140,15 @@ static int em28xx_v4l2_open(struct file *filp) field = V4L2_FIELD_INTERLACED; videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, fh->type, field, - sizeof(struct em28xx_buffer), fh); + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, field, + sizeof(struct em28xx_buffer), fh); + + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); @@ -1781,20 +2205,21 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_stop(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } - mutex_lock(&dev->lock); - if (res_check(fh)) - res_free(fh); + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } if (dev->users == 1) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - /* the device is already disconnect, free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - mutex_unlock(&dev->lock); kfree(dev); return 0; } @@ -1815,10 +2240,12 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } + + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); - mutex_unlock(&dev->lock); return 0; } @@ -1843,16 +2270,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; + if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) + return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); } + + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; } @@ -1870,17 +2303,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return POLLERR; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { return POLLERR; - - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } } /* @@ -1896,14 +2329,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -1929,6 +2358,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, @@ -1941,6 +2372,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_querybuf = vidioc_querybuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, @@ -2062,13 +2494,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->mute = 1; dev->volume = 0x1f; - /* enable vbi capturing */ - /* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val)); - em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); em28xx_set_outfmt(dev); em28xx_colorlevels_set_default(dev); @@ -2091,14 +2520,17 @@ int em28xx_register_analog_devices(struct em28xx *dev) } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + if (em28xx_vbi_supported(dev) == 1) { + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + "vbi"); - /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]); - if (ret < 0) { - em28xx_errdev("unable to register vbi device\n"); - return ret; + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("unable to register vbi device\n"); + return ret; + } } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { @@ -2118,8 +2550,12 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->radio_dev->num); } - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", - dev->vdev->num, dev->vbi_dev->num); + em28xx_info("V4L2 video device registered as /dev/video%d\n", + dev->vdev->num); + + if (dev->vbi_dev) + em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n", + dev->vbi_dev->num); return 0; } diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index a2add61f7d5..0a73e8bf0d6 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -108,6 +108,8 @@ #define EM2882_BOARD_KWORLD_ATSC_315U 69 #define EM2882_BOARD_EVGA_INDTUBE 70 #define EM2820_BOARD_SILVERCREST_WEBCAM 71 +#define EM2861_BOARD_GADMEI_UTV330PLUS 72 +#define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -213,7 +215,8 @@ struct em28xx_usb_isoc_ctl { int tmp_buf_len; /* Stores already requested buffers */ - struct em28xx_buffer *buf; + struct em28xx_buffer *vid_buf; + struct em28xx_buffer *vbi_buf; /* Stores the number of received fields */ int nfields; @@ -398,6 +401,7 @@ struct em28xx_board { unsigned int has_snapshot_button:1; unsigned int is_webcam:1; unsigned int valid:1; + unsigned int has_ir_i2c:1; unsigned char xclk, i2c_speed; unsigned char radio_addr; @@ -408,7 +412,7 @@ struct em28xx_board { struct em28xx_input input[MAX_EM28XX_INPUT]; struct em28xx_input radio; - IR_KEYTAB_TYPE *ir_codes; + struct ir_scancode_table *ir_codes; }; struct em28xx_eeprom { @@ -441,6 +445,10 @@ enum em28xx_dev_state { #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +/* em28xx resource types (used for res_get/res_lock etc */ +#define EM28XX_RESOURCE_VIDEO 0x01 +#define EM28XX_RESOURCE_VBI 0x02 + struct em28xx_audio { char name[50]; char *transfer_buffer[EM28XX_AUDIO_BUFS]; @@ -461,10 +469,11 @@ struct em28xx; struct em28xx_fh { struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ int radio; + unsigned int resources; struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -491,7 +500,6 @@ struct em28xx { /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; - unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; @@ -542,6 +550,12 @@ struct em28xx { enum em28xx_dev_state state; enum em28xx_io_method io; + /* vbi related state tracking */ + int capture_type; + int vbi_read; + unsigned char cur_field; + + struct work_struct request_module_wk; /* locks */ @@ -553,10 +567,14 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* resources in use */ + unsigned int resources; + unsigned char eedata[256]; /* Isoc control struct */ struct em28xx_dmaqueue vidq; + struct em28xx_dmaqueue vbiq; struct em28xx_usb_isoc_ctl isoc_ctl; spinlock_t slock; @@ -595,6 +613,10 @@ struct em28xx { struct delayed_work sbutton_query_work; struct em28xx_dvb *dvb; + + /* I2C keyboard data */ + struct i2c_board_info info; + struct IR_i2c_init_data init_data; }; struct em28xx_ops { @@ -633,6 +655,7 @@ int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); +int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); @@ -680,6 +703,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev); int em28xx_ir_init(struct em28xx *dev); int em28xx_ir_fini(struct em28xx *dev); +/* Provided by em28xx-vbi.c */ +extern struct videobuf_queue_ops em28xx_vbi_qops; + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ |