media:uvc: supports dynamic setting of urb configuration

Frames are corrupted for higher resolution with default URB queue
size and frame size. This patch creates a sysfs node to support
dynamic setting of number of packers per urb  and maximum packet
size for higher resolution frames.
$ find . -name urb_config.
$ echo qsize:fsize > urb_config.

Change-Id: I70aaad824049acdc21a8a52242066a6437b96833
Signed-off-by: Arjun Singh <quic_arsingh@quicinc.com>
This commit is contained in:
Arjun Singh
2022-09-09 13:02:37 +05:30
parent 367074bf05
commit 0441dc4864
3 changed files with 111 additions and 15 deletions

View File

@@ -2093,6 +2093,71 @@ struct uvc_device_info {
u32 meta_format; u32 meta_format;
}; };
/* ------------------------------------------------------------------------
* set urb queue size and urb packet size
*
*/
static ssize_t store_urb_config(struct device *dev,
struct device_attribute *attr, const char *buff, size_t count)
{
struct uvc_streaming *stream;
struct usb_interface *intf = to_usb_interface(dev);
struct uvc_device *udev = usb_get_intfdata(intf);
long max_urb, max_urb_packets;
int ret;
char *arr, *tmp;
arr = kstrdup(buff, GFP_KERNEL);
if (!arr)
return -ENOMEM;
tmp = strsep(&arr, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &max_urb);
if (ret < 0)
return ret;
tmp = strsep(&arr, ":");
if (!tmp)
return -EINVAL;
ret = kstrtol(tmp, 10, &max_urb_packets);
if (ret < 0)
return ret;
if (max_urb <= 0 || max_urb > 128 ||
max_urb_packets <= 0 || max_urb_packets > 128)
return -EINVAL;
list_for_each_entry(stream, &udev->streams, list) {
if (stream->refcnt)
continue;
stream->max_urb = max_urb;
stream->max_urb_packets = max_urb_packets;
}
return count;
}
static ssize_t show_urb_config(struct device *dev,
struct device_attribute *attr, char *buff)
{
return 0;
}
static struct device_attribute urb_config_attr = {
.attr = {
.name = "urb_config",
.mode = 00660,
},
.show = show_urb_config,
.store = store_urb_config,
};
static int uvc_probe(struct usb_interface *intf, static int uvc_probe(struct usb_interface *intf,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
@@ -2225,6 +2290,12 @@ static int uvc_probe(struct usb_interface *intf,
uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n"); uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
usb_enable_autosuspend(udev); usb_enable_autosuspend(udev);
/* sysfs file for dynamically setting urb configs */
ret = sysfs_create_file(&dev->intf->dev.kobj, &urb_config_attr.attr);
if (ret != 0)
pr_info("Unable to initialize urb configuration: %d\n", ret);
return 0; return 0;
error: error:

View File

@@ -1515,7 +1515,7 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
{ {
unsigned int i; unsigned int i;
for (i = 0; i < UVC_URBS; ++i) { for (i = 0; i < stream->max_urb; ++i) {
if (stream->urb_buffer[i]) { if (stream->urb_buffer[i]) {
#ifndef CONFIG_DMA_NONCOHERENT #ifndef CONFIG_DMA_NONCOHERENT
usb_free_coherent(stream->dev->udev, stream->urb_size, usb_free_coherent(stream->dev->udev, stream->urb_size,
@@ -1555,12 +1555,22 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
* payloads across multiple URBs. * payloads across multiple URBs.
*/ */
npackets = DIV_ROUND_UP(size, psize); npackets = DIV_ROUND_UP(size, psize);
if (npackets > UVC_MAX_PACKETS) if (npackets > stream->max_urb_packets)
npackets = UVC_MAX_PACKETS; npackets = stream->max_urb_packets;
/* Allocate memory for storing URB pointers */
stream->urb = kcalloc(stream->max_urb,
sizeof(struct urb *), gfp_flags | __GFP_NOWARN);
stream->urb_buffer = kcalloc(stream->max_urb,
sizeof(char *), gfp_flags | __GFP_NOWARN);
stream->urb_dma = kcalloc(stream->max_urb,
sizeof(dma_addr_t), gfp_flags | __GFP_NOWARN);
/* Retry allocations until one succeed. */ /* Retry allocations until one succeed. */
for (; npackets > 1; npackets /= 2) { for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) { for (i = 0; i < stream->max_urb; ++i) {
stream->urb_size = psize * npackets; stream->urb_size = psize * npackets;
#ifndef CONFIG_DMA_NONCOHERENT #ifndef CONFIG_DMA_NONCOHERENT
stream->urb_buffer[i] = usb_alloc_coherent( stream->urb_buffer[i] = usb_alloc_coherent(
@@ -1576,10 +1586,10 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,
} }
} }
if (i == UVC_URBS) { if (i == stream->max_urb) {
uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " uvc_trace(UVC_TRACE_VIDEO,
"of %ux%u bytes each.\n", UVC_URBS, npackets, "Allocated %u URB buffers of %ux%u bytes each.\n",
psize); stream->max_urb, npackets, psize);
return npackets; return npackets;
} }
} }
@@ -1599,7 +1609,7 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
uvc_video_stats_stop(stream); uvc_video_stats_stop(stream);
for (i = 0; i < UVC_URBS; ++i) { for (i = 0; i < stream->max_urb; ++i) {
urb = stream->urb[i]; urb = stream->urb[i];
if (urb == NULL) if (urb == NULL)
continue; continue;
@@ -1611,6 +1621,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
if (free_buffers) if (free_buffers)
uvc_free_urb_buffers(stream); uvc_free_urb_buffers(stream);
stream->refcnt--;
} }
/* /*
@@ -1660,7 +1672,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
size = npackets * psize; size = npackets * psize;
for (i = 0; i < UVC_URBS; ++i) { for (i = 0; i < stream->max_urb; ++i) {
urb = usb_alloc_urb(npackets, gfp_flags); urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) { if (urb == NULL) {
uvc_uninit_video(stream, 1); uvc_uninit_video(stream, 1);
@@ -1726,7 +1738,7 @@ static int uvc_init_video_bulk(struct uvc_streaming *stream,
if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) if (stream->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
size = 0; size = 0;
for (i = 0; i < UVC_URBS; ++i) { for (i = 0; i < stream->max_urb; ++i) {
urb = usb_alloc_urb(0, gfp_flags); urb = usb_alloc_urb(0, gfp_flags);
if (urb == NULL) { if (urb == NULL) {
uvc_uninit_video(stream, 1); uvc_uninit_video(stream, 1);
@@ -1831,7 +1843,8 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
return ret; return ret;
/* Submit the URBs. */ /* Submit the URBs. */
for (i = 0; i < UVC_URBS; ++i) { stream->refcnt++;
for (i = 0; i < stream->max_urb; ++i) {
ret = usb_submit_urb(stream->urb[i], gfp_flags); ret = usb_submit_urb(stream->urb[i], gfp_flags);
if (ret < 0) { if (ret < 0) {
uvc_printk(KERN_ERR, "Failed to submit URB %u " uvc_printk(KERN_ERR, "Failed to submit URB %u "
@@ -1992,6 +2005,9 @@ int uvc_video_init(struct uvc_streaming *stream)
stream->cur_format = format; stream->cur_format = format;
stream->cur_frame = frame; stream->cur_frame = frame;
stream->max_urb = UVC_URBS;
stream->max_urb_packets = UVC_MAX_PACKETS;
/* Select the video decoding function */ /* Select the video decoding function */
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT) if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)

View File

@@ -535,9 +535,9 @@ struct uvc_streaming {
u32 max_payload_size; u32 max_payload_size;
} bulk; } bulk;
struct urb *urb[UVC_URBS]; struct urb **urb;
char *urb_buffer[UVC_URBS]; char **urb_buffer;
dma_addr_t urb_dma[UVC_URBS]; dma_addr_t *urb_dma;
unsigned int urb_size; unsigned int urb_size;
u32 sequence; u32 sequence;
@@ -570,6 +570,15 @@ struct uvc_streaming {
spinlock_t lock; spinlock_t lock;
} clock; } clock;
/* Maximum number of URBs that can be submitted */
u32 max_urb;
/* Maximum number of packets per URB */
u32 max_urb_packets;
/*set if stream in progress */
u8 refcnt;
}; };
struct uvc_device { struct uvc_device {