]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - drivers/media/video/gspca/xirlink_cit.c
[media] gspca_xirlink_cit: various usb bandwidth allocation improvements / fixes
[mv-sheeva.git] / drivers / media / video / gspca / xirlink_cit.c
index b13ecbaf3e68842586bfb17e51b7f1b585a88d50..daeda2cd9d09f8ea93b8fa07bc23f9320550e3e8 100644 (file)
@@ -4,7 +4,7 @@
  * Supports Xirlink C-It Video Camera, IBM PC Camera,
  * IBM NetCamera and Veo Stingray.
  *
- * Copyright (C) 2010 Hans de Goede <hdgoede@redhat.com>
+ * Copyright (C) 2010 Hans de Goede <hdegoede@redhat.com>
  *
  * This driver is based on earlier work of:
  *
@@ -31,7 +31,7 @@
 
 #include "gspca.h"
 
-MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
 MODULE_DESCRIPTION("Xirlink C-IT");
 MODULE_LICENSE("GPL");
 
@@ -798,7 +798,7 @@ static int cit_write_reg(struct gspca_dev *gspca_dev, u16 value, u16 index)
                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
                        value, index, NULL, 0, 1000);
        if (err < 0)
-               PDEBUG(D_ERR, "Failed to write a register (index 0x%04X,"
+               err("Failed to write a register (index 0x%04X,"
                        " value 0x%02X, error %d)", index, value, err);
 
        return 0;
@@ -814,8 +814,7 @@ static int cit_read_reg(struct gspca_dev *gspca_dev, u16 index)
                        USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
                        0x00, index, buf, 8, 1000);
        if (res < 0) {
-               PDEBUG(D_ERR,
-                       "Failed to read a register (index 0x%04X, error %d)",
+               err("Failed to read a register (index 0x%04X, error %d)",
                        index, res);
                return res;
        }
@@ -1586,16 +1585,16 @@ static int cit_get_packet_size(struct gspca_dev *gspca_dev)
        intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface);
        alt = usb_altnum_to_altsetting(intf, gspca_dev->alt);
        if (!alt) {
-               PDEBUG(D_ERR, "Couldn't get altsetting");
+               err("Couldn't get altsetting");
                return -EIO;
        }
 
        return le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
 }
 
-static int cit_start_model0(struct gspca_dev *gspca_dev)
+/* Calculate the clockdiv giving us max fps given the available bandwidth */
+static int cit_get_clock_div(struct gspca_dev *gspca_dev)
 {
-       const unsigned short compression = 0; /* 0=none, 7=best frame rate */
        int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
        int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
        int packet_size;
@@ -1610,6 +1609,23 @@ static int cit_start_model0(struct gspca_dev *gspca_dev)
                        fps[clock_div - 1] * 3 / 2)
                clock_div--;
 
+       PDEBUG(D_PROBE,
+              "PacketSize: %d, res: %dx%d -> using clockdiv: %d (%d fps)",
+              packet_size, gspca_dev->width, gspca_dev->height, clock_div,
+              fps[clock_div]);
+
+       return clock_div;
+}
+
+static int cit_start_model0(struct gspca_dev *gspca_dev)
+{
+       const unsigned short compression = 0; /* 0=none, 7=best frame rate */
+       int clock_div;
+
+       clock_div = cit_get_clock_div(gspca_dev);
+       if (clock_div < 0)
+               return clock_div;
+
        cit_write_reg(gspca_dev, 0x0000, 0x0100); /* turn on led */
        cit_write_reg(gspca_dev, 0x0003, 0x0438);
        cit_write_reg(gspca_dev, 0x001e, 0x042b);
@@ -1651,7 +1667,6 @@ static int cit_start_model0(struct gspca_dev *gspca_dev)
 
        cit_write_reg(gspca_dev, compression, 0x0109);
        cit_write_reg(gspca_dev, clock_div, 0x0111);
-       PDEBUG(D_PROBE, "Using clockdiv: %d", clock_div);
 
        return 0;
 }
@@ -1659,19 +1674,11 @@ static int cit_start_model0(struct gspca_dev *gspca_dev)
 static int cit_start_model1(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int clock_div = 7; /* 0=30 1=25 2=20 3=15 4=12 5=7.5 6=6 7=3fps ?? */
-       int fps[8] = { 30, 25, 20, 15, 12, 8, 6, 3 };
-       int i, packet_size;
+       int i, clock_div;
 
-       packet_size = cit_get_packet_size(gspca_dev);
-       if (packet_size < 0)
-               return packet_size;
-
-       while (clock_div > 3 &&
-                       1000 * packet_size >
-                       gspca_dev->width * gspca_dev->height *
-                       fps[clock_div - 1] * 3 / 2)
-               clock_div--;
+       clock_div = cit_get_clock_div(gspca_dev);
+       if (clock_div < 0)
+               return clock_div;
 
        cit_read_reg(gspca_dev, 0x0128);
        cit_read_reg(gspca_dev, 0x0100);
@@ -1861,7 +1868,6 @@ static int cit_start_model1(struct gspca_dev *gspca_dev)
 
        cit_write_reg(gspca_dev, 0x01, 0x0100); /* LED On  */
        cit_write_reg(gspca_dev, clock_div, 0x0111);
-       PDEBUG(D_PROBE, "Using clockdiv: %d", clock_div);
 
        return 0;
 }
@@ -2255,6 +2261,9 @@ static int cit_start_model3(struct gspca_dev *gspca_dev)
        cit_model3_Packet1(gspca_dev, 0x0097, 0x0096);  /* Blue sharpness */
        cit_model3_Packet1(gspca_dev, 0x0098, 0x000b);
 
+       /* FIXME we should probably use cit_get_clock_div() here (in
+          combination with isoc negotiation using the programmable isoc size)
+          like with the IBM netcam pro). */
        cit_write_reg(gspca_dev, clock_div, 0x0111); /* Clock Divider */
 
        switch (gspca_dev->width) {
@@ -2622,7 +2631,11 @@ static int cit_start_model4(struct gspca_dev *gspca_dev)
 static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
 {
        const unsigned short compression = 0; /* 0=none, 7=best frame rate */
-       int i, clock_div = 0;
+       int i, clock_div;
+
+       clock_div = cit_get_clock_div(gspca_dev);
+       if (clock_div < 0)
+               return clock_div;
 
        cit_write_reg(gspca_dev, 0x0003, 0x0133);
        cit_write_reg(gspca_dev, 0x0000, 0x0117);
@@ -2661,7 +2674,6 @@ static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
                cit_write_reg(gspca_dev, 0x008b, 0x011c);
                cit_write_reg(gspca_dev, 0x0008, 0x0118);
                cit_write_reg(gspca_dev, 0x0000, 0x0132);
-               clock_div = 3;
                break;
        case 320: /* 320x240 */
                cit_write_reg(gspca_dev, 0x0028, 0x010b);
@@ -2673,7 +2685,6 @@ static int cit_start_ibm_netcam_pro(struct gspca_dev *gspca_dev)
                cit_write_reg(gspca_dev, 0x003f, 0x011c);
                cit_write_reg(gspca_dev, 0x000c, 0x0118);
                cit_write_reg(gspca_dev, 0x0000, 0x0132);
-               clock_div = 5;
                break;
        }
 
@@ -2758,6 +2769,64 @@ static int sd_start(struct gspca_dev *gspca_dev)
        return 0;
 }
 
+static int sd_isoc_init(struct gspca_dev *gspca_dev)
+{
+       struct usb_host_interface *alt;
+       int max_packet_size;
+
+       switch (gspca_dev->width) {
+       case 160:
+               max_packet_size = 450;
+               break;
+       case 176:
+               max_packet_size = 600;
+               break;
+       default:
+               max_packet_size = 1022;
+               break;
+       }
+
+       /* Start isoc bandwidth "negotiation" at max isoc bandwidth */
+       alt = &gspca_dev->dev->config->intf_cache[0]->altsetting[1];
+       alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+       return 0;
+}
+
+static int sd_isoc_nego(struct gspca_dev *gspca_dev)
+{
+       int ret, packet_size, min_packet_size;
+       struct usb_host_interface *alt;
+
+       switch (gspca_dev->width) {
+       case 160:
+               min_packet_size = 200;
+               break;
+       case 176:
+               min_packet_size = 266;
+               break;
+       default:
+               min_packet_size = 400;
+               break;
+       }
+
+       alt = &gspca_dev->dev->config->intf_cache[0]->altsetting[1];
+       packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+       if (packet_size <= min_packet_size)
+               return -EIO;
+
+       packet_size -= 100;
+       if (packet_size < min_packet_size)
+               packet_size = min_packet_size;
+       alt->endpoint[0].desc.wMaxPacketSize = cpu_to_le16(packet_size);
+
+       ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+       if (ret < 0)
+               err("set alt 1 err %d", ret);
+
+       return ret;
+}
+
 static void sd_stopN(struct gspca_dev *gspca_dev)
 {
        cit_write_reg(gspca_dev, 0x0000, 0x010c);
@@ -3135,6 +3204,20 @@ static const struct sd_desc sd_desc = {
        .pkt_scan = sd_pkt_scan,
 };
 
+static const struct sd_desc sd_desc_isoc_nego = {
+       .name = MODULE_NAME,
+       .ctrls = sd_ctrls,
+       .nctrls = ARRAY_SIZE(sd_ctrls),
+       .config = sd_config,
+       .init = sd_init,
+       .start = sd_start,
+       .isoc_init = sd_isoc_init,
+       .isoc_nego = sd_isoc_nego,
+       .stopN = sd_stopN,
+       .stop0 = sd_stop0,
+       .pkt_scan = sd_pkt_scan,
+};
+
 /* -- module initialisation -- */
 static const __devinitdata struct usb_device_id device_table[] = {
        { USB_DEVICE_VER(0x0545, 0x8080, 0x0001, 0x0001), .driver_info = CIT_MODEL0 },
@@ -3152,6 +3235,8 @@ MODULE_DEVICE_TABLE(usb, device_table);
 static int sd_probe(struct usb_interface *intf,
                        const struct usb_device_id *id)
 {
+       const struct sd_desc *desc = &sd_desc;
+
        switch (id->driver_info) {
        case CIT_MODEL0:
        case CIT_MODEL1:
@@ -3159,16 +3244,21 @@ static int sd_probe(struct usb_interface *intf,
                        return -ENODEV;
                break;
        case CIT_MODEL2:
-       case CIT_MODEL3:
        case CIT_MODEL4:
-       case CIT_IBM_NETCAM_PRO:
                if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
                        return -ENODEV;
                break;
+       case CIT_MODEL3:
+               if (intf->cur_altsetting->desc.bInterfaceNumber != 0)
+                       return -ENODEV;
+               /* FIXME this likely applies to all model3 cams and probably
+                  to other models too. */
+               if (ibm_netcam_pro)
+                       desc = &sd_desc_isoc_nego;
+               break;
        }
 
-       return gspca_dev_probe2(intf, id, &sd_desc, sizeof(struct sd),
-                               THIS_MODULE);
+       return gspca_dev_probe2(intf, id, desc, sizeof(struct sd), THIS_MODULE);
 }
 
 static struct usb_driver sd_driver = {
@@ -3185,17 +3275,11 @@ static struct usb_driver sd_driver = {
 /* -- module insert / remove -- */
 static int __init sd_mod_init(void)
 {
-       int ret;
-       ret = usb_register(&sd_driver);
-       if (ret < 0)
-               return ret;
-       PDEBUG(D_PROBE, "registered");
-       return 0;
+       return usb_register(&sd_driver);
 }
 static void __exit sd_mod_exit(void)
 {
        usb_deregister(&sd_driver);
-       PDEBUG(D_PROBE, "deregistered");
 }
 
 module_init(sd_mod_init);