[media] pwc: Allow dqbuf / read to complete while waiting for controls

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c
index 986dd5d..5376572 100644
--- a/drivers/media/video/pwc/pwc-v4l.c
+++ b/drivers/media/video/pwc/pwc-v4l.c
@@ -526,8 +526,24 @@
 		container_of(ctrl->handler, struct pwc_device, ctrl_handler);
 	int ret = 0;
 
-	if (!pdev->udev)
-		return -ENODEV;
+	/*
+	 * Sometimes it can take quite long for the pwc to complete usb control
+	 * transfers, so release the modlock to give streaming by another
+	 * process / thread the chance to continue with a dqbuf.
+	 */
+	mutex_unlock(&pdev->modlock);
+
+	/*
+	 * Take the udev-lock to protect against the disconnect handler
+	 * completing and setting dev->udev to NULL underneath us. Other code
+	 * does not need to do this since it is protected by the modlock.
+	 */
+	mutex_lock(&pdev->udevlock);
+
+	if (!pdev->udev) {
+		ret = -ENODEV;
+		goto leave;
+	}
 
 	switch (ctrl->id) {
 	case V4L2_CID_AUTO_WHITE_BALANCE:
@@ -590,6 +606,9 @@
 	if (ret)
 		PWC_ERROR("g_ctrl %s error %d\n", ctrl->name, ret);
 
+leave:
+	mutex_unlock(&pdev->udevlock);
+	mutex_lock(&pdev->modlock);
 	return ret;
 }
 
@@ -751,8 +770,14 @@
 		container_of(ctrl->handler, struct pwc_device, ctrl_handler);
 	int ret = 0;
 
-	if (!pdev->udev)
-		return -ENODEV;
+	/* See the comments on locking in pwc_g_volatile_ctrl */
+	mutex_unlock(&pdev->modlock);
+	mutex_lock(&pdev->udevlock);
+
+	if (!pdev->udev) {
+		ret = -ENODEV;
+		goto leave;
+	}
 
 	switch (ctrl->id) {
 	case V4L2_CID_BRIGHTNESS:
@@ -841,6 +866,9 @@
 	if (ret)
 		PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret);
 
+leave:
+	mutex_unlock(&pdev->udevlock);
+	mutex_lock(&pdev->modlock);
 	return ret;
 }