[PATCH] flexcop-pci: add suspend/resume support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Patrick! Hi list!

This patch adds suspend/resume support to flexcop-pci driver.

I could only test this patch with the bare card, but without having a DVB-S 
signal. I checked it with and without running szap (obviously getting no 
lock).
It works fine here with suspend-to-disk on a tuxonice kernel.

Setting of hw-filter in resume is done the same way as the watchdog does it: 
Just looping over fc->demux.feed_list and running flexcop_pid_feed_control.
Where I am unsure is the order at resume. For now hw filters get started 
first, then dma is re-started.

Do I need to give special care to irq handling?

Regards
Matthias
Index: v4l-dvb/linux/drivers/media/dvb/b2c2/flexcop-common.h
===================================================================
--- v4l-dvb.orig/linux/drivers/media/dvb/b2c2/flexcop-common.h
+++ v4l-dvb/linux/drivers/media/dvb/b2c2/flexcop-common.h
@@ -117,6 +117,9 @@ int flexcop_device_initialize(struct fle
 void flexcop_device_exit(struct flexcop_device *fc);
 void flexcop_reset_block_300(struct flexcop_device *fc);
 
+void flexcop_device_suspend(struct flexcop_device *fc);
+void flexcop_device_resume(struct flexcop_device *fc);
+
 /* from flexcop-dma.c */
 int flexcop_dma_allocate(struct pci_dev *pdev,
 		struct flexcop_dma *dma, u32 size);
Index: v4l-dvb/linux/drivers/media/dvb/b2c2/flexcop.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/dvb/b2c2/flexcop.c
+++ v4l-dvb/linux/drivers/media/dvb/b2c2/flexcop.c
@@ -292,6 +292,59 @@ void flexcop_device_exit(struct flexcop_
 }
 EXPORT_SYMBOL(flexcop_device_exit);
 
+void flexcop_device_suspend(struct flexcop_device *fc)
+{
+	/* flexcop_device_exit does only unregister objects
+	 * so just stop streaming here */
+	struct dvb_demux_feed *feed;
+
+	/* copied from flexcop_pci_irq_check_work */
+	info("stopping pid feeds");
+	spin_lock_irq(&fc->demux.lock);
+	list_for_each_entry(feed, &fc->demux.feed_list,
+			list_head) {
+		flexcop_pid_feed_control(fc, feed, 0);
+	}
+	spin_unlock_irq(&fc->demux.lock);
+}
+EXPORT_SYMBOL(flexcop_device_suspend);
+
+void flexcop_device_resume(struct flexcop_device *fc)
+{
+	/* copied from flexcop_device_initialize */
+	struct dvb_demux_feed *feed;
+	flexcop_reset(fc);
+
+	flexcop_sram_init(fc);
+	flexcop_hw_filter_init(fc);
+	flexcop_smc_ctrl(fc, 0);
+
+	/* do the MAC address reading after initializing the dvb_adapter */
+	/* TODO: need not reread MAC address, but status was not saved */
+	if (fc->get_mac_addr(fc, 0) == 0) {
+		u8 *b = fc->dvb_adapter.proposed_mac;
+		info("MAC address = %pM", b);
+		flexcop_set_mac_filter(fc, b);
+		flexcop_mac_filter_ctrl(fc, 1);
+	} else
+		warn("reading of MAC address failed.\n");
+
+	/* TODO: Is it fine to start streaming here,
+	 * before DMA is re-initialized */
+
+	/* copied from flexcop_pci_irq_check_work */
+	info("restarting pid feeds");
+	spin_lock_irq(&fc->demux.lock);
+	list_for_each_entry(feed, &fc->demux.feed_list,
+			list_head) {
+		flexcop_pid_feed_control(fc, feed, 1);
+	}
+	spin_unlock_irq(&fc->demux.lock);
+
+	flexcop_device_name(fc, "resume of", "complete");
+}
+EXPORT_SYMBOL(flexcop_device_resume);
+
 static int flexcop_module_init(void)
 {
 	info(DRIVER_NAME " loaded successfully");
Index: v4l-dvb/linux/drivers/media/dvb/b2c2/flexcop-pci.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/dvb/b2c2/flexcop-pci.c
+++ v4l-dvb/linux/drivers/media/dvb/b2c2/flexcop-pci.c
@@ -319,8 +319,6 @@ static int flexcop_pci_init(struct flexc
 	pci_read_config_byte(fc_pci->pdev, PCI_CLASS_REVISION, &card_rev);
 	info("card revision %x", card_rev);
 
-	if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
-		return ret;
 	pci_set_master(fc_pci->pdev);
 
 	if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
@@ -334,7 +332,6 @@ static int flexcop_pci_init(struct flexc
 		goto err_pci_release_regions;
 	}
 
-	pci_set_drvdata(fc_pci->pdev, fc_pci);
 	spin_lock_init(&fc_pci->irq_lock);
 	if ((ret = request_irq(fc_pci->pdev->irq, flexcop_pci_isr,
 					IRQF_SHARED, DRIVER_NAME, fc_pci)) != 0)
@@ -345,7 +342,6 @@ static int flexcop_pci_init(struct flexc
 
 err_pci_iounmap:
 	pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
-	pci_set_drvdata(fc_pci->pdev, NULL);
 err_pci_release_regions:
 	pci_release_regions(fc_pci->pdev);
 err_pci_disable_device:
@@ -358,9 +354,7 @@ static void flexcop_pci_exit(struct flex
 	if (fc_pci->init_state & FC_PCI_INIT) {
 		free_irq(fc_pci->pdev->irq, fc_pci);
 		pci_iounmap(fc_pci->pdev, fc_pci->io_mem);
-		pci_set_drvdata(fc_pci->pdev, NULL);
 		pci_release_regions(fc_pci->pdev);
-		pci_disable_device(fc_pci->pdev);
 	}
 	fc_pci->init_state &= ~FC_PCI_INIT;
 }
@@ -399,6 +393,11 @@ static int flexcop_pci_probe(struct pci_
 
 	/* bus specific part */
 	fc_pci->pdev = pdev;
+	ret = pci_enable_device(pdev);
+	if (ret != 0)
+		goto err_kfree;
+
+	pci_set_drvdata(pdev, fc_pci);
 	if ((ret = flexcop_pci_init(fc_pci)) != 0)
 		goto err_kfree;
 
@@ -428,6 +427,7 @@ err_fc_exit:
 err_pci_exit:
 	flexcop_pci_exit(fc_pci);
 err_kfree:
+	pci_set_drvdata(pdev, NULL);
 	flexcop_device_kfree(fc);
 	return ret;
 }
@@ -445,9 +445,65 @@ static void flexcop_pci_remove(struct pc
 	flexcop_pci_dma_exit(fc_pci);
 	flexcop_device_exit(fc_pci->fc_dev);
 	flexcop_pci_exit(fc_pci);
+	pci_set_drvdata(pdev, NULL);
+	pci_disable_device(pdev);
 	flexcop_device_kfree(fc_pci->fc_dev);
 }
 
+#ifdef CONFIG_PM
+static int flexcop_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
+{
+	struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
+	struct flexcop_device *fc = fc_pci->fc_dev;
+
+	/* most parts are from flexcop_pci_remove */
+
+	if (irq_chk_intv > 0)
+		cancel_delayed_work(&fc_pci->irq_check_work);
+
+	flexcop_pci_dma_exit(fc_pci);
+	flexcop_device_suspend(fc);
+	flexcop_pci_exit(fc_pci);
+
+	pci_save_state(pdev);
+
+	pci_disable_device(pdev);
+	pci_set_power_state(pdev, pci_choose_state(pdev, mesg));
+
+	return 0;
+}
+
+static int flexcop_pci_resume(struct pci_dev *pdev)
+{
+	struct flexcop_pci *fc_pci = pci_get_drvdata(pdev);
+	struct flexcop_device *fc = fc_pci->fc_dev;
+
+	/* restore power state 0 */
+	pci_set_power_state(pdev, PCI_D0);
+	pci_restore_state(pdev);
+
+	pci_enable_device(pdev);
+
+	/* from flexcop_pci_probe */
+	flexcop_pci_init(fc_pci);
+
+	/* init flexcop */
+	flexcop_device_resume(fc); /* instead of flexcop_device_initialize */
+
+	/* init dma */
+	flexcop_pci_dma_init(fc_pci);
+
+	/* last step: restart watchdog */
+	if (irq_chk_intv > 0)
+		schedule_delayed_work(&fc_pci->irq_check_work,
+				msecs_to_jiffies(irq_chk_intv < 100 ?
+					100 :
+					irq_chk_intv));
+
+	return 0;
+}
+#endif
+
 static struct pci_device_id flexcop_pci_tbl[] = {
 	{ PCI_DEVICE(0x13d0, 0x2103) },
 	{ },
@@ -460,6 +516,10 @@ static struct pci_driver flexcop_pci_dri
 	.id_table = flexcop_pci_tbl,
 	.probe    = flexcop_pci_probe,
 	.remove   = flexcop_pci_remove,
+#ifdef CONFIG_PM
+	.suspend  = flexcop_pci_suspend,
+	.resume   = flexcop_pci_resume,
+#endif
 };
 
 static int __init flexcop_pci_module_init(void)

[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux