V4L/DVB (6271): V4L: Add basic support for suspend/resume for saa7134

This adds support for suspend/resume for core of saa7134

Should fix bug#7220

Signed-off-by: Maxim Levitsky <maximlevitsky@gmail.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index a1d986e..7f5df32 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -31,6 +31,7 @@
 #include <linux/delay.h>
 #include <linux/mutex.h>
 #include <linux/dma-mapping.h>
+#include <linux/pm.h>
 
 #include "saa7134-reg.h"
 #include "saa7134.h"
@@ -392,6 +393,38 @@
 	spin_unlock_irqrestore(&dev->slock,flags);
 }
 
+/* resends a current buffer in queue after resume */
+
+int saa7134_buffer_requeue(struct saa7134_dev *dev,
+			 struct saa7134_dmaqueue *q)
+{
+	struct saa7134_buf *buf , *next;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->slock, flags);
+
+	buf  = q->curr;
+	next = buf;
+
+	dprintk("buffer_requeue\n");
+
+	if (!buf) {
+		spin_unlock_irqrestore(&dev->slock, flags);
+		return 0;
+	}
+
+	dprintk("buffer_requeue : resending active buffers \n");
+
+	if (!list_empty(&q->queue))
+		next = list_entry(q->queue.next, struct saa7134_buf,
+					  vb.queue);
+
+	buf->activate(dev, buf, next);
+	spin_unlock_irqrestore(&dev->slock, flags);
+
+	return 0;
+}
+
 /* ------------------------------------------------------------------ */
 
 int saa7134_set_dmabits(struct saa7134_dev *dev)
@@ -647,6 +680,39 @@
 /* ------------------------------------------------------------------ */
 
 /* early init (no i2c, no irq) */
+
+static int saa7134_hw_enable1(struct saa7134_dev *dev)
+{
+	/* RAM FIFO config */
+	saa_writel(SAA7134_FIFO_SIZE, 0x08070503);
+	saa_writel(SAA7134_THRESHOULD, 0x02020202);
+
+	/* enable audio + video processing */
+	saa_writel(SAA7134_MAIN_CTRL,
+			SAA7134_MAIN_CTRL_VPLLE |
+			SAA7134_MAIN_CTRL_APLLE |
+			SAA7134_MAIN_CTRL_EXOSC |
+			SAA7134_MAIN_CTRL_EVFE1 |
+			SAA7134_MAIN_CTRL_EVFE2 |
+			SAA7134_MAIN_CTRL_ESFE  |
+			SAA7134_MAIN_CTRL_EBDAC);
+
+	/*
+	* Initialize OSS _after_ enabling audio clock PLL and audio processing.
+	* OSS initialization writes to registers via the audio DSP; these
+	* writes will fail unless the audio clock has been started.  At worst,
+	* audio will not work.
+	*/
+
+	/* enable peripheral devices */
+	saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
+
+	/* set vertical line numbering start (vbi needs this) */
+	saa_writeb(SAA7134_SOURCE_TIMING2, 0x20);
+
+	return 0;
+}
+
 static int saa7134_hwinit1(struct saa7134_dev *dev)
 {
 	dprintk("hwinit1\n");
@@ -663,44 +729,16 @@
 		saa7134_ts_init1(dev);
 	saa7134_input_init1(dev);
 
-	/* RAM FIFO config */
-	saa_writel(SAA7134_FIFO_SIZE, 0x08070503);
-	saa_writel(SAA7134_THRESHOULD,0x02020202);
-
-	/* enable audio + video processing */
-	saa_writel(SAA7134_MAIN_CTRL,
-		   SAA7134_MAIN_CTRL_VPLLE |
-		   SAA7134_MAIN_CTRL_APLLE |
-		   SAA7134_MAIN_CTRL_EXOSC |
-		   SAA7134_MAIN_CTRL_EVFE1 |
-		   SAA7134_MAIN_CTRL_EVFE2 |
-		   SAA7134_MAIN_CTRL_ESFE  |
-		   SAA7134_MAIN_CTRL_EBDAC);
-
-	/*
-	 * Initialize OSS _after_ enabling audio clock PLL and audio processing.
-	 * OSS initialization writes to registers via the audio DSP; these
-	 * writes will fail unless the audio clock has been started.  At worst,
-	 * audio will not work.
-	 */
-
-	/* enable peripheral devices */
-	saa_writeb(SAA7134_SPECIAL_MODE, 0x01);
-
-	/* set vertical line numbering start (vbi needs this) */
-	saa_writeb(SAA7134_SOURCE_TIMING2, 0x20);
+	saa7134_hw_enable1(dev);
 
 	return 0;
 }
 
 /* late init (with i2c + irq) */
-static int saa7134_hwinit2(struct saa7134_dev *dev)
+static int saa7134_hw_enable2(struct saa7134_dev *dev)
 {
-	unsigned int irq2_mask;
-	dprintk("hwinit2\n");
 
-	saa7134_video_init2(dev);
-	saa7134_tvaudio_init2(dev);
+	unsigned int irq2_mask;
 
 	/* enable IRQ's */
 	irq2_mask =
@@ -726,6 +764,20 @@
 	return 0;
 }
 
+static int saa7134_hwinit2(struct saa7134_dev *dev)
+{
+
+	dprintk("hwinit2\n");
+
+	saa7134_video_init2(dev);
+	saa7134_tvaudio_init2(dev);
+
+	saa7134_hw_enable2(dev);
+
+	return 0;
+}
+
+
 /* shutdown */
 static int saa7134_hwfini(struct saa7134_dev *dev)
 {
@@ -1118,6 +1170,65 @@
 	kfree(dev);
 }
 
+static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
+{
+
+	/* Disable card's IRQs to prevent it from resuming computer */
+
+	struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
+
+	saa_writel(SAA7134_IRQ1, 0);
+	saa_writel(SAA7134_IRQ2, 0);
+
+
+	pci_set_power_state(pci_dev, pci_choose_state(pci_dev, state));
+	pci_save_state(pci_dev);
+
+    return 0;
+}
+
+static int saa7134_resume(struct pci_dev *pci_dev)
+{
+
+	struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
+
+	pci_restore_state(pci_dev);
+	pci_set_power_state(pci_dev, PCI_D0);
+
+	/* Do things that are done in saa7134_initdev ,
+		except of initializing memory structures.*/
+
+	saa7134_board_init1(dev);
+
+	if (saa7134_boards[dev->board].video_out)
+		saa7134_videoport_init(dev);
+
+	if (card_has_mpeg(dev))
+		saa7134_ts_init_hw(dev);
+
+	saa7134_hw_enable1(dev);
+
+	saa7134_set_decoder(dev);
+
+	saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id);
+
+	saa7134_board_init2(dev);
+	saa7134_hw_enable2(dev);
+
+	dev->force_mute_update = 1;
+	saa7134_tvaudio_setmute(dev);
+	dev->force_mute_update = 0;
+	saa7134_tvaudio_setvolume(dev, dev->ctl_volume);
+	saa7134_enable_i2s(dev);
+
+	/*recapture unfinished buffer(s)*/
+	saa7134_buffer_requeue(dev, &dev->video_q);
+	saa7134_buffer_requeue(dev, &dev->vbi_q);
+	saa7134_buffer_requeue(dev, &dev->ts_q);
+
+	return 0;
+}
+
 /* ----------------------------------------------------------- */
 
 int saa7134_ts_register(struct saa7134_mpeg_ops *ops)
@@ -1159,6 +1270,8 @@
 	.id_table = saa7134_pci_tbl,
 	.probe    = saa7134_initdev,
 	.remove   = __devexit_p(saa7134_finidev),
+	.suspend  = saa7134_suspend,
+	.resume   = saa7134_resume
 };
 
 static int saa7134_init(void)
diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c
index 5b1d1da..4b63ad3 100644
--- a/drivers/media/video/saa7134/saa7134-ts.c
+++ b/drivers/media/video/saa7134/saa7134-ts.c
@@ -177,6 +177,22 @@
 module_param(ts_nr_packets, int, 0444);
 MODULE_PARM_DESC(ts_nr_packets,"size of a ts buffers (in ts packets)");
 
+int saa7134_ts_init_hw(struct saa7134_dev *dev)
+{
+	/* deactivate TS softreset */
+	saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+	/* TSSOP high active, TSVAL high active, TSLOCK ignored */
+	saa_writeb(SAA7134_TS_PARALLEL, 0xec);
+	saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1));
+	saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff));
+	saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff));
+	/* TSNOPIT=0, TSCOLAP=0 */
+	saa_writeb(SAA7134_TS_DMA2,
+		((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00));
+
+	return 0;
+}
+
 int saa7134_ts_init1(struct saa7134_dev *dev)
 {
 	/* sanitycheck insmod options */
@@ -200,12 +216,7 @@
 	saa7134_pgtable_alloc(dev->pci,&dev->ts.pt_ts);
 
 	/* init TS hw */
-	saa_writeb(SAA7134_TS_SERIAL1, 0x00);  /* deactivate TS softreset */
-	saa_writeb(SAA7134_TS_PARALLEL, 0xec); /* TSSOP high active, TSVAL high active, TSLOCK ignored */
-	saa_writeb(SAA7134_TS_PARALLEL_SERIAL, (TS_PACKET_SIZE-1));
-	saa_writeb(SAA7134_TS_DMA0, ((dev->ts.nr_packets-1)&0xff));
-	saa_writeb(SAA7134_TS_DMA1, (((dev->ts.nr_packets-1)>>8)&0xff));
-	saa_writeb(SAA7134_TS_DMA2, ((((dev->ts.nr_packets-1)>>16)&0x3f) | 0x00)); /* TSNOPIT=0, TSCOLAP=0 */
+	saa7134_ts_init_hw(dev);
 
 	return 0;
 }
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index 43501b5..df2dab0 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -231,7 +231,7 @@
 	}
 
 	if (dev->hw_mute  == mute &&
-		dev->hw_input == in) {
+		dev->hw_input == in && !dev->force_mute_update) {
 		dprintk("mute/input: nothing to do [mute=%d,input=%s]\n",
 			mute,in->name);
 		return;
@@ -876,7 +876,7 @@
 /* ------------------------------------------------------------------ */
 /* common stuff + external entry points                               */
 
-static void saa7134_enable_i2s(struct saa7134_dev *dev)
+void saa7134_enable_i2s(struct saa7134_dev *dev)
 {
 	int i2s_format;
 
diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
index 525b5b7..24d5797 100644
--- a/drivers/media/video/saa7134/saa7134-video.c
+++ b/drivers/media/video/saa7134/saa7134-video.c
@@ -540,22 +540,12 @@
 
 /* ------------------------------------------------------------------ */
 
-static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
+void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
 {
-	int luma_control,sync_control,mux;
 
 	dprintk("set tv norm = %s\n",norm->name);
 	dev->tvnorm = norm;
 
-	mux = card_in(dev,dev->ctl_input).vmux;
-	luma_control = norm->luma_control;
-	sync_control = norm->sync_control;
-
-	if (mux > 5)
-		luma_control |= 0x80; /* svideo */
-	if (noninterlaced || dev->nosignal)
-		sync_control |= 0x20;
-
 	/* setup cropping */
 	dev->crop_bounds.left    = norm->h_start;
 	dev->crop_defrect.left   = norm->h_start;
@@ -570,6 +560,40 @@
 
 	dev->crop_current = dev->crop_defrect;
 
+	saa7134_set_decoder(dev);
+
+	if (card_in(dev, dev->ctl_input).tv) {
+		if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290)
+				&& ((card(dev).tuner_config == 1)
+				||  (card(dev).tuner_config == 2)))
+			saa7134_set_gpio(dev, 22, 5);
+		saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &norm->id);
+	}
+}
+
+static void video_mux(struct saa7134_dev *dev, int input)
+{
+	dprintk("video input = %d [%s]\n", input, card_in(dev, input).name);
+	dev->ctl_input = input;
+	set_tvnorm(dev, dev->tvnorm);
+	saa7134_tvaudio_setinput(dev, &card_in(dev, input));
+}
+
+void saa7134_set_decoder(struct saa7134_dev *dev)
+{
+	int luma_control, sync_control, mux;
+
+	struct saa7134_tvnorm *norm = dev->tvnorm;
+	mux = card_in(dev, dev->ctl_input).vmux;
+
+	luma_control = norm->luma_control;
+	sync_control = norm->sync_control;
+
+	if (mux > 5)
+		luma_control |= 0x80; /* svideo */
+	if (noninterlaced || dev->nosignal)
+		sync_control |= 0x20;
+
 	/* setup video decoder */
 	saa_writeb(SAA7134_INCR_DELAY,            0x08);
 	saa_writeb(SAA7134_ANALOG_IN_CTRL1,       0xc0 | mux);
@@ -604,23 +628,6 @@
 	saa_writeb(SAA7134_MISC_VGATE_MSB,        norm->vgate_misc);
 	saa_writeb(SAA7134_RAW_DATA_GAIN,         0x40);
 	saa_writeb(SAA7134_RAW_DATA_OFFSET,       0x80);
-
-	/* only tell the tuner if this is a tv input */
-	if (card_in(dev,dev->ctl_input).tv) {
-		if ((card(dev).tuner_type == TUNER_PHILIPS_TDA8290)
-				&& ((card(dev).tuner_config == 1)
-				||  (card(dev).tuner_config == 2)))
-			saa7134_set_gpio(dev, 22, 5);
-		saa7134_i2c_call_clients(dev,VIDIOC_S_STD,&norm->id);
-	}
-}
-
-static void video_mux(struct saa7134_dev *dev, int input)
-{
-	dprintk("video input = %d [%s]\n",input,card_in(dev,input).name);
-	dev->ctl_input = input;
-	set_tvnorm(dev,dev->tvnorm);
-	saa7134_tvaudio_setinput(dev,&card_in(dev,input));
 }
 
 static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
@@ -2399,34 +2406,40 @@
 	dev->video_q.timeout.data     = (unsigned long)(&dev->video_q);
 	dev->video_q.dev              = dev;
 
-	if (saa7134_boards[dev->board].video_out) {
-		/* enable video output */
-		int vo = saa7134_boards[dev->board].video_out;
-		int video_reg;
-		unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
-		video_reg = video_out[vo][1];
-		if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
-			video_reg &= ~VP_T_CODE_P_INVERTED;
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
-		video_reg = video_out[vo][5];
-		if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
-			video_reg &= ~VP_CLK_CTRL2_DELAYED;
-		if (vid_port_opts & SET_CLOCK_INVERTED)
-			video_reg |= VP_CLK_CTRL1_INVERTED;
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
-		video_reg = video_out[vo][6];
-		if (vid_port_opts & SET_VSYNC_OFF) {
-			video_reg &= ~VP_VS_TYPE_MASK;
-			video_reg |= VP_VS_TYPE_OFF;
-		}
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
-		saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
+	if (saa7134_boards[dev->board].video_out)
+		saa7134_videoport_init(dev);
+
+	return 0;
+}
+
+int saa7134_videoport_init(struct saa7134_dev *dev)
+{
+	/* enable video output */
+	int vo = saa7134_boards[dev->board].video_out;
+	int video_reg;
+	unsigned int vid_port_opts = saa7134_boards[dev->board].vid_port_opts;
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL0, video_out[vo][0]);
+	video_reg = video_out[vo][1];
+	if (vid_port_opts & SET_T_CODE_POLARITY_NON_INVERTED)
+		video_reg &= ~VP_T_CODE_P_INVERTED;
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL1, video_reg);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL2, video_out[vo][2]);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL3, video_out[vo][3]);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL4, video_out[vo][4]);
+	video_reg = video_out[vo][5];
+	if (vid_port_opts & SET_CLOCK_NOT_DELAYED)
+		video_reg &= ~VP_CLK_CTRL2_DELAYED;
+	if (vid_port_opts & SET_CLOCK_INVERTED)
+		video_reg |= VP_CLK_CTRL1_INVERTED;
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL5, video_reg);
+	video_reg = video_out[vo][6];
+	if (vid_port_opts & SET_VSYNC_OFF) {
+		video_reg &= ~VP_VS_TYPE_MASK;
+		video_reg |= VP_VS_TYPE_OFF;
 	}
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL6, video_reg);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL7, video_out[vo][7]);
+	saa_writeb(SAA7134_VIDEO_PORT_CTRL8, video_out[vo][8]);
 
 	return 0;
 }
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index cb617c8..5b1f226 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -522,6 +522,7 @@
 	struct saa7134_input       *input;
 	struct saa7134_input       *hw_input;
 	unsigned int               hw_mute;
+	unsigned int               force_mute_update;
 	int                        last_carrier;
 	int                        nosignal;
 
@@ -594,6 +595,9 @@
 void saa7134_buffer_timeout(unsigned long data);
 void saa7134_dma_free(struct videobuf_queue *q,struct saa7134_buf *buf);
 
+int saa7134_buffer_requeue(struct saa7134_dev *dev,
+			 struct saa7134_dmaqueue *q);
+
 int saa7134_set_dmabits(struct saa7134_dev *dev);
 
 extern int (*saa7134_dmasound_init)(struct saa7134_dev *dev);
@@ -626,6 +630,10 @@
 extern struct video_device saa7134_video_template;
 extern struct video_device saa7134_radio_template;
 
+void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm);
+int saa7134_videoport_init(struct saa7134_dev *dev);
+void saa7134_set_decoder(struct saa7134_dev *dev);
+
 int saa7134_common_ioctl(struct saa7134_dev *dev,
 			 unsigned int cmd, void *arg);
 
@@ -649,6 +657,8 @@
 int saa7134_ts_register(struct saa7134_mpeg_ops *ops);
 void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops);
 
+int saa7134_ts_init_hw(struct saa7134_dev *dev);
+
 /* ----------------------------------------------------------- */
 /* saa7134-vbi.c                                               */
 
@@ -677,6 +687,8 @@
 
 int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
 
+void saa7134_enable_i2s(struct saa7134_dev *dev);
+
 /* ----------------------------------------------------------- */
 /* saa7134-oss.c                                               */