diff options
Diffstat (limited to 'drivers/gpu/drm/udl/udl_main.c')
| -rw-r--r-- | drivers/gpu/drm/udl/udl_main.c | 128 | 
1 files changed, 62 insertions, 66 deletions
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 853f147036f6..061cb88c08a2 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -20,11 +20,10 @@  #define NR_USB_REQUEST_CHANNEL 0x12  #define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE) -#define WRITES_IN_FLIGHT (4) +#define WRITES_IN_FLIGHT (20)  #define MAX_VENDOR_DESCRIPTOR_SIZE 256 -#define GET_URB_TIMEOUT	HZ -#define FREE_URB_TIMEOUT (HZ*2) +static struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout);  static int udl_parse_vendor_descriptor(struct udl_device *udl)  { @@ -95,7 +94,7 @@ success:  /*   * Need to ensure a channel is selected before submitting URBs   */ -static int udl_select_std_channel(struct udl_device *udl) +int udl_select_std_channel(struct udl_device *udl)  {  	static const u8 set_def_chn[] = {0x57, 0xCD, 0xDC, 0xA7,  					 0x1C, 0x88, 0x5E, 0x15, @@ -119,14 +118,6 @@ static int udl_select_std_channel(struct udl_device *udl)  	return ret < 0 ? ret : 0;  } -static void udl_release_urb_work(struct work_struct *work) -{ -	struct urb_node *unode = container_of(work, struct urb_node, -					      release_urb_work.work); - -	up(&unode->dev->urbs.limit_sem); -} -  void udl_urb_completion(struct urb *urb)  {  	struct urb_node *unode = urb->context; @@ -137,6 +128,7 @@ void udl_urb_completion(struct urb *urb)  	if (urb->status) {  		if (!(urb->status == -ENOENT ||  		    urb->status == -ECONNRESET || +		    urb->status == -EPROTO ||  		    urb->status == -ESHUTDOWN)) {  			DRM_ERROR("%s - nonzero write bulk status received: %d\n",  				__func__, urb->status); @@ -150,49 +142,34 @@ void udl_urb_completion(struct urb *urb)  	udl->urbs.available++;  	spin_unlock_irqrestore(&udl->urbs.lock, flags); -#if 0 -	/* -	 * When using fb_defio, we deadlock if up() is called -	 * while another is waiting. So queue to another process. -	 */ -	if (fb_defio) -		schedule_delayed_work(&unode->release_urb_work, 0); -	else -#endif -		up(&udl->urbs.limit_sem); +	wake_up(&udl->urbs.sleep);  }  static void udl_free_urb_list(struct drm_device *dev)  {  	struct udl_device *udl = to_udl(dev); -	int count = udl->urbs.count; -	struct list_head *node;  	struct urb_node *unode;  	struct urb *urb;  	DRM_DEBUG("Waiting for completes and freeing all render urbs\n");  	/* keep waiting and freeing, until we've got 'em all */ -	while (count--) { -		down(&udl->urbs.limit_sem); - +	while (udl->urbs.count) {  		spin_lock_irq(&udl->urbs.lock); - -		node = udl->urbs.list.next; /* have reserved one with sem */ -		list_del_init(node); - +		urb = udl_get_urb_locked(udl, MAX_SCHEDULE_TIMEOUT); +		udl->urbs.count--;  		spin_unlock_irq(&udl->urbs.lock); - -		unode = list_entry(node, struct urb_node, entry); -		urb = unode->urb; - +		if (WARN_ON(!urb)) +			break; +		unode = urb->context;  		/* Free each separately allocated piece */  		usb_free_coherent(urb->dev, udl->urbs.size,  				  urb->transfer_buffer, urb->transfer_dma);  		usb_free_urb(urb); -		kfree(node); +		kfree(unode);  	} -	udl->urbs.count = 0; + +	wake_up_all(&udl->urbs.sleep);  }  static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size) @@ -205,24 +182,20 @@ static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size)  	struct usb_device *udev = udl_to_usb_device(udl);  	spin_lock_init(&udl->urbs.lock); - -retry: -	udl->urbs.size = size;  	INIT_LIST_HEAD(&udl->urbs.list); - -	sema_init(&udl->urbs.limit_sem, 0); +	init_waitqueue_head(&udl->urbs.sleep);  	udl->urbs.count = 0;  	udl->urbs.available = 0; +retry: +	udl->urbs.size = size; +  	while (udl->urbs.count * size < wanted_size) {  		unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL);  		if (!unode)  			break;  		unode->dev = udl; -		INIT_DELAYED_WORK(&unode->release_urb_work, -			  udl_release_urb_work); -  		urb = usb_alloc_urb(0, GFP_KERNEL);  		if (!urb) {  			kfree(unode); @@ -250,7 +223,6 @@ retry:  		list_add_tail(&unode->entry, &udl->urbs.list); -		up(&udl->urbs.limit_sem);  		udl->urbs.count++;  		udl->urbs.available++;  	} @@ -260,35 +232,41 @@ retry:  	return udl->urbs.count;  } -struct urb *udl_get_urb(struct drm_device *dev) +static struct urb *udl_get_urb_locked(struct udl_device *udl, long timeout)  { -	struct udl_device *udl = to_udl(dev); -	int ret = 0; -	struct list_head *entry;  	struct urb_node *unode; -	struct urb *urb = NULL; + +	assert_spin_locked(&udl->urbs.lock);  	/* Wait for an in-flight buffer to complete and get re-queued */ -	ret = down_timeout(&udl->urbs.limit_sem, GET_URB_TIMEOUT); -	if (ret) { -		DRM_INFO("wait for urb interrupted: %x available: %d\n", -		       ret, udl->urbs.available); -		goto error; +	if (!wait_event_lock_irq_timeout(udl->urbs.sleep, +					 !udl->urbs.count || +					 !list_empty(&udl->urbs.list), +					 udl->urbs.lock, timeout)) { +		DRM_INFO("wait for urb interrupted: available: %d\n", +			 udl->urbs.available); +		return NULL;  	} -	spin_lock_irq(&udl->urbs.lock); +	if (!udl->urbs.count) +		return NULL; -	BUG_ON(list_empty(&udl->urbs.list)); /* reserved one with limit_sem */ -	entry = udl->urbs.list.next; -	list_del_init(entry); +	unode = list_first_entry(&udl->urbs.list, struct urb_node, entry); +	list_del_init(&unode->entry);  	udl->urbs.available--; -	spin_unlock_irq(&udl->urbs.lock); +	return unode ? unode->urb : NULL; +} -	unode = list_entry(entry, struct urb_node, entry); -	urb = unode->urb; +#define GET_URB_TIMEOUT	HZ +struct urb *udl_get_urb(struct drm_device *dev) +{ +	struct udl_device *udl = to_udl(dev); +	struct urb *urb; -error: +	spin_lock_irq(&udl->urbs.lock); +	urb = udl_get_urb_locked(udl, GET_URB_TIMEOUT); +	spin_unlock_irq(&udl->urbs.lock);  	return urb;  } @@ -297,10 +275,13 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len)  	struct udl_device *udl = to_udl(dev);  	int ret; -	BUG_ON(len > udl->urbs.size); - +	if (WARN_ON(len > udl->urbs.size)) { +		ret = -EINVAL; +		goto error; +	}  	urb->transfer_buffer_length = len; /* set to actual payload len */  	ret = usb_submit_urb(urb, GFP_ATOMIC); + error:  	if (ret) {  		udl_urb_completion(urb); /* because no one else will */  		DRM_ERROR("usb_submit_urb error %x\n", ret); @@ -308,6 +289,21 @@ int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len)  	return ret;  } +/* wait until all pending URBs have been processed */ +void udl_sync_pending_urbs(struct drm_device *dev) +{ +	struct udl_device *udl = to_udl(dev); + +	spin_lock_irq(&udl->urbs.lock); +	/* 2 seconds as a sane timeout */ +	if (!wait_event_lock_irq_timeout(udl->urbs.sleep, +					 udl->urbs.available == udl->urbs.count, +					 udl->urbs.lock, +					 msecs_to_jiffies(2000))) +		drm_err(dev, "Timeout for syncing pending URBs\n"); +	spin_unlock_irq(&udl->urbs.lock); +} +  int udl_init(struct udl_device *udl)  {  	struct drm_device *dev = &udl->drm;  |