Message ID | 1515110659-20145-2-git-send-email-brad@nextdimension.cc (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Thu, Jan 4, 2018 at 7:04 PM, Brad Love <brad@nextdimension.cc> wrote: > Implement use of secondary TS port on em28xx. > Adds has_dual_ts field, allows secondary demod/tuner to be > added to a single em28xx device. > > Hauppauge DualHD models are configured to use this feature. > > Signed-off-by: Brad Love <brad@nextdimension.cc> :+1 Reviewed-by: Michael Ira Krufky <mkrufky@linuxtv.org> > --- > drivers/media/usb/em28xx/em28xx-cards.c | 126 +++++++++++++++++++++++++++++++- > drivers/media/usb/em28xx/em28xx-core.c | 42 +++++++++-- > drivers/media/usb/em28xx/em28xx-dvb.c | 33 +++++++-- > drivers/media/usb/em28xx/em28xx.h | 12 +++ > 4 files changed, 196 insertions(+), 17 deletions(-) > > diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c > index 34e16f6a..7f5d0b28 100644 > --- a/drivers/media/usb/em28xx/em28xx-cards.c > +++ b/drivers/media/usb/em28xx/em28xx-cards.c > @@ -2402,6 +2402,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, > }, > @@ -2417,6 +2418,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, > }, > @@ -3239,7 +3241,8 @@ static void em28xx_release_resources(struct em28xx *dev) > em28xx_i2c_unregister(dev, 1); > em28xx_i2c_unregister(dev, 0); > > - usb_put_dev(udev); > + if (dev->ts == PRIMARY_TS) > + usb_put_dev(udev); > > /* Mark device as unused */ > clear_bit(dev->devno, em28xx_devused); > @@ -3432,6 +3435,35 @@ 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 */ > + dev_warn(&dev->intf->dev, ": 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)) > > @@ -3551,6 +3583,17 @@ 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) { > + dev->dvb_ep_isoc_ts2 = e->bEndpointAddress; > + dev->dvb_max_pkt_size_isoc_ts2 = size; > + dev->dvb_alt_isoc = i; > + } > + } else { > + dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; > + } > + break; > } > } > /* NOTE: > @@ -3565,6 +3608,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, > * 0x83 isoc* => audio > * 0x84 isoc => digital > * 0x84 bulk => analog or digital** > + * 0x85 isoc => digital TS2 > + * 0x85 bulk => digital TS2 > * (*: audio should always be isoc) > * (**: analog, if ep 0x82 is isoc, otherwise digital) > * > @@ -3632,6 +3677,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) { > dev_err(&interface->dev, > "Audio interface %i found (Vendor Class)\n", ifnum); > @@ -3711,6 +3760,65 @@ static int em28xx_usb_probe(struct usb_interface *interface, > dev->dvb_xfer_bulk ? "bulk" : "isoc"); > } > > + 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; > + > + dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */ > + dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */ > + > + 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; > + dev_info(&dev->intf->dev, "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); > @@ -3753,15 +3861,29 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) > if (!dev) > return; > > + if (dev->dev_next != NULL) { > + dev->dev_next->disconnected = 1; > + dev_info(&dev->intf->dev, "Disconnecting %s\n", > + dev->dev_next->name); > + flush_request_modules(dev->dev_next); > + } > + > dev->disconnected = 1; > > - dev_err(&dev->intf->dev, "Disconnecting\n"); > + dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name); > > flush_request_modules(dev); > > em28xx_close_extension(dev); > > + 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); > } > > diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c > index 1d0d8cc..ef38e56 100644 > --- a/drivers/media/usb/em28xx/em28xx-core.c > +++ b/drivers/media/usb/em28xx/em28xx-core.c > @@ -638,10 +638,18 @@ 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->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 { > /* FIXME: which is the best order? */ > /* video registers are sampled by VREF */ > @@ -1077,7 +1085,11 @@ 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); > pr_info("em28xx: Registered (%s) extension\n", ops->name); > @@ -1091,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); > @@ -1106,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); > } > @@ -1118,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); > @@ -1134,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; > @@ -1148,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 8a81c94..9bc9576 100644 > --- a/drivers/media/usb/em28xx/em28xx-dvb.c > +++ b/drivers/media/usb/em28xx/em28xx-dvb.c > @@ -199,7 +199,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) > int rc; > struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; > struct em28xx *dev = i2c_bus->dev; > - struct usb_device *udev = interface_to_usbdev(dev->intf); > int dvb_max_packet_size, packet_multiplier, dvb_alt; > > if (dev->dvb_xfer_bulk) { > @@ -218,7 +217,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) > dvb_alt = dev->dvb_alt_isoc; > } > > - usb_set_interface(udev, dev->ifnum, dvb_alt); > rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); > if (rc < 0) > return rc; > @@ -1128,8 +1126,9 @@ 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; > + struct usb_device *udev; > > if (dev->is_audio_only) { > /* Shouldn't initialize IR for this interface */ > @@ -1914,7 +1913,10 @@ 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); > @@ -1940,7 +1942,10 @@ 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); > @@ -1976,7 +1981,10 @@ 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], > @@ -2003,7 +2011,10 @@ 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); > > @@ -2046,6 +2057,14 @@ static int em28xx_dvb_init(struct em28xx *dev) > if (result < 0) > goto out_free; > > + if (dev->dvb_xfer_bulk) { > + dvb_alt = 0; > + } else { /* isoc */ > + dvb_alt = dev->dvb_alt_isoc; > + } > + > + udev = interface_to_usbdev(dev->intf); > + usb_set_interface(udev, dev->ifnum, dvb_alt); > dev_info(&dev->intf->dev, "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 88084f2..c85292c 100644 > --- a/drivers/media/usb/em28xx/em28xx.h > +++ b/drivers/media/usb/em28xx/em28xx.h > @@ -217,6 +217,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, > @@ -457,6 +460,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; > @@ -621,6 +625,7 @@ struct em28xx { > unsigned int is_audio_only:1; > enum em28xx_int_audio_type int_audio_type; > enum em28xx_usb_audio_type usb_audio_type; > + unsigned char name[32]; > > struct em28xx_board board; > > @@ -682,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 */ > @@ -695,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 */ > @@ -726,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) > -- > 2.7.4 >
diff --git a/drivers/media/usb/em28xx/em28xx-cards.c b/drivers/media/usb/em28xx/em28xx-cards.c index 34e16f6a..7f5d0b28 100644 --- a/drivers/media/usb/em28xx/em28xx-cards.c +++ b/drivers/media/usb/em28xx/em28xx-cards.c @@ -2402,6 +2402,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, }, @@ -2417,6 +2418,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, }, @@ -3239,7 +3241,8 @@ static void em28xx_release_resources(struct em28xx *dev) em28xx_i2c_unregister(dev, 1); em28xx_i2c_unregister(dev, 0); - usb_put_dev(udev); + if (dev->ts == PRIMARY_TS) + usb_put_dev(udev); /* Mark device as unused */ clear_bit(dev->devno, em28xx_devused); @@ -3432,6 +3435,35 @@ 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 */ + dev_warn(&dev->intf->dev, ": 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)) @@ -3551,6 +3583,17 @@ 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) { + dev->dvb_ep_isoc_ts2 = e->bEndpointAddress; + dev->dvb_max_pkt_size_isoc_ts2 = size; + dev->dvb_alt_isoc = i; + } + } else { + dev->dvb_ep_bulk_ts2 = e->bEndpointAddress; + } + break; } } /* NOTE: @@ -3565,6 +3608,8 @@ static int em28xx_usb_probe(struct usb_interface *interface, * 0x83 isoc* => audio * 0x84 isoc => digital * 0x84 bulk => analog or digital** + * 0x85 isoc => digital TS2 + * 0x85 bulk => digital TS2 * (*: audio should always be isoc) * (**: analog, if ep 0x82 is isoc, otherwise digital) * @@ -3632,6 +3677,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) { dev_err(&interface->dev, "Audio interface %i found (Vendor Class)\n", ifnum); @@ -3711,6 +3760,65 @@ static int em28xx_usb_probe(struct usb_interface *interface, dev->dvb_xfer_bulk ? "bulk" : "isoc"); } + 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; + + dev->dev_next->board.ir_codes = NULL; /* No IR for 2nd tuner */ + dev->dev_next->board.has_ir_i2c = 0; /* No IR for 2nd tuner */ + + 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; + dev_info(&dev->intf->dev, "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); @@ -3753,15 +3861,29 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) if (!dev) return; + if (dev->dev_next != NULL) { + dev->dev_next->disconnected = 1; + dev_info(&dev->intf->dev, "Disconnecting %s\n", + dev->dev_next->name); + flush_request_modules(dev->dev_next); + } + dev->disconnected = 1; - dev_err(&dev->intf->dev, "Disconnecting\n"); + dev_err(&dev->intf->dev, "Disconnecting %s\n", dev->name); flush_request_modules(dev); em28xx_close_extension(dev); + 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); } diff --git a/drivers/media/usb/em28xx/em28xx-core.c b/drivers/media/usb/em28xx/em28xx-core.c index 1d0d8cc..ef38e56 100644 --- a/drivers/media/usb/em28xx/em28xx-core.c +++ b/drivers/media/usb/em28xx/em28xx-core.c @@ -638,10 +638,18 @@ 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->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 { /* FIXME: which is the best order? */ /* video registers are sampled by VREF */ @@ -1077,7 +1085,11 @@ 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); pr_info("em28xx: Registered (%s) extension\n", ops->name); @@ -1091,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); @@ -1106,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); } @@ -1118,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); @@ -1134,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; @@ -1148,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 8a81c94..9bc9576 100644 --- a/drivers/media/usb/em28xx/em28xx-dvb.c +++ b/drivers/media/usb/em28xx/em28xx-dvb.c @@ -199,7 +199,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) int rc; struct em28xx_i2c_bus *i2c_bus = dvb->adapter.priv; struct em28xx *dev = i2c_bus->dev; - struct usb_device *udev = interface_to_usbdev(dev->intf); int dvb_max_packet_size, packet_multiplier, dvb_alt; if (dev->dvb_xfer_bulk) { @@ -218,7 +217,6 @@ static int em28xx_start_streaming(struct em28xx_dvb *dvb) dvb_alt = dev->dvb_alt_isoc; } - usb_set_interface(udev, dev->ifnum, dvb_alt); rc = em28xx_set_mode(dev, EM28XX_DIGITAL_MODE); if (rc < 0) return rc; @@ -1128,8 +1126,9 @@ 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; + struct usb_device *udev; if (dev->is_audio_only) { /* Shouldn't initialize IR for this interface */ @@ -1914,7 +1913,10 @@ 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); @@ -1940,7 +1942,10 @@ 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); @@ -1976,7 +1981,10 @@ 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], @@ -2003,7 +2011,10 @@ 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); @@ -2046,6 +2057,14 @@ static int em28xx_dvb_init(struct em28xx *dev) if (result < 0) goto out_free; + if (dev->dvb_xfer_bulk) { + dvb_alt = 0; + } else { /* isoc */ + dvb_alt = dev->dvb_alt_isoc; + } + + udev = interface_to_usbdev(dev->intf); + usb_set_interface(udev, dev->ifnum, dvb_alt); dev_info(&dev->intf->dev, "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 88084f2..c85292c 100644 --- a/drivers/media/usb/em28xx/em28xx.h +++ b/drivers/media/usb/em28xx/em28xx.h @@ -217,6 +217,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, @@ -457,6 +460,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; @@ -621,6 +625,7 @@ struct em28xx { unsigned int is_audio_only:1; enum em28xx_int_audio_type int_audio_type; enum em28xx_usb_audio_type usb_audio_type; + unsigned char name[32]; struct em28xx_board board; @@ -682,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 */ @@ -695,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 */ @@ -726,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)
Implement use of secondary TS port on em28xx. Adds has_dual_ts field, allows secondary demod/tuner to be added to a single em28xx device. Hauppauge DualHD models are configured to use this feature. Signed-off-by: Brad Love <brad@nextdimension.cc> --- drivers/media/usb/em28xx/em28xx-cards.c | 126 +++++++++++++++++++++++++++++++- drivers/media/usb/em28xx/em28xx-core.c | 42 +++++++++-- drivers/media/usb/em28xx/em28xx-dvb.c | 33 +++++++-- drivers/media/usb/em28xx/em28xx.h | 12 +++ 4 files changed, 196 insertions(+), 17 deletions(-)