V4L/DVB (6271): V4L: Add basic support for suspend/resume for saa7134
authorMaxim Levitsky <maximlevitsky@gmail.com>
Thu, 27 Sep 2007 23:34:25 +0000 (20:34 -0300)
committerMauro Carvalho Chehab <mchehab@infradead.org>
Wed, 10 Oct 2007 03:02:59 +0000 (00:02 -0300)
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>
drivers/media/video/saa7134/saa7134-core.c
drivers/media/video/saa7134/saa7134-ts.c
drivers/media/video/saa7134/saa7134-tvaudio.c
drivers/media/video/saa7134/saa7134-video.c
drivers/media/video/saa7134/saa7134.h

index a1d986e..7f5df32 100644 (file)
@@ -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 @@ void saa7134_buffer_timeout(unsigned long data)
        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 @@ static irqreturn_t saa7134_irq(int irq, void *dev_id)
 /* ------------------------------------------------------------------ */
 
 /* 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 @@ static int saa7134_hwinit1(struct saa7134_dev *dev)
                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 @@ static int saa7134_hwinit2(struct saa7134_dev *dev)
        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 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
        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 @@ static struct pci_driver saa7134_pci_driver = {
        .id_table = saa7134_pci_tbl,
        .probe    = saa7134_initdev,
        .remove   = __devexit_p(saa7134_finidev),
+       .suspend  = saa7134_suspend,
+       .resume   = saa7134_resume
 };
 
 static int saa7134_init(void)
index 5b1d1da..4b63ad3 100644 (file)
@@ -177,6 +177,22 @@ static unsigned int ts_nr_packets = 64;
 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 @@ int saa7134_ts_init1(struct saa7134_dev *dev)
        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;
 }
index 43501b5..df2dab0 100644 (file)
@@ -231,7 +231,7 @@ static void mute_input_7134(struct saa7134_dev *dev)
        }
 
        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 @@ static int tvaudio_thread_ddep(void *data)
 /* ------------------------------------------------------------------ */
 /* 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;
 
index 525b5b7..24d5797 100644 (file)
@@ -540,22 +540,12 @@ void res_free(struct saa7134_dev *dev, struct saa7134_fh *fh, unsigned int bits)
 
 /* ------------------------------------------------------------------ */
 
-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 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
 
        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 @@ static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
        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 @@ int saa7134_video_init1(struct saa7134_dev *dev)
        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;
 }
index cb617c8..5b1f226 100644 (file)
@@ -522,6 +522,7 @@ struct saa7134_dev {
        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_next(struct saa7134_dev *dev, struct saa7134_dmaqueue *q);
 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 @@ void saa7134_i2c_call_clients(struct saa7134_dev *dev,
 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 @@ void saa7134_irq_ts_done(struct saa7134_dev *dev, unsigned long status);
 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 saa7134_tvaudio_do_scan(struct saa7134_dev *dev);
 
 int saa_dsp_writel(struct saa7134_dev *dev, int reg, u32 value);
 
+void saa7134_enable_i2s(struct saa7134_dev *dev);
+
 /* ----------------------------------------------------------- */
 /* saa7134-oss.c                                               */