From 40b97ef43a4edbaeda1c8ef1a3468302bd7c757d Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Tue, 10 Jan 2017 01:14:18 -0200 Subject: [PATCH 1/7] lgdt3306a: support i2c mux for use by em28xx Adds an i2c mux to the lgdt3306a demodulator. This was done to support the Hauppauge WinTV-dualHD 01595 USB TV tuner (em28xx), which utilizes two si2157 tuners behind gate control. Signed-off-by: Kevin Cheng Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb-frontends/Kconfig | 2 +- drivers/media/dvb-frontends/lgdt3306a.c | 108 ++++++++++++++++++++++++ drivers/media/dvb-frontends/lgdt3306a.h | 4 + 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 4b59e4a8bdab1..9bd01b6bc2442 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -621,7 +621,7 @@ config DVB_LGDT3305 config DVB_LGDT3306A tristate "LG Electronics LGDT3306A based" - depends on DVB_CORE && I2C + depends on DVB_CORE && I2C && I2C_MUX default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM-B 64/256 demodulator module. Say Y when you want diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 0ca4e810e9d8f..9732635e3cb60 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -22,6 +22,7 @@ #include #include "dvb_math.h" #include "lgdt3306a.h" +#include static int debug; @@ -65,6 +66,8 @@ struct lgdt3306a_state { enum fe_modulation current_modulation; u32 current_frequency; u32 snr; + + struct i2c_mux_core *muxc; }; /* @@ -2131,6 +2134,111 @@ static struct dvb_frontend_ops lgdt3306a_ops = { .search = lgdt3306a_search, }; +static int lgdt3306a_select(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + struct lgdt3306a_state *state = i2c_get_clientdata(client); + + return lgdt3306a_i2c_gate_ctrl(&state->frontend, 1); +} + +static int lgdt3306a_deselect(struct i2c_mux_core *muxc, u32 chan) +{ + struct i2c_client *client = i2c_mux_priv(muxc); + struct lgdt3306a_state *state = i2c_get_clientdata(client); + + return lgdt3306a_i2c_gate_ctrl(&state->frontend, 0); +} + +static int lgdt3306a_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct lgdt3306a_config *config; + struct lgdt3306a_state *state; + struct dvb_frontend *fe; + int ret; + + config = kzalloc(sizeof(struct lgdt3306a_config), GFP_KERNEL); + if (config == NULL) { + ret = -ENOMEM; + goto fail; + } + + memcpy(config, client->dev.platform_data, + sizeof(struct lgdt3306a_config)); + + config->i2c_addr = client->addr; + fe = lgdt3306a_attach(config, client->adapter); + if (fe == NULL) { + ret = -ENODEV; + goto err_fe; + } + + i2c_set_clientdata(client, fe->demodulator_priv); + state = fe->demodulator_priv; + + /* create mux i2c adapter for tuner */ + state->muxc = i2c_mux_alloc(client->adapter, &client->dev, + 1, 0, I2C_MUX_LOCKED, + lgdt3306a_select, lgdt3306a_deselect); + if (!state->muxc) { + ret = -ENOMEM; + goto err_kfree; + } + state->muxc->priv = client; + ret = i2c_mux_add_adapter(state->muxc, 0, 0, 0); + if (ret) + goto err_kfree; + + /* create dvb_frontend */ + fe->ops.i2c_gate_ctrl = NULL; + *config->i2c_adapter = state->muxc->adapter[0]; + *config->fe = fe; + + return 0; + +err_kfree: + kfree(state); +err_fe: + kfree(config); +fail: + dev_dbg(&client->dev, "failed=%d\n", ret); + return ret; +} + +static int lgdt3306a_remove(struct i2c_client *client) +{ + struct lgdt3306a_state *state = i2c_get_clientdata(client); + + i2c_mux_del_adapters(state->muxc); + + state->frontend.ops.release = NULL; + state->frontend.demodulator_priv = NULL; + + kfree(state->cfg); + kfree(state); + + return 0; +} + +static const struct i2c_device_id lgdt3306a_id_table[] = { + {"lgdt3306a", 0}, + {} +}; +MODULE_DEVICE_TABLE(i2c, lgdt3306a_id_table); + +static struct i2c_driver lgdt3306a_driver = { + .driver = { + .name = "lgdt3306a", + .suppress_bind_attrs = true, + }, + .probe = lgdt3306a_probe, + .remove = lgdt3306a_remove, + .id_table = lgdt3306a_id_table, +}; + +module_i2c_driver(lgdt3306a_driver); + MODULE_DESCRIPTION("LG Electronics LGDT3306A ATSC/QAM-B Demodulator Driver"); MODULE_AUTHOR("Fred Richter "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb-frontends/lgdt3306a.h b/drivers/media/dvb-frontends/lgdt3306a.h index 9dbb2dced1fe4..6ce337ec52721 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.h +++ b/drivers/media/dvb-frontends/lgdt3306a.h @@ -56,6 +56,10 @@ struct lgdt3306a_config { /* demod clock freq in MHz; 24 or 25 supported */ int xtalMHz; + + /* returned by driver if using i2c bus multiplexing */ + struct dvb_frontend **fe; + struct i2c_adapter **i2c_adapter; }; #if IS_REACHABLE(CONFIG_DVB_LGDT3306A) From 2f083f07f7ee7e746050978d56468fd3548affff Mon Sep 17 00:00:00 2001 From: Kevin Cheng Date: Tue, 10 Jan 2017 01:14:29 -0200 Subject: [PATCH 2/7] em28xx: support for Hauppauge WinTV-dualHD 01595 ATSC/QAM Hauppauge WinTV-dualHD model 01595 is a USB 2.0 dual ATSC/QAM tuner with the following components: USB bridge: Empia em28274 Demodulator: 2x LG LGDT3306a at addresses 0xb2 and 0x1c Tuner: 2x Silicon Labs si2157 at addresses 0xc0 and 0xc4 This patch enables only the first tuner. Signed-off-by: Kevin Cheng Signed-off-by: Mauro Carvalho Chehab --- drivers/media/usb/em28xx/em28xx-cards.c | 19 +++++++ drivers/media/usb/em28xx/em28xx-dvb.c | 74 +++++++++++++++++++++++++ drivers/media/usb/em28xx/em28xx.h | 1 + 3 files changed, 94 insertions(+) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index e397f544f108c..66fbf39d0d559 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -508,6 +508,7 @@ static struct em28xx_reg_seq plex_px_bcud[] = { /* * 2040:0265 Hauppauge WinTV-dualHD DVB + * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM * reg 0x80/0x84: * GPIO_0: Yellow LED tuner 1, 0=on, 1=off * GPIO_1: Green LED tuner 1, 0=on, 1=off @@ -2389,6 +2390,21 @@ struct em28xx_board em28xx_boards[] = { .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, }, + /* + * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM). + * Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157 + */ + [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = { + .name = "Hauppauge WinTV-dualHD 01595 ATSC/QAM", + .def_i2c_bus = 1, + .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | + EM28XX_I2C_FREQ_400_KHZ, + .tuner_type = TUNER_ABSENT, + .tuner_gpio = hauppauge_dualhd_dvb, + .has_dvb = 1, + .ir_codes = RC_MAP_HAUPPAUGE, + .leds = hauppauge_dualhd_leds, + }, }; EXPORT_SYMBOL_GPL(em28xx_boards); @@ -2514,6 +2530,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, { USB_DEVICE(0x2040, 0x0265), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, + { USB_DEVICE(0x2040, 0x026d), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112), @@ -2949,6 +2967,7 @@ static void em28xx_card_setup(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: case EM2884_BOARD_HAUPPAUGE_WINTV_HVR_930C: case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB: + case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595: { struct tveeprom tv; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 8cedef0daae47..1792829850a5b 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -36,6 +36,7 @@ #include "lgdt330x.h" #include "lgdt3305.h" +#include "lgdt3306a.h" #include "zl10353.h" #include "s5h1409.h" #include "mt2060.h" @@ -917,6 +918,17 @@ static struct tda18271_config pinnacle_80e_dvb_config = { .role = TDA18271_MASTER, }; +static struct lgdt3306a_config hauppauge_01595_lgdt3306a_config = { + .qam_if_khz = 4000, + .vsb_if_khz = 3250, + .spectral_inversion = 0, + .deny_i2c_rptr = 0, + .mpeg_mode = LGDT3306A_MPEG_SERIAL, + .tpclk_edge = LGDT3306A_TPCLK_RISING_EDGE, + .tpvalid_polarity = LGDT3306A_TP_VALID_HIGH, + .xtalMHz = 25, +}; + /* ------------------------------------------------------------------ */ static int em28xx_attach_xc3028(u8 addr, struct em28xx *dev) @@ -1936,6 +1948,68 @@ static int em28xx_dvb_init(struct em28xx *dev) } break; + case EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595: + { + struct i2c_adapter *adapter; + struct i2c_client *client; + struct i2c_board_info info = {}; + struct lgdt3306a_config lgdt3306a_config; + struct si2157_config si2157_config = {}; + + /* attach demod */ + lgdt3306a_config = hauppauge_01595_lgdt3306a_config; + lgdt3306a_config.fe = &dvb->fe[0]; + lgdt3306a_config.i2c_adapter = &adapter; + strlcpy(info.type, "lgdt3306a", sizeof(info.type)); + info.addr = 0x59; + info.platform_data = &lgdt3306a_config; + request_module(info.type); + client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], + &info); + if (client == NULL || client->dev.driver == NULL) { + result = -ENODEV; + goto out_free; + } + + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_demod = client; + + /* attach tuner */ + si2157_config.fe = dvb->fe[0]; + si2157_config.if_port = 1; + si2157_config.inversion = 1; +#ifdef CONFIG_MEDIA_CONTROLLER_DVB + si2157_config.mdev = dev->media_dev; +#endif + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2157", sizeof(info.type)); + info.addr = 0x60; + info.platform_data = &si2157_config; + request_module(info.type); + + client = i2c_new_device(adapter, &info); + if (client == NULL || client->dev.driver == NULL) { + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + if (!try_module_get(client->dev.driver->owner)) { + i2c_unregister_device(client); + module_put(dvb->i2c_client_demod->dev.driver->owner); + i2c_unregister_device(dvb->i2c_client_demod); + result = -ENODEV; + goto out_free; + } + + dvb->i2c_client_tuner = client; + } + break; default: em28xx_errdev("/2: The frontend of your DVB/ATSC card" " isn't supported yet\n"); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index d148463b22c1e..4a8846d53b15a 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -147,6 +147,7 @@ #define EM2884_BOARD_ELGATO_EYETV_HYBRID_2008 97 #define EM28178_BOARD_PLEX_PX_BCUD 98 #define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB 99 +#define EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 100 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 From bde05a916fe8687c2df47b5c439bfa991e5c0870 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Sun, 16 Apr 2017 00:13:39 -0500 Subject: [PATCH 3/7] em28xx: Hauppauge DualHD DVB/ATSC second tuner support --- drivers/media/usb/em28xx/em28xx-cards.c | 129 ++++++++++++++++++++++-- drivers/media/usb/em28xx/em28xx-core.c | 49 +++++++-- drivers/media/usb/em28xx/em28xx-dvb.c | 24 +++-- drivers/media/usb/em28xx/em28xx.h | 11 ++ 4 files changed, 191 insertions(+), 22 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 66fbf39d0d559..123f5868f722b 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2387,6 +2387,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, .tuner_gpio = hauppauge_dualhd_dvb, .has_dvb = 1, + .has_dual_ts = 1, .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, }, @@ -2402,6 +2403,7 @@ struct em28xx_board em28xx_boards[] = { .tuner_type = TUNER_ABSENT, .tuner_gpio = hauppauge_dualhd_dvb, .has_dvb = 1, + .has_dual_ts = 1, .ir_codes = RC_MAP_HAUPPAUGE, .leds = hauppauge_dualhd_leds, }, @@ -3222,7 +3224,8 @@ static void em28xx_release_resources(struct em28xx *dev) em28xx_i2c_unregister(dev, 1); em28xx_i2c_unregister(dev, 0); - usb_put_dev(dev->udev); + if(dev->ts == PRIMARY_TS) + usb_put_dev(dev->udev); /* Mark device as unused */ clear_bit(dev->devno, em28xx_devused); @@ -3423,6 +3426,34 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, return 0; } +int em28xx_duplicate_dev(struct em28xx *dev) +{ + int nr; + struct em28xx *sec_dev = kzalloc(sizeof(*sec_dev), GFP_KERNEL); + if (sec_dev == NULL) { + dev->dev_next = NULL; + return -ENOMEM; + } + memcpy(sec_dev, dev, sizeof(sizeof(*sec_dev))); + /* Check to see next free device and mark as used */ + do { + nr = find_first_zero_bit(em28xx_devused, EM28XX_MAXBOARDS); + if (nr >= EM28XX_MAXBOARDS) { + /* No free device slots */ + printk(DRIVER_NAME ": Supports only %i em28xx boards.\n", + EM28XX_MAXBOARDS); + kfree(sec_dev); + dev->dev_next = NULL; + return -ENOMEM; + } + } while (test_and_set_bit(nr, em28xx_devused)); + sec_dev->devno = nr; + snprintf(sec_dev->name, 28, "em28xx #%d", nr); + sec_dev->dev_next = NULL; + dev->dev_next = sec_dev; + return 0; +} + /* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ #define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) @@ -3436,7 +3467,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, struct usb_device *udev; struct em28xx *dev = NULL; int retval; - bool has_vendor_audio = false, has_video = false, has_dvb = false; + bool has_vendor_audio = false, has_video = false; + bool has_dvb = false, has_dvb_ts2 = false; int i, nr, try_bulk; const int ifnum = interface->altsetting[0].desc.bInterfaceNumber; char *speed; @@ -3543,6 +3575,19 @@ static int em28xx_usb_probe(struct usb_interface *interface, } } break; + case 0x85: + if (usb_endpoint_xfer_isoc(e)) { + if (size > dev->dvb_max_pkt_size_isoc_ts2) { + has_dvb_ts2 = true; /* see NOTE (~) */ + dev->dvb_ep_isoc_ts2 = e->bEndpointAddress; + dev->dvb_max_pkt_size_isoc_ts2 = size; + dev->dvb_alt_isoc = i; + } + } else { + has_dvb_ts2 = true; + dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; + } + break; } } /* NOTE: @@ -3625,6 +3670,10 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->has_video = has_video; dev->ifnum = ifnum; + dev->ts = PRIMARY_TS; + snprintf(dev->name, 28, "em28xx"); + dev->dev_next = NULL; + if (has_vendor_audio) { printk(KERN_INFO DRIVER_NAME ": Audio interface %i found %s\n", ifnum, "(Vendor Class)"); @@ -3703,7 +3752,62 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->dvb_xfer_bulk ? "bulk" : "isoc"); } - kref_init(&dev->ref); + if(dev->board.has_dual_ts && em28xx_duplicate_dev(dev) == 0) + { + dev->dev_next->ts = SECONDARY_TS; + dev->dev_next->alt = -1; + dev->dev_next->is_audio_only = has_vendor_audio && !(has_video || has_dvb); + dev->dev_next->has_video = false; + dev->dev_next->ifnum = ifnum; + dev->dev_next->model = id->driver_info; + + mutex_init(&dev->dev_next->lock); + retval = em28xx_init_dev(dev->dev_next, udev, interface, dev->dev_next->devno); + if (retval) { + goto err_free; + } + + if (usb_xfer_mode < 0) { + if (dev->dev_next->board.is_webcam) + try_bulk = 1; + else + try_bulk = 0; + } else { + try_bulk = usb_xfer_mode > 0; + } + + /* Select USB transfer types to use */ + if (has_dvb) { + if (!dev->dvb_ep_isoc_ts2 || (try_bulk && dev->dvb_ep_bulk_ts2)) + dev->dev_next->dvb_xfer_bulk = 1; + em28xx_info("dvb ts2 set to %s mode.\n", + dev->dev_next->dvb_xfer_bulk ? "bulk" : "isoc"); + } + + dev->dev_next->dvb_ep_isoc = dev->dvb_ep_isoc_ts2; + dev->dev_next->dvb_ep_bulk = dev->dvb_ep_bulk_ts2; + dev->dev_next->dvb_max_pkt_size_isoc = dev->dvb_max_pkt_size_isoc_ts2; + dev->dev_next->dvb_alt_isoc = dev->dvb_alt_isoc; + + /* Configuare hardware to support TS2*/ + if(dev->dvb_xfer_bulk) { + /* The ep4 and ep5 are configuared for BULK */ + em28xx_write_reg(dev, 0x0b, 0x96); + mdelay(100); + em28xx_write_reg(dev, 0x0b, 0x80); + mdelay(100); + } else { + /* The ep4 and ep5 are configuared for ISO */ + em28xx_write_reg(dev, 0x0b, 0x96); + mdelay(100); + em28xx_write_reg(dev, 0x0b, 0x82); + mdelay(100); + } + + kref_init(&dev->dev_next->ref); + } + + kref_init(&dev->ref); request_modules(dev); @@ -3745,7 +3849,13 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) if (!dev) return; - dev->disconnected = 1; + if(dev->dev_next!=NULL) { + dev->dev_next->disconnected = 1; + em28xx_info("Disconnecting %s\n", dev->dev_next->name); + flush_request_modules(dev->dev_next); + } + + dev->disconnected = 1; em28xx_info("Disconnecting %s\n", dev->name); @@ -3753,8 +3863,15 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) em28xx_close_extension(dev); - em28xx_release_resources(dev); - kref_put(&dev->ref, em28xx_free_device); + if(dev->dev_next!=NULL) + em28xx_release_resources(dev->dev_next); + em28xx_release_resources(dev); + + if(dev->dev_next!=NULL) { + kref_put(&dev->dev_next->ref, em28xx_free_device); + dev->dev_next = NULL; + } + kref_put(&dev->ref, em28xx_free_device); } static int em28xx_usb_suspend(struct usb_interface *interface, diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index eebd5d7088d00..296e60a720d60 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -636,10 +636,23 @@ int em28xx_capture_start(struct em28xx *dev, int start) dev->chip_id == CHIP_ID_EM28174 || dev->chip_id == CHIP_ID_EM28178) { /* The Transport Stream Enable Register moved in em2874 */ - rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - start ? - EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE); + if(dev->board.has_dual_ts) { + if (dev->ts == PRIMARY_TS) + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + start ? + EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE); + else + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + start ? + EM2874_TS2_CAPTURE_ENABLE : 0x00, + EM2874_TS2_CAPTURE_ENABLE); + } else { + rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, + start ? + EM2874_TS1_CAPTURE_ENABLE : 0x00, + EM2874_TS1_CAPTURE_ENABLE); + } } else { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ @@ -1072,8 +1085,12 @@ int em28xx_register_extension(struct em28xx_ops *ops) mutex_lock(&em28xx_devlist_mutex); list_add_tail(&ops->next, &em28xx_extension_devlist); list_for_each_entry(dev, &em28xx_devlist, devlist) { - ops->init(dev); - } + if (ops->init) { + ops->init(dev); + if(dev->dev_next!=NULL) + ops->init(dev->dev_next); + } + } mutex_unlock(&em28xx_devlist_mutex); printk(KERN_INFO "em28xx: Registered (%s) extension\n", ops->name); return 0; @@ -1086,7 +1103,11 @@ void em28xx_unregister_extension(struct em28xx_ops *ops) mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(dev, &em28xx_devlist, devlist) { - ops->fini(dev); + if (ops->fini) { + if(dev->dev_next!=NULL) + ops->fini(dev->dev_next); + ops->fini(dev); + } } list_del(&ops->next); mutex_unlock(&em28xx_devlist_mutex); @@ -1101,8 +1122,11 @@ void em28xx_init_extension(struct em28xx *dev) mutex_lock(&em28xx_devlist_mutex); list_add_tail(&dev->devlist, &em28xx_devlist); list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->init) + if (ops->init) { ops->init(dev); + if(dev->dev_next!=NULL) + ops->init(dev->dev_next); + } } mutex_unlock(&em28xx_devlist_mutex); } @@ -1113,8 +1137,11 @@ void em28xx_close_extension(struct em28xx *dev) mutex_lock(&em28xx_devlist_mutex); list_for_each_entry(ops, &em28xx_extension_devlist, next) { - if (ops->fini) + if (ops->fini) { + if(dev->dev_next!=NULL) + ops->fini(dev->dev_next); ops->fini(dev); + } } list_del(&dev->devlist); mutex_unlock(&em28xx_devlist_mutex); @@ -1129,6 +1156,8 @@ int em28xx_suspend_extension(struct em28xx *dev) list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->suspend) ops->suspend(dev); + if(dev->dev_next!=NULL) + ops->suspend(dev->dev_next); } mutex_unlock(&em28xx_devlist_mutex); return 0; @@ -1143,6 +1172,8 @@ int em28xx_resume_extension(struct em28xx *dev) list_for_each_entry(ops, &em28xx_extension_devlist, next) { if (ops->resume) ops->resume(dev); + if(dev->dev_next!=NULL) + ops->resume(dev->dev_next); } mutex_unlock(&em28xx_devlist_mutex); return 0; diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 1792829850a5b..7d818930ec942 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -215,7 +215,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } - usb_set_interface(dev->udev, dev->ifnum, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; @@ -1116,7 +1115,7 @@ static void em28xx_unregister_dvb(struct em28xx_dvb *dvb) static int em28xx_dvb_init(struct em28xx *dev) { - int result = 0; + int result = 0, dvb_alt = 0; struct em28xx_dvb *dvb; if (dev->is_audio_only) { @@ -1899,7 +1898,8 @@ static int em28xx_dvb_init(struct em28xx *dev) si2168_config.ts_mode = SI2168_TS_SERIAL; memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2168", I2C_NAME_SIZE); - info.addr = 0x64; + if(dev->ts == PRIMARY_TS) info.addr = 0x64; + else info.addr = 0x67; info.platform_data = &si2168_config; request_module(info.type); client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], &info); @@ -1925,7 +1925,8 @@ static int em28xx_dvb_init(struct em28xx *dev) #endif memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", I2C_NAME_SIZE); - info.addr = 0x60; + if(dev->ts == PRIMARY_TS) info.addr = 0x60; + else info.addr = 0x63; info.platform_data = &si2157_config; request_module(info.type); client = i2c_new_device(adapter, &info); @@ -1961,7 +1962,8 @@ static int em28xx_dvb_init(struct em28xx *dev) lgdt3306a_config.fe = &dvb->fe[0]; lgdt3306a_config.i2c_adapter = &adapter; strlcpy(info.type, "lgdt3306a", sizeof(info.type)); - info.addr = 0x59; + if(dev->ts == PRIMARY_TS) info.addr = 0x59; + else info.addr = 0x0e; info.platform_data = &lgdt3306a_config; request_module(info.type); client = i2c_new_device(&dev->i2c_adap[dev->def_i2c_bus], @@ -1988,7 +1990,8 @@ static int em28xx_dvb_init(struct em28xx *dev) #endif memset(&info, 0, sizeof(struct i2c_board_info)); strlcpy(info.type, "si2157", sizeof(info.type)); - info.addr = 0x60; + if(dev->ts == PRIMARY_TS) info.addr = 0x60; + else info.addr = 0x62; info.platform_data = &si2157_config; request_module(info.type); @@ -2031,7 +2034,14 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result < 0) goto out_free; - em28xx_info("DVB extension successfully initialized\n"); + if (dev->dvb_xfer_bulk) { + dvb_alt = 0; + } else { /* isoc */ + dvb_alt = dev->dvb_alt_isoc; + } + + usb_set_interface(dev->udev, dev->ifnum, dvb_alt); + em28xx_info("DVB extension successfully initialized\n"); kref_get(&dev->ref); diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 4a8846d53b15a..402ab1cd91872 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -216,6 +216,9 @@ /* max. number of button state polling addresses */ #define EM28XX_NUM_BUTTON_ADDRESSES_MAX 5 +#define PRIMARY_TS 0 +#define SECONDARY_TS 1 + enum em28xx_mode { EM28XX_SUSPEND, EM28XX_ANALOG_MODE, @@ -456,6 +459,7 @@ struct em28xx_board { unsigned int mts_firmware:1; unsigned int max_range_640_480:1; unsigned int has_dvb:1; + unsigned int has_dual_ts:1; unsigned int is_webcam:1; unsigned int valid:1; unsigned int has_ir_i2c:1; @@ -683,6 +687,8 @@ struct em28xx { u8 ifnum; /* number of the assigned usb interface */ u8 analog_ep_isoc; /* address of isoc endpoint for analog */ u8 analog_ep_bulk; /* address of bulk endpoint for analog */ + u8 dvb_ep_isoc_ts2; /* address of isoc endpoint for DVB TS2*/ + u8 dvb_ep_bulk_ts2; /* address of bulk endpoint for DVB TS2*/ u8 dvb_ep_isoc; /* address of isoc endpoint for DVB */ u8 dvb_ep_bulk; /* address of bulk endpoint for DVB */ int alt; /* alternate setting */ @@ -696,6 +702,8 @@ struct em28xx { int dvb_alt_isoc; /* alternate setting for DVB isoc transfers */ unsigned int dvb_max_pkt_size_isoc; /* isoc max packet size of the selected DVB ep at dvb_alt */ + unsigned int dvb_max_pkt_size_isoc_ts2; /* isoc max packet size of the + selected DVB ep at dvb_alt */ unsigned int dvb_xfer_bulk:1; /* use bulk instead of isoc transfers for DVB */ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */ @@ -727,6 +735,9 @@ struct em28xx { struct media_entity input_ent[MAX_EM28XX_INPUT]; struct media_pad input_pad[MAX_EM28XX_INPUT]; #endif + + struct em28xx *dev_next; + int ts; }; #define kref_to_dev(d) container_of(d, struct em28xx, ref) From fa22c49cc03dd59ea1211de3e6a9248dd43d5567 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Thu, 16 Mar 2017 23:10:11 +0000 Subject: [PATCH 4/7] em28xx: bulk transfer implementation fix --- drivers/media/usb/em28xx/em28xx-core.c | 34 +++++++++++++++++++------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 296e60a720d60..27278f731e6da 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -636,17 +636,33 @@ int em28xx_capture_start(struct em28xx *dev, int start) dev->chip_id == CHIP_ID_EM28174 || dev->chip_id == CHIP_ID_EM28178) { /* The Transport Stream Enable Register moved in em2874 */ + if(dev->dvb_xfer_bulk) { + /* TS1 Maximum Transfer Size = 188 * EM28XX_DVB_BULK_PACKET_MULTIPLIER */ + em28xx_write_reg(dev, EM2874_R5D_TS1_PKT_SIZE, 0xef); + } else { + /* TS1 Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, EM2874_R5D_TS1_PKT_SIZE, 0x05); + } + if(dev->board.has_dual_ts) { - if (dev->ts == PRIMARY_TS) - rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - start ? - EM2874_TS1_CAPTURE_ENABLE : 0x00, - EM2874_TS1_CAPTURE_ENABLE); - else + if(start) { + if(dev->dvb_xfer_bulk) { + /* TS2 Maximum Transfer Size = 188 * EM28XX_DVB_BULK_PACKET_MULTIPLIER */ + em28xx_write_reg(dev, EM2874_R5E_TS2_PKT_SIZE, 0xef); + } else { + /* TS2 Maximum Transfer Size = 188 * 5 */ + em28xx_write_reg(dev, EM2874_R5E_TS2_PKT_SIZE, 0x05); + } rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, - start ? - EM2874_TS2_CAPTURE_ENABLE : 0x00, - EM2874_TS2_CAPTURE_ENABLE); + (EM2874_TS1_CAPTURE_ENABLE | EM2874_TS2_CAPTURE_ENABLE), + (EM2874_TS1_CAPTURE_ENABLE | EM2874_TS2_CAPTURE_ENABLE)); + } else { + if(dev->ts == PRIMARY_TS) { + rc = em28xx_toggle_reg_bits(dev, EM2874_R5F_TS_ENABLE, EM2874_TS1_CAPTURE_ENABLE); + } else { + rc = em28xx_toggle_reg_bits(dev, EM2874_R5F_TS_ENABLE, EM2874_TS2_CAPTURE_ENABLE); + } + } } else { rc = em28xx_write_reg_bits(dev, EM2874_R5F_TS_ENABLE, start ? From 67326520ccf64a13135518eaecad7c8427d0d8ca Mon Sep 17 00:00:00 2001 From: Brad Love Date: Thu, 16 Mar 2017 23:13:05 +0000 Subject: [PATCH 5/7] em28xx: usb packet size tweaks --- drivers/media/usb/em28xx/em28xx-dvb.c | 4 ++-- drivers/media/usb/em28xx/em28xx.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-dvb.c b/drivers/media/usb/em28xx/em28xx-dvb.c index 7d818930ec942..2dc9480e65420 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -202,7 +202,7 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) if (dev->dvb_xfer_bulk) { if (!dev->dvb_ep_bulk) return -ENODEV; - dvb_max_packet_size = 512; /* USB 2.0 spec */ + dvb_max_packet_size = 188; packet_multiplier = EM28XX_DVB_BULK_PACKET_MULTIPLIER; dvb_alt = 0; } else { /* isoc */ @@ -1143,7 +1143,7 @@ static int em28xx_dvb_init(struct em28xx *dev) result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, dev->dvb_xfer_bulk, EM28XX_DVB_NUM_BUFS, - 512, + 188, EM28XX_DVB_BULK_PACKET_MULTIPLIER); } else { result = em28xx_alloc_urbs(dev, EM28XX_DIGITAL_MODE, diff --git a/drivers/media/usb/em28xx/em28xx.h b/drivers/media/usb/em28xx/em28xx.h index 402ab1cd91872..d85c546776c1f 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -190,7 +190,7 @@ USB 2.0 spec says bulk packet size is always 512 bytes */ #define EM28XX_BULK_PACKET_MULTIPLIER 384 -#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 384 +#define EM28XX_DVB_BULK_PACKET_MULTIPLIER 240 #define EM28XX_INTERLACED_DEFAULT 1 From 0307dc6150831921d84fdb3aa3e7dcf3cefc64ac Mon Sep 17 00:00:00 2001 From: Brad Love Date: Thu, 16 Mar 2017 23:29:26 +0000 Subject: [PATCH 6/7] lgdt3306a: add QAM AUTO support --- drivers/media/dvb-frontends/lgdt3306a.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/media/dvb-frontends/lgdt3306a.c b/drivers/media/dvb-frontends/lgdt3306a.c index 9732635e3cb60..291b8f3031c28 100644 --- a/drivers/media/dvb-frontends/lgdt3306a.c +++ b/drivers/media/dvb-frontends/lgdt3306a.c @@ -624,6 +624,9 @@ static int lgdt3306a_set_modulation(struct lgdt3306a_state *state, case QAM_256: ret = lgdt3306a_set_qam(state, QAM_256); break; + case QAM_AUTO: + ret = lgdt3306a_set_qam(state, QAM_64); + break; default: return -EINVAL; } @@ -649,6 +652,7 @@ static int lgdt3306a_agc_setup(struct lgdt3306a_state *state, break; case QAM_64: case QAM_256: + case QAM_AUTO: break; default: return -EINVAL; @@ -703,6 +707,7 @@ static int lgdt3306a_spectral_inversion(struct lgdt3306a_state *state, break; case QAM_64: case QAM_256: + case QAM_AUTO: /* Auto ok for QAM */ ret = lgdt3306a_set_inversion_auto(state, 1); break; @@ -726,6 +731,7 @@ static int lgdt3306a_set_if(struct lgdt3306a_state *state, break; case QAM_64: case QAM_256: + case QAM_AUTO: if_freq_khz = state->cfg->qam_if_khz; break; default: @@ -1644,6 +1650,9 @@ static int lgdt3306a_read_signal_strength(struct dvb_frontend *fe, case QAM_256: ref_snr = 2800; /* 28dB */ break; + case QAM_AUTO: + ref_snr = 2200; /* 22dB */ + break; default: return -EINVAL; } From 37b5c4aa62e481183335cb787623a499ed51c221 Mon Sep 17 00:00:00 2001 From: Brad Love Date: Wed, 19 Apr 2017 19:23:21 +0000 Subject: [PATCH 7/7] em28xx: Add Hauppauge SoloHD/DualHD bulk models support ATSC and DVB DualHD --- drivers/media/usb/em28xx/em28xx-cards.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 123f5868f722b..2d6ff27e1bf3f 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -507,8 +507,10 @@ static struct em28xx_reg_seq plex_px_bcud[] = { }; /* - * 2040:0265 Hauppauge WinTV-dualHD DVB - * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM + * 2040:0265 Hauppauge WinTV-dualHD DVB ISOC + * 2040:8265 Hauppauge WinTV-dualHD DVB Bulk + * 2040:026d Hauppauge WinTV-dualHD ATSC/QAM ISOC + * 2040:826d Hauppauge WinTV-dualHD ATSC/QAM Bulk * reg 0x80/0x84: * GPIO_0: Yellow LED tuner 1, 0=on, 1=off * GPIO_1: Green LED tuner 1, 0=on, 1=off @@ -2376,7 +2378,8 @@ struct em28xx_board em28xx_boards[] = { .has_dvb = 1, }, /* - * 2040:0265 Hauppauge WinTV-dualHD (DVB version). + * 2040:0265 Hauppauge WinTV-dualHD (DVB version) ISOC. + * 2040:8265 Hauppauge WinTV-dualHD (DVB version) BULK. * Empia EM28274, 2x Silicon Labs Si2168, 2x Silicon Labs Si2157 */ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB] = { @@ -2392,7 +2395,8 @@ struct em28xx_board em28xx_boards[] = { .leds = hauppauge_dualhd_leds, }, /* - * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM). + * 2040:026d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) ISOC. + * 2040:826d Hauppauge WinTV-dualHD (model 01595 - ATSC/QAM) BULK. * Empia EM28274, 2x LG LGDT3306A, 2x Silicon Labs Si2157 */ [EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595] = { @@ -2532,8 +2536,12 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 }, { USB_DEVICE(0x2040, 0x0265), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, + { USB_DEVICE(0x2040, 0x8265), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_DVB }, { USB_DEVICE(0x2040, 0x026d), .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, + { USB_DEVICE(0x2040, 0x826d), + .driver_info = EM28174_BOARD_HAUPPAUGE_WINTV_DUALHD_01595 }, { USB_DEVICE(0x0438, 0xb002), .driver_info = EM2880_BOARD_AMD_ATI_TV_WONDER_HD_600 }, { USB_DEVICE(0x2001, 0xf112),