Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c0b33bdc authored by Hans de Goede's avatar Hans de Goede Committed by Mauro Carvalho Chehab
Browse files

[media] gspca-stv06xx: support bandwidth changing



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: default avatarLee Jones <lee.jones@canonical.com>
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent bc250684
Loading
Loading
Loading
Loading
+54 −1
Original line number Diff line number Diff line
@@ -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 @@ static int stv06xx_start(struct gspca_dev *gspca_dev)
	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
+10 −1
Original line number Diff line number Diff line
@@ -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}
};
+14 −4
Original line number Diff line number Diff line
@@ -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);
+3 −0
Original line number Diff line number Diff line
@@ -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,
+4 −0
Original line number Diff line number Diff line
@@ -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);

Loading