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:
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user