V4L/DVB (7089): V4L: stkwebcam: Power management support

Signed-off-by: Jaime Velasco Juan <jsagarribay@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
index ba4fe93..f84e8d5 100644
--- a/drivers/media/video/stk-webcam.c
+++ b/drivers/media/video/stk-webcam.c
@@ -682,6 +682,7 @@
 		return -ENXIO;
 	fp->private_data = vdev;
 	kref_get(&dev->kref);
+	usb_autopm_get_interface(dev->interface);
 
 	return 0;
 }
@@ -703,6 +704,7 @@
 	}
 
 	if (dev->owner != fp) {
+		usb_autopm_put_interface(dev->interface);
 		kref_put(&dev->kref, stk_camera_cleanup);
 		return 0;
 	}
@@ -713,6 +715,7 @@
 
 	dev->owner = NULL;
 
+	usb_autopm_put_interface(dev->interface);
 	kref_put(&dev->kref, stk_camera_cleanup);
 
 	return 0;
@@ -1085,6 +1088,42 @@
 	return 0;
 }
 
+static int stk_setup_format(struct stk_camera *dev)
+{
+	int i = 0;
+	int depth;
+	if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8)
+		depth = 1;
+	else
+		depth = 2;
+	while (stk_sizes[i].m != dev->vsettings.mode
+			&& i < ARRAY_SIZE(stk_sizes))
+		i++;
+	if (i == ARRAY_SIZE(stk_sizes)) {
+		STK_ERROR("Something is broken in %s\n", __FUNCTION__);
+		return -EFAULT;
+	}
+	/* This registers controls some timings, not sure of what. */
+	stk_camera_write_reg(dev, 0x001b, 0x0e);
+	if (dev->vsettings.mode == MODE_SXGA)
+		stk_camera_write_reg(dev, 0x001c, 0x0e);
+	else
+		stk_camera_write_reg(dev, 0x001c, 0x46);
+	/*
+	 * Registers 0x0115 0x0114 are the size of each line (bytes),
+	 * regs 0x0117 0x0116 are the heigth of the image.
+	 */
+	stk_camera_write_reg(dev, 0x0115,
+		((stk_sizes[i].w * depth) >> 8) & 0xff);
+	stk_camera_write_reg(dev, 0x0114,
+		(stk_sizes[i].w * depth) & 0xff);
+	stk_camera_write_reg(dev, 0x0117,
+		(stk_sizes[i].h >> 8) & 0xff);
+	stk_camera_write_reg(dev, 0x0116,
+		stk_sizes[i].h & 0xff);
+	return stk_sensor_configure(dev);
+}
+
 static int stk_vidioc_s_fmt_cap(struct file *filp,
 		void *priv, struct v4l2_format *fmtd)
 {
@@ -1099,10 +1138,10 @@
 		return -EBUSY;
 	if (dev->owner && dev->owner != filp)
 		return -EBUSY;
-	dev->owner = filp;
 	ret = stk_vidioc_try_fmt_cap(filp, priv, fmtd);
 	if (ret)
 		return ret;
+	dev->owner = filp;
 
 	dev->vsettings.palette = fmtd->fmt.pix.pixelformat;
 	stk_free_buffers(dev);
@@ -1110,25 +1149,7 @@
 	dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m;
 
 	stk_initialise(dev);
-	/* This registers controls some timings, not sure of what. */
-	stk_camera_write_reg(dev, 0x001b, 0x0e);
-	if (dev->vsettings.mode == MODE_SXGA)
-		stk_camera_write_reg(dev, 0x001c, 0x0e);
-	else
-		stk_camera_write_reg(dev, 0x001c, 0x46);
-	/*
-	 * Registers 0x0115 0x0114 are the size of each line (bytes),
-	 * regs 0x0117 0x0116 are the heigth of the image.
-	 */
-	stk_camera_write_reg(dev, 0x0115,
-		(fmtd->fmt.pix.bytesperline >> 8) & 0xff);
-	stk_camera_write_reg(dev, 0x0114,
-		fmtd->fmt.pix.bytesperline & 0xff);
-	stk_camera_write_reg(dev, 0x0117,
-		(fmtd->fmt.pix.height >> 8) & 0xff);
-	stk_camera_write_reg(dev, 0x0116,
-		fmtd->fmt.pix.height & 0xff);
-	return stk_sensor_configure(dev);
+	return stk_setup_format(dev);
 }
 
 static int stk_vidioc_reqbufs(struct file *filp,
@@ -1422,6 +1443,7 @@
 	}
 
 	stk_create_sysfs_files(&dev->vdev);
+	usb_autopm_enable(dev->interface);
 
 	return 0;
 }
@@ -1439,11 +1461,41 @@
 	kref_put(&dev->kref, stk_camera_cleanup);
 }
 
+#ifdef CONFIG_PM
+int stk_camera_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct stk_camera *dev = usb_get_intfdata(intf);
+	if (is_streaming(dev)) {
+		stk_stop_stream(dev);
+		/* yes, this is ugly */
+		set_streaming(dev);
+	}
+	return 0;
+}
+
+int stk_camera_resume(struct usb_interface *intf)
+{
+	struct stk_camera *dev = usb_get_intfdata(intf);
+	if (!is_initialised(dev))
+		return 0;
+	unset_initialised(dev);
+	stk_initialise(dev);
+	stk_setup_format(dev);
+	if (is_streaming(dev))
+		stk_start_stream(dev);
+	return 0;
+}
+#endif
+
 static struct usb_driver stk_camera_driver = {
 	.name = "stkwebcam",
 	.probe = stk_camera_probe,
 	.disconnect = stk_camera_disconnect,
 	.id_table = stkwebcam_table,
+#ifdef CONFIG_PM
+	.suspend = stk_camera_suspend,
+	.resume = stk_camera_resume,
+#endif
 };
 
 
diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/video/stk-webcam.h
index 7e989d1..a2164cb 100644
--- a/drivers/media/video/stk-webcam.h
+++ b/drivers/media/video/stk-webcam.h
@@ -79,6 +79,7 @@
 #define unset_present(dev)	((dev)->status &= \
 					~(S_PRESENT|S_INITIALISED|S_STREAMING))
 #define set_initialised(dev)	((dev)->status |= S_INITIALISED)
+#define unset_initialised(dev)	((dev)->status &= ~S_INITIALISED)
 #define set_memallocd(dev)	((dev)->status |= S_MEMALLOCD)
 #define unset_memallocd(dev)	((dev)->status &= ~S_MEMALLOCD)
 #define set_streaming(dev)	((dev)->status |= S_STREAMING)