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;
};
/* ------------------------------------------------------------------------
* 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,
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");
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;
error:

View File

@@ -1515,7 +1515,7 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
{
unsigned int i;
for (i = 0; i < UVC_URBS; ++i) {
for (i = 0; i < stream->max_urb; ++i) {
if (stream->urb_buffer[i]) {
#ifndef CONFIG_DMA_NONCOHERENT
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.
*/
npackets = DIV_ROUND_UP(size, psize);
if (npackets > UVC_MAX_PACKETS)
npackets = UVC_MAX_PACKETS;
if (npackets > stream->max_urb_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. */
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;
#ifndef CONFIG_DMA_NONCOHERENT
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) {
uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers "
"of %ux%u bytes each.\n", UVC_URBS, npackets,
psize);
if (i == stream->max_urb) {
uvc_trace(UVC_TRACE_VIDEO,
"Allocated %u URB buffers of %ux%u bytes each.\n",
stream->max_urb, npackets, psize);
return npackets;
}
}
@@ -1599,7 +1609,7 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
uvc_video_stats_stop(stream);
for (i = 0; i < UVC_URBS; ++i) {
for (i = 0; i < stream->max_urb; ++i) {
urb = stream->urb[i];
if (urb == NULL)
continue;
@@ -1611,6 +1621,8 @@ static void uvc_uninit_video(struct uvc_streaming *stream, int free_buffers)
if (free_buffers)
uvc_free_urb_buffers(stream);
stream->refcnt--;
}
/*
@@ -1660,7 +1672,7 @@ static int uvc_init_video_isoc(struct uvc_streaming *stream,
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);
if (urb == NULL) {
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)
size = 0;
for (i = 0; i < UVC_URBS; ++i) {
for (i = 0; i < stream->max_urb; ++i) {
urb = usb_alloc_urb(0, gfp_flags);
if (urb == NULL) {
uvc_uninit_video(stream, 1);
@@ -1831,7 +1843,8 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags)
return ret;
/* 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);
if (ret < 0) {
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_frame = frame;
stream->max_urb = UVC_URBS;
stream->max_urb_packets = UVC_MAX_PACKETS;
/* Select the video decoding function */
if (stream->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
if (stream->dev->quirks & UVC_QUIRK_BUILTIN_ISIGHT)

View File

@@ -535,9 +535,9 @@ struct uvc_streaming {
u32 max_payload_size;
} bulk;
struct urb *urb[UVC_URBS];
char *urb_buffer[UVC_URBS];
dma_addr_t urb_dma[UVC_URBS];
struct urb **urb;
char **urb_buffer;
dma_addr_t *urb_dma;
unsigned int urb_size;
u32 sequence;
@@ -570,6 +570,15 @@ struct uvc_streaming {
spinlock_t lock;
} 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 {