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