Merge tag 'v3.2' into staging/for_v3.3
[firefly-linux-kernel-4.4.55.git] / drivers / media / video / gspca / gspca.c
index 2ca10dfec91fd2347532d35e22ea44fd4ee01298..ca5a2b139d0b739f8d5aa149785d95c60d674a4a 100644 (file)
@@ -633,23 +633,32 @@ static u32 which_bandwidth(struct gspca_dev *gspca_dev)
        u32 bandwidth;
        int i;
 
+       /* get the (max) image size */
        i = gspca_dev->curr_mode;
        bandwidth = gspca_dev->cam.cam_mode[i].sizeimage;
 
-       /* if the image is compressed, estimate the mean image size */
-       if (bandwidth < gspca_dev->cam.cam_mode[i].width *
+       /* if the image is compressed, estimate its mean size */
+       if (!gspca_dev->cam.needs_full_bandwidth &&
+           bandwidth < gspca_dev->cam.cam_mode[i].width *
                                gspca_dev->cam.cam_mode[i].height)
-               bandwidth /= 3;
+               bandwidth = bandwidth * 3 / 8;  /* 0.375 */
 
        /* estimate the frame rate */
        if (gspca_dev->sd_desc->get_streamparm) {
                struct v4l2_streamparm parm;
 
-               parm.parm.capture.timeperframe.denominator = 15;
                gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm);
                bandwidth *= parm.parm.capture.timeperframe.denominator;
+               bandwidth /= parm.parm.capture.timeperframe.numerator;
        } else {
-               bandwidth *= 15;                /* 15 fps */
+
+               /* don't hope more than 15 fps with USB 1.1 and
+                * image resolution >= 640x480 */
+               if (gspca_dev->width >= 640
+                && gspca_dev->dev->speed == USB_SPEED_FULL)
+                       bandwidth *= 15;                /* 15 fps */
+               else
+                       bandwidth *= 30;                /* 30 fps */
        }
 
        PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth);
@@ -667,9 +676,8 @@ struct ep_tb_s {
  * build the table of the endpoints
  * and compute the minimum bandwidth for the image transfer
  */
-static int build_ep_tb(struct gspca_dev *gspca_dev,
+static int build_isoc_ep_tb(struct gspca_dev *gspca_dev,
                        struct usb_interface *intf,
-                       int xfer,
                        struct ep_tb_s *ep_tb)
 {
        struct usb_host_endpoint *ep;
@@ -687,17 +695,21 @@ static int build_ep_tb(struct gspca_dev *gspca_dev,
                ep_tb->bandwidth = 2000 * 2000 * 120;
                found = 0;
                for (j = 0; j < nbalt; j++) {
-                       ep = alt_xfer(&intf->altsetting[j], xfer);
+                       ep = alt_xfer(&intf->altsetting[j],
+                                     USB_ENDPOINT_XFER_ISOC);
                        if (ep == NULL)
                                continue;
+                       if (ep->desc.bInterval == 0) {
+                               pr_err("alt %d iso endp with 0 interval\n", j);
+                               continue;
+                       }
                        psize = le16_to_cpu(ep->desc.wMaxPacketSize);
-                       if (!gspca_dev->cam.bulk)               /* isoc */
-                               psize = (psize & 0x07ff) *
-                                               (1 + ((psize >> 11) & 3));
-                       bandwidth = psize * ep->desc.bInterval * 1000;
+                       psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+                       bandwidth = psize * 1000;
                        if (gspca_dev->dev->speed == USB_SPEED_HIGH
                         || gspca_dev->dev->speed == USB_SPEED_SUPER)
                                bandwidth *= 8;
+                       bandwidth /= 1 << (ep->desc.bInterval - 1);
                        if (bandwidth <= last_bw)
                                continue;
                        if (bandwidth < ep_tb->bandwidth) {
@@ -715,6 +727,23 @@ static int build_ep_tb(struct gspca_dev *gspca_dev,
                ep_tb++;
        }
 
+       /*
+        * If the camera:
+        * has a usb audio class interface (a built in usb mic); and
+        * is a usb 1 full speed device; and
+        * uses the max full speed iso bandwidth; and
+        * and has more than 1 alt setting
+        * then skip the highest alt setting to spare bandwidth for the mic
+        */
+       if (gspca_dev->audio &&
+                       gspca_dev->dev->speed == USB_SPEED_FULL &&
+                       last_bw >= 1000000 &&
+                       i > 1) {
+               PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt");
+               i--;
+               ep_tb--;
+       }
+
        /* get the requested bandwidth and start at the highest atlsetting */
        bandwidth = which_bandwidth(gspca_dev);
        ep_tb--;
@@ -790,10 +819,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
                                                    ep->desc.bEndpointAddress);
                        urb->transfer_flags = URB_ISO_ASAP
                                        | URB_NO_TRANSFER_DMA_MAP;
-                       if (gspca_dev->dev->speed == USB_SPEED_LOW)
-                               urb->interval = ep->desc.bInterval;
-                       else
-                               urb->interval = 1 << (ep->desc.bInterval - 1);
+                       urb->interval = 1 << (ep->desc.bInterval - 1);
                        urb->complete = isoc_irq;
                        urb->number_of_packets = npkt;
                        for (i = 0; i < npkt; i++) {
@@ -848,7 +874,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
        xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
                                   : USB_ENDPOINT_XFER_ISOC;
 
-       /* if the subdriver forced an altsetting, get the endpoint */
+       /* if bulk or the subdriver forced an altsetting, get the endpoint */
        if (gspca_dev->alt != 0) {
                gspca_dev->alt--;       /* (previous version compatibility) */
                ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer);
@@ -863,7 +889,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
 
        /* else, compute the minimum bandwidth
         * and build the endpoint table */
-               alt_idx = build_ep_tb(gspca_dev, intf, xfer, ep_tb);
+               alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb);
                if (alt_idx <= 0) {
                        pr_err("no transfer endpoint found\n");
                        ret = -EIO;
@@ -880,7 +906,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
        for (;;) {
                if (alt != gspca_dev->alt) {
                        alt = gspca_dev->alt;
-                       if (gspca_dev->nbalt > 1) {
+                       if (intf->num_altsetting > 1) {
                                ret = usb_set_interface(gspca_dev->dev,
                                                        gspca_dev->iface,
                                                        alt);
@@ -2300,15 +2326,14 @@ int gspca_dev_probe2(struct usb_interface *intf,
        }
        gspca_dev->dev = dev;
        gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber;
-       gspca_dev->nbalt = intf->num_altsetting;
 
        /* check if any audio device */
-       if (dev->config->desc.bNumInterfaces != 1) {
+       if (dev->actconfig->desc.bNumInterfaces != 1) {
                int i;
                struct usb_interface *intf2;
 
-               for (i = 0; i < dev->config->desc.bNumInterfaces; i++) {
-                       intf2 = dev->config->interface[i];
+               for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) {
+                       intf2 = dev->actconfig->interface[i];
                        if (intf2 != NULL
                         && intf2->altsetting != NULL
                         && intf2->altsetting->desc.bInterfaceClass ==
@@ -2389,7 +2414,7 @@ int gspca_dev_probe(struct usb_interface *intf,
        }
 
        /* the USB video interface must be the first one */
-       if (dev->config->desc.bNumInterfaces != 1
+       if (dev->actconfig->desc.bNumInterfaces != 1
         && intf->cur_altsetting->desc.bInterfaceNumber != 0)
                return -ENODEV;