diff mbox series

[v6] media: uvcvideo: Fix bandwidth error for Alcor camera

Message ID 20230116145105.2066-1-laurent.pinchart@ideasonboard.com
State New
Headers show
Series [v6] media: uvcvideo: Fix bandwidth error for Alcor camera | expand

Commit Message

Laurent Pinchart Jan. 16, 2023, 2:51 p.m. UTC
From: Ai Chao <aichao@kylinos.cn>

The Alcor Corp. Slave camera (1b17:6684 and 2017:0011) returns a wrong
dwMaxPayloadTransferSize value for compressed formats. Valid values are
typically up to 3072 bytes per interval (for high-speed, high-bandwidth
devices), and those faulty devices request 2752512 bytes per interval.
This is a firmware issue, but the manufacturer cannot provide a fixed
firmware.

Fix this by checking the dwMaxPayloadTransferSize field, and hardcoding
a value of 1024 if it exceeds 3072 for compressed formats transferred
over isochronous endpoints. While at it, document the other quirk that
handles a bandwidth issue for uncompressed formats.

Signed-off-by: Ai Chao <aichao@kylinos.cn>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes since v5:

- Print a warning message
---
 drivers/media/usb/uvc/uvc_video.c | 34 +++++++++++++++++++++++++++++++
 drivers/media/usb/uvc/uvcvideo.h  |  1 +
 2 files changed, 35 insertions(+)
diff mbox series

Patch

diff --git a/drivers/media/usb/uvc/uvc_video.c b/drivers/media/usb/uvc/uvc_video.c
index d4b023d4de7c..9634596f3dc7 100644
--- a/drivers/media/usb/uvc/uvc_video.c
+++ b/drivers/media/usb/uvc/uvc_video.c
@@ -200,6 +200,20 @@  static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
 	if ((ctrl->dwMaxPayloadTransferSize & 0xffff0000) == 0xffff0000)
 		ctrl->dwMaxPayloadTransferSize &= ~0xffff0000;
 
+	/*
+	 * Many devices report an incorrect dwMaxPayloadTransferSize value. The
+	 * most common issue is devices requesting the maximum possible USB
+	 * bandwidth (3072 bytes per interval for high-speed, high-bandwidth
+	 * isochronous endpoints) while they actually require less, preventing
+	 * multiple cameras from being used at the same time due to bandwidth
+	 * overallocation.
+	 *
+	 * For those devices, replace the dwMaxPayloadTransferSize value based
+	 * on an estimation calculated from the frame format and size. This is
+	 * only possible for uncompressed formats, as not enough information is
+	 * available to reliably estimate the bandwidth requirements for
+	 * compressed formats.
+	 */
 	if (!(format->flags & UVC_FMT_FLAG_COMPRESSED) &&
 	    stream->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
 	    stream->intf->num_altsetting > 1) {
@@ -236,6 +250,26 @@  static void uvc_fixup_video_ctrl(struct uvc_streaming *stream,
 
 		ctrl->dwMaxPayloadTransferSize = bandwidth;
 	}
+
+	/*
+	 * Another issue is with devices that report a transfer size that
+	 * greatly exceeds the maximum supported by any existing USB version
+	 * for isochronous transfers.  For instance, the "Slave camera" devices
+	 * from Alcor Corp. (2017:0011 and 1b17:66B8) request 2752512 bytes per
+	 * interval.
+	 *
+	 * For uncompressed formats, this can be addressed by the FIX_BANDWIDTH
+	 * quirk, but for compressed format we can't meaningfully estimate the
+	 * required bandwidth. Just hardcode it to 1024 bytes per interval,
+	 * which should be large enough for compressed formats.
+	 */
+	if ((format->flags & UVC_FMT_FLAG_COMPRESSED) &&
+	    ctrl->dwMaxPayloadTransferSize > 3072 &&
+	    stream->intf->num_altsetting > 1) {
+		uvc_warn_once(stream->dev, UVC_WARN_PAYLOAD_SIZE,
+			      "Device requested invalid bandwidth, lowering to 1024 bytes per interval\n");
+		ctrl->dwMaxPayloadTransferSize = 1024;
+	}
 }
 
 static size_t uvc_video_ctrl_size(struct uvc_streaming *stream)
diff --git a/drivers/media/usb/uvc/uvcvideo.h b/drivers/media/usb/uvc/uvcvideo.h
index 9a596c8d894a..72189249719e 100644
--- a/drivers/media/usb/uvc/uvcvideo.h
+++ b/drivers/media/usb/uvc/uvcvideo.h
@@ -630,6 +630,7 @@  struct uvc_driver {
 #define UVC_WARN_MINMAX		0
 #define UVC_WARN_PROBE_DEF	1
 #define UVC_WARN_XU_GET_RES	2
+#define UVC_WARN_PAYLOAD_SIZE	3
 
 extern unsigned int uvc_clock_param;
 extern unsigned int uvc_no_drop_param;