[media] gspca-stv06xx: support bandwidth changing
authorHans de Goede <hdegoede@redhat.com>
Wed, 27 Oct 2010 12:12:30 +0000 (09:12 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Wed, 29 Dec 2010 10:16:40 +0000 (08:16 -0200)
stv06xx devices have only one altsetting, but the actual used
bandwidth can be programmed through a register. We were already
setting this register lower then the max packetsize of the altsetting
indicates. This patch makes the gspca-stv06xx update the usb descriptor
for the alt setting to reflect the actual packetsize in use, so that
the usb subsystem uses the correct information for scheduling usb transfers.

This patch also tries to fallback to lower speeds in case a ENOSPC error
is received when submitting urbs, but currently this is only supported
with stv06xx cams with the pb0100 sensor, as this is the only one for
which we know how to change the framerate.

This patch is based on an initial incomplete patch by
Lee Jones <lee.jones@canonical.com>

Signed-off-by: Lee Jones <lee.jones@canonical.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/gspca/stv06xx/stv06xx.c
drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
drivers/media/video/gspca/stv06xx/stv06xx_st6422.c
drivers/media/video/gspca/stv06xx/stv06xx_st6422.h
drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h

index 544d5f72a1e39188ad98aeea9dd3d0b43157b6e3..28ea4175b80e55f4850ef1eb571ea347a39d685c 100644 (file)
@@ -263,7 +263,21 @@ static int stv06xx_init(struct gspca_dev *gspca_dev)
 static int stv06xx_start(struct gspca_dev *gspca_dev)
 {
        struct sd *sd = (struct sd *) gspca_dev;
-       int err;
+       struct usb_host_interface *alt;
+       struct usb_interface *intf;
+       int err, packet_size;
+
+       intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+       alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+       if (!alt) {
+               PDEBUG(D_ERR, "Couldn't get altsetting");
+               return -EIO;
+       }
+
+       packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+       err = stv06xx_write_bridge(sd, STV_ISO_SIZE_L, packet_size);
+       if (err < 0)
+               return err;
 
        /* Prepare the sensor for start */
        err = sd->sensor->start(sd);
@@ -282,6 +296,43 @@ out:
        return (err < 0) ? err : 0;
 }
 
+static int stv06xx_isoc_init(struct gspca_dev *gspca_dev)
+{
+       struct usb_host_interface *alt;
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       /* 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(sd->sensor->max_packet_size[gspca_dev->curr_mode]);
+
+       return 0;
+}
+
+static int stv06xx_isoc_nego(struct gspca_dev *gspca_dev)
+{
+       int ret, packet_size, min_packet_size;
+       struct usb_host_interface *alt;
+       struct sd *sd = (struct sd *) gspca_dev;
+
+       alt = &gspca_dev->dev->config->intf_cache[0]->altsetting[1];
+       packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+       min_packet_size = sd->sensor->min_packet_size[gspca_dev->curr_mode];
+       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)
+               PDEBUG(D_ERR|D_STREAM, "set alt 1 err %d", ret);
+
+       return ret;
+}
+
 static void stv06xx_stopN(struct gspca_dev *gspca_dev)
 {
        int err;
@@ -462,6 +513,8 @@ static const struct sd_desc sd_desc = {
        .start = stv06xx_start,
        .stopN = stv06xx_stopN,
        .pkt_scan = stv06xx_pkt_scan,
+       .isoc_init = stv06xx_isoc_init,
+       .isoc_nego = stv06xx_isoc_nego,
 #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
        .int_pkt_scan = sd_int_pkt_scan,
 #endif
index cf3d0ccc1121f008b46616f7e3c5a6a9dec569f3..b538dce96f78d8cbb1c7297ea8b40ce21712ea7b 100644 (file)
@@ -146,6 +146,11 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
        .i2c_addr = (0x55 << 1),
        .i2c_len = 1,
 
+       /* FIXME (see if we can lower min_packet_size, needs testing, and also
+          adjusting framerate when the bandwidth gets lower) */
+       .min_packet_size = { 847 },
+       .max_packet_size = { 847 },
+
        .init = hdcs_init,
        .probe = hdcs_probe_1x00,
        .start = hdcs_start,
@@ -160,6 +165,11 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
        .i2c_addr = (0x55 << 1),
        .i2c_len = 1,
 
+       /* FIXME (see if we can lower min_packet_size, needs testing, and also
+          adjusting framerate when the bandwidthm gets lower) */
+       .min_packet_size = { 847 },
+       .max_packet_size = { 847 },
+
        .init = hdcs_init,
        .probe = hdcs_probe_1020,
        .start = hdcs_start,
@@ -177,7 +187,6 @@ static const u16 stv_bridge_init[][2] = {
        {STV_REG04, 0x07},
 
        {STV_SCAN_RATE, 0x20},
-       {STV_ISO_SIZE_L, 847},
        {STV_Y_CTRL, 0x01},
        {STV_X_CTRL, 0x0a}
 };
index 285221e6b3908995e157fe3839b2d0f8f1621077..ac47b4c9438874af6c7038d0d8516f17347b1b58 100644 (file)
@@ -208,11 +208,24 @@ static int pb0100_probe(struct sd *sd)
 
 static int pb0100_start(struct sd *sd)
 {
-       int err;
+       int err, packet_size, max_packet_size;
+       struct usb_host_interface *alt;
+       struct usb_interface *intf;
        struct cam *cam = &sd->gspca_dev.cam;
        s32 *sensor_settings = sd->sensor_priv;
        u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv;
 
+       intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
+       alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
+       packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
+
+       /* If we don't have enough bandwidth use a lower framerate */
+       max_packet_size = sd->sensor->max_packet_size[sd->gspca_dev.curr_mode];
+       if (packet_size < max_packet_size)
+               stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
+       else
+               stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(5)|BIT(3)|BIT(1));
+
        /* Setup sensor window */
        if (mode & PB0100_CROP_TO_VGA) {
                stv06xx_write_sensor(sd, PB_RSTART, 30);
@@ -328,9 +341,6 @@ static int pb0100_init(struct sd *sd)
        stv06xx_write_bridge(sd, STV_REG03, 0x45);
        stv06xx_write_bridge(sd, STV_REG04, 0x07);
 
-       /* ISO-Size (0x27b: 635... why? - HDCS uses 847) */
-       stv06xx_write_bridge(sd, STV_ISO_SIZE_L, 847);
-
        /* Scan/timing for the sensor */
        stv06xx_write_sensor(sd, PB_ROWSPEED, BIT(4)|BIT(3)|BIT(1));
        stv06xx_write_sensor(sd, PB_CFILLIN, 14);
index 4de4fa5ebc5766f46044a499b1b387c95941cffa..757de246dc759f2e7b561bd16d6b7673bf9c1300 100644 (file)
@@ -138,6 +138,9 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
        .i2c_addr = 0xba,
        .i2c_len = 2,
 
+       .min_packet_size = { 635, 847 },
+       .max_packet_size = { 847, 923 },
+
        .init = pb0100_init,
        .probe = pb0100_probe,
        .start = pb0100_start,
index 934b9cebc1abc5f045934697734940f798cb3729..fb229d8ded58e1f112bb016df5c936ad966c9aca 100644 (file)
@@ -53,6 +53,10 @@ struct stv06xx_sensor {
        /* length of an i2c word */
        u8 i2c_len;
 
+       /* Isoc packet size (per mode) */
+       int min_packet_size[4];
+       int max_packet_size[4];
+
        /* Probes if the sensor is connected */
        int (*probe)(struct sd *sd);
 
index ea677b5d071e26bd5c50a2793b099d9be759d4a3..42f3a1e41188c65f6336b11d71d8007fc2acb19f 100644 (file)
@@ -213,7 +213,6 @@ static int st6422_init(struct sd *sd)
                { 0x150e, 0x8e },
                { 0x150f, 0x37 },
                { 0x15c0, 0x00 },
-               { 0x15c1, 1023 }, /* 160x120, ISOC_PACKET_SIZE */
                { 0x15c3, 0x08 },       /* 0x04/0x14 ... test pictures ??? */
 
 
@@ -237,23 +236,9 @@ static void st6422_disconnect(struct sd *sd)
 
 static int st6422_start(struct sd *sd)
 {
-       int err, packet_size;
+       int err;
        struct cam *cam = &sd->gspca_dev.cam;
        s32 *sensor_settings = sd->sensor_priv;
-       struct usb_host_interface *alt;
-       struct usb_interface *intf;
-
-       intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface);
-       alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt);
-       if (!alt) {
-               err("Couldn't get altsetting");
-               return -EIO;
-       }
-
-       packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize);
-       err = stv06xx_write_bridge(sd, 0x15c1, packet_size);
-       if (err < 0)
-               return err;
 
        if (cam->cam_mode[sd->gspca_dev.curr_mode].priv)
                err = stv06xx_write_bridge(sd, 0x1505, 0x0f);
index b2d45fe50522025dd1390c5e087ea13038378f10..12608aebe50b45cb57c4052a65bed944262714e7 100644 (file)
@@ -49,6 +49,9 @@ static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val);
 
 const struct stv06xx_sensor stv06xx_sensor_st6422 = {
        .name = "ST6422",
+       /* No known way to lower framerate in case of less bandwidth */
+       .min_packet_size = { 300, 847 },
+       .max_packet_size = { 300, 847 },
        .init = st6422_init,
        .probe = st6422_probe,
        .start = st6422_start,
index b3b5508473bcca22d971328737d6ea5d38583e9f..7fe3587f5f71fac1bc0aef41e8478261f7e278cc 100644 (file)
@@ -197,6 +197,10 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
        .i2c_flush = 5,
        .i2c_addr = 0x20,
        .i2c_len = 1,
+       /* FIXME (see if we can lower packet_size-s, needs testing, and also
+          adjusting framerate when the bandwidth gets lower) */
+       .min_packet_size = { 1023 },
+       .max_packet_size = { 1023 },
        .init = vv6410_init,
        .probe = vv6410_probe,
        .start = vv6410_start,
@@ -220,10 +224,6 @@ static const u8 x1536[] = {        /* 0x1536 - 0x153b */
        0x02, 0x00, 0x60, 0x01, 0x20, 0x01
 };
 
-static const u8 x15c1[] = {    /* 0x15c1 - 0x15c2 */
-       0xff, 0x03 /* Output word 0x03ff = 1023 (ISO size) */
-};
-
 static const struct stv_init stv_bridge_init[] = {
        /* This reg is written twice. Some kind of reset? */
        {NULL,  0x1620, 0x80},
@@ -232,7 +232,6 @@ static const struct stv_init stv_bridge_init[] = {
        {NULL,  0x1423, 0x04},
        {x1500, 0x1500, ARRAY_SIZE(x1500)},
        {x1536, 0x1536, ARRAY_SIZE(x1536)},
-       {x15c1, 0x15c1, ARRAY_SIZE(x15c1)}
 };
 
 static const u8 vv6410_sensor_init[][2] = {