V4L/DVB (9764): em28xx: Add support for suspend the device when not used

Several chips may be turned off when the device is not used, like audio,
video and dvb demods. This patch adds a gpio callback at the core
structs to allow turning off such devices.

Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
index be78485..a4b61e5 100644
--- a/drivers/media/video/em28xx/em28xx-cards.c
+++ b/drivers/media/video/em28xx/em28xx-cards.c
@@ -1456,7 +1456,7 @@
 	em28xx_set_mode(dev, EM28XX_ANALOG_MODE);
 
 	/* Unlock device */
-	em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+	em28xx_set_mode(dev, EM28XX_SUSPEND);
 }
 
 static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl)
diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
index 71a1aaa..c3ff8ad 100644
--- a/drivers/media/video/em28xx/em28xx-core.c
+++ b/drivers/media/video/em28xx/em28xx-core.c
@@ -737,12 +737,14 @@
 	if (!gpio)
 		return rc;
 
-	em28xx_write_reg(dev, 0x48, 0x00);
-	if (dev->mode == EM28XX_ANALOG_MODE)
-		em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
-	else
-		em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
-	msleep(6);
+	if (dev->mode != EM28XX_SUSPEND) {
+		em28xx_write_reg(dev, 0x48, 0x00);
+		if (dev->mode == EM28XX_ANALOG_MODE)
+			em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x67);
+		else
+			em28xx_write_reg(dev, EM28XX_R12_VINENABLE, 0x37);
+		msleep(6);
+	}
 
 	/* Send GPIO reset sequences specified at board entry */
 	while (gpio->sleep >= 0) {
@@ -767,9 +769,12 @@
 	if (dev->mode == set_mode)
 		return 0;
 
-	if (set_mode == EM28XX_MODE_UNDEFINED) {
+	if (set_mode == EM28XX_SUSPEND) {
 		dev->mode = set_mode;
-		return 0;
+
+		/* FIXME: add suspend support for ac97 */
+
+		return em28xx_gpio_set(dev, dev->board.suspend_gpio);
 	}
 
 	dev->mode = set_mode;
diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
index 09c15cc..211156d 100644
--- a/drivers/media/video/em28xx/em28xx-dvb.c
+++ b/drivers/media/video/em28xx/em28xx-dvb.c
@@ -161,7 +161,7 @@
 
 	em28xx_uninit_isoc(dev);
 
-	em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+	em28xx_set_mode(dev, EM28XX_SUSPEND);
 
 	return 0;
 }
@@ -215,7 +215,7 @@
 	if (acquire)
 		return em28xx_set_mode(dev, EM28XX_DIGITAL_MODE);
 	else
-		return em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+		return em28xx_set_mode(dev, EM28XX_SUSPEND);
 }
 
 /* ------------------------------------------------------------------ */
@@ -466,12 +466,12 @@
 	if (result < 0)
 		goto out_free;
 
-	em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+	em28xx_set_mode(dev, EM28XX_SUSPEND);
 	printk(KERN_INFO "Successfully loaded em28xx-dvb\n");
 	return 0;
 
 out_free:
-	em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+	em28xx_set_mode(dev, EM28XX_SUSPEND);
 	kfree(dvb);
 	dev->dvb = NULL;
 	return result;
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 87e9f1d..efa4c82 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -1697,7 +1697,7 @@
 
 		/* do this before setting alternate! */
 		em28xx_uninit_isoc(dev);
-		em28xx_set_mode(dev, EM28XX_MODE_UNDEFINED);
+		em28xx_set_mode(dev, EM28XX_SUSPEND);
 
 		/* set alternate 0 */
 		dev->alt = 0;
diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
index b2b41b3..b505d44 100644
--- a/drivers/media/video/em28xx/em28xx.h
+++ b/drivers/media/video/em28xx/em28xx.h
@@ -160,7 +160,7 @@
 #define EM2800_I2C_WRITE_TIMEOUT 20
 
 enum em28xx_mode {
-	EM28XX_MODE_UNDEFINED,
+	EM28XX_SUSPEND,
 	EM28XX_ANALOG_MODE,
 	EM28XX_DIGITAL_MODE,
 };
@@ -336,6 +336,7 @@
 	unsigned int tda9887_conf;
 
 	struct em28xx_reg_seq *dvb_gpio;
+	struct em28xx_reg_seq *suspend_gpio;
 
 	unsigned int is_em2800:1;
 	unsigned int has_msp34xx:1;