diff options
author | Hugues Fruchet <hugues.fruchet@st.com> | 2018-06-13 05:59:39 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab+samsung@kernel.org> | 2018-06-28 07:49:56 -0400 |
commit | 152e0bf60219ba589254c7aff4c095943c36cf68 (patch) | |
tree | 8204a5f38cd33ba54a181e18b19ffd2a48080609 /drivers/media/platform/stm32/stm32-dcmi.c | |
parent | f11552d030e8e5a0a945b1920e31eaf48fe3fad4 (diff) |
media: stm32-dcmi: add power saving support
Implements runtime & system sleep power management ops.
Signed-off-by: Hugues Fruchet <hugues.fruchet@st.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab+samsung@kernel.org>
Diffstat (limited to 'drivers/media/platform/stm32/stm32-dcmi.c')
-rw-r--r-- | drivers/media/platform/stm32/stm32-dcmi.c | 81 |
1 files changed, 65 insertions, 16 deletions
diff --git a/drivers/media/platform/stm32/stm32-dcmi.c b/drivers/media/platform/stm32/stm32-dcmi.c index 4f65f14fbbc1..721564176d8c 100644 --- a/drivers/media/platform/stm32/stm32-dcmi.c +++ b/drivers/media/platform/stm32/stm32-dcmi.c | |||
@@ -22,7 +22,9 @@ | |||
22 | #include <linux/of.h> | 22 | #include <linux/of.h> |
23 | #include <linux/of_device.h> | 23 | #include <linux/of_device.h> |
24 | #include <linux/of_graph.h> | 24 | #include <linux/of_graph.h> |
25 | #include <linux/pinctrl/consumer.h> | ||
25 | #include <linux/platform_device.h> | 26 | #include <linux/platform_device.h> |
27 | #include <linux/pm_runtime.h> | ||
26 | #include <linux/reset.h> | 28 | #include <linux/reset.h> |
27 | #include <linux/videodev2.h> | 29 | #include <linux/videodev2.h> |
28 | 30 | ||
@@ -567,9 +569,9 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) | |||
567 | u32 val = 0; | 569 | u32 val = 0; |
568 | int ret; | 570 | int ret; |
569 | 571 | ||
570 | ret = clk_enable(dcmi->mclk); | 572 | ret = pm_runtime_get_sync(dcmi->dev); |
571 | if (ret) { | 573 | if (ret) { |
572 | dev_err(dcmi->dev, "%s: Failed to start streaming, cannot enable clock\n", | 574 | dev_err(dcmi->dev, "%s: Failed to start streaming, cannot get sync\n", |
573 | __func__); | 575 | __func__); |
574 | goto err_release_buffers; | 576 | goto err_release_buffers; |
575 | } | 577 | } |
@@ -579,7 +581,7 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) | |||
579 | if (ret && ret != -ENOIOCTLCMD) { | 581 | if (ret && ret != -ENOIOCTLCMD) { |
580 | dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error", | 582 | dev_err(dcmi->dev, "%s: Failed to start streaming, subdev streamon error", |
581 | __func__); | 583 | __func__); |
582 | goto err_disable_clock; | 584 | goto err_pm_put; |
583 | } | 585 | } |
584 | 586 | ||
585 | spin_lock_irq(&dcmi->irqlock); | 587 | spin_lock_irq(&dcmi->irqlock); |
@@ -664,8 +666,8 @@ static int dcmi_start_streaming(struct vb2_queue *vq, unsigned int count) | |||
664 | err_subdev_streamoff: | 666 | err_subdev_streamoff: |
665 | v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); | 667 | v4l2_subdev_call(dcmi->entity.subdev, video, s_stream, 0); |
666 | 668 | ||
667 | err_disable_clock: | 669 | err_pm_put: |
668 | clk_disable(dcmi->mclk); | 670 | pm_runtime_put(dcmi->dev); |
669 | 671 | ||
670 | err_release_buffers: | 672 | err_release_buffers: |
671 | spin_lock_irq(&dcmi->irqlock); | 673 | spin_lock_irq(&dcmi->irqlock); |
@@ -717,7 +719,7 @@ static void dcmi_stop_streaming(struct vb2_queue *vq) | |||
717 | /* Stop all pending DMA operations */ | 719 | /* Stop all pending DMA operations */ |
718 | dmaengine_terminate_all(dcmi->dma_chan); | 720 | dmaengine_terminate_all(dcmi->dma_chan); |
719 | 721 | ||
720 | clk_disable(dcmi->mclk); | 722 | pm_runtime_put(dcmi->dev); |
721 | 723 | ||
722 | if (dcmi->errors_count) | 724 | if (dcmi->errors_count) |
723 | dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", | 725 | dev_warn(dcmi->dev, "Some errors found while streaming: errors=%d (overrun=%d), buffers=%d\n", |
@@ -1707,12 +1709,6 @@ static int dcmi_probe(struct platform_device *pdev) | |||
1707 | return -EPROBE_DEFER; | 1709 | return -EPROBE_DEFER; |
1708 | } | 1710 | } |
1709 | 1711 | ||
1710 | ret = clk_prepare(mclk); | ||
1711 | if (ret) { | ||
1712 | dev_err(&pdev->dev, "Unable to prepare mclk %p\n", mclk); | ||
1713 | goto err_dma_release; | ||
1714 | } | ||
1715 | |||
1716 | spin_lock_init(&dcmi->irqlock); | 1712 | spin_lock_init(&dcmi->irqlock); |
1717 | mutex_init(&dcmi->lock); | 1713 | mutex_init(&dcmi->lock); |
1718 | init_completion(&dcmi->complete); | 1714 | init_completion(&dcmi->complete); |
@@ -1728,7 +1724,7 @@ static int dcmi_probe(struct platform_device *pdev) | |||
1728 | /* Initialize the top-level structure */ | 1724 | /* Initialize the top-level structure */ |
1729 | ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); | 1725 | ret = v4l2_device_register(&pdev->dev, &dcmi->v4l2_dev); |
1730 | if (ret) | 1726 | if (ret) |
1731 | goto err_clk_unprepare; | 1727 | goto err_dma_release; |
1732 | 1728 | ||
1733 | dcmi->vdev = video_device_alloc(); | 1729 | dcmi->vdev = video_device_alloc(); |
1734 | if (!dcmi->vdev) { | 1730 | if (!dcmi->vdev) { |
@@ -1788,14 +1784,15 @@ static int dcmi_probe(struct platform_device *pdev) | |||
1788 | dev_info(&pdev->dev, "Probe done\n"); | 1784 | dev_info(&pdev->dev, "Probe done\n"); |
1789 | 1785 | ||
1790 | platform_set_drvdata(pdev, dcmi); | 1786 | platform_set_drvdata(pdev, dcmi); |
1787 | |||
1788 | pm_runtime_enable(&pdev->dev); | ||
1789 | |||
1791 | return 0; | 1790 | return 0; |
1792 | 1791 | ||
1793 | err_device_release: | 1792 | err_device_release: |
1794 | video_device_release(dcmi->vdev); | 1793 | video_device_release(dcmi->vdev); |
1795 | err_device_unregister: | 1794 | err_device_unregister: |
1796 | v4l2_device_unregister(&dcmi->v4l2_dev); | 1795 | v4l2_device_unregister(&dcmi->v4l2_dev); |
1797 | err_clk_unprepare: | ||
1798 | clk_unprepare(dcmi->mclk); | ||
1799 | err_dma_release: | 1796 | err_dma_release: |
1800 | dma_release_channel(dcmi->dma_chan); | 1797 | dma_release_channel(dcmi->dma_chan); |
1801 | 1798 | ||
@@ -1806,20 +1803,72 @@ static int dcmi_remove(struct platform_device *pdev) | |||
1806 | { | 1803 | { |
1807 | struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); | 1804 | struct stm32_dcmi *dcmi = platform_get_drvdata(pdev); |
1808 | 1805 | ||
1806 | pm_runtime_disable(&pdev->dev); | ||
1807 | |||
1809 | v4l2_async_notifier_unregister(&dcmi->notifier); | 1808 | v4l2_async_notifier_unregister(&dcmi->notifier); |
1810 | v4l2_device_unregister(&dcmi->v4l2_dev); | 1809 | v4l2_device_unregister(&dcmi->v4l2_dev); |
1811 | clk_unprepare(dcmi->mclk); | 1810 | |
1812 | dma_release_channel(dcmi->dma_chan); | 1811 | dma_release_channel(dcmi->dma_chan); |
1813 | 1812 | ||
1814 | return 0; | 1813 | return 0; |
1815 | } | 1814 | } |
1816 | 1815 | ||
1816 | static __maybe_unused int dcmi_runtime_suspend(struct device *dev) | ||
1817 | { | ||
1818 | struct stm32_dcmi *dcmi = dev_get_drvdata(dev); | ||
1819 | |||
1820 | clk_disable_unprepare(dcmi->mclk); | ||
1821 | |||
1822 | return 0; | ||
1823 | } | ||
1824 | |||
1825 | static __maybe_unused int dcmi_runtime_resume(struct device *dev) | ||
1826 | { | ||
1827 | struct stm32_dcmi *dcmi = dev_get_drvdata(dev); | ||
1828 | int ret; | ||
1829 | |||
1830 | ret = clk_prepare_enable(dcmi->mclk); | ||
1831 | if (ret) | ||
1832 | dev_err(dev, "%s: Failed to prepare_enable clock\n", __func__); | ||
1833 | |||
1834 | return ret; | ||
1835 | } | ||
1836 | |||
1837 | static __maybe_unused int dcmi_suspend(struct device *dev) | ||
1838 | { | ||
1839 | /* disable clock */ | ||
1840 | pm_runtime_force_suspend(dev); | ||
1841 | |||
1842 | /* change pinctrl state */ | ||
1843 | pinctrl_pm_select_sleep_state(dev); | ||
1844 | |||
1845 | return 0; | ||
1846 | } | ||
1847 | |||
1848 | static __maybe_unused int dcmi_resume(struct device *dev) | ||
1849 | { | ||
1850 | /* restore pinctl default state */ | ||
1851 | pinctrl_pm_select_default_state(dev); | ||
1852 | |||
1853 | /* clock enable */ | ||
1854 | pm_runtime_force_resume(dev); | ||
1855 | |||
1856 | return 0; | ||
1857 | } | ||
1858 | |||
1859 | static const struct dev_pm_ops dcmi_pm_ops = { | ||
1860 | SET_SYSTEM_SLEEP_PM_OPS(dcmi_suspend, dcmi_resume) | ||
1861 | SET_RUNTIME_PM_OPS(dcmi_runtime_suspend, | ||
1862 | dcmi_runtime_resume, NULL) | ||
1863 | }; | ||
1864 | |||
1817 | static struct platform_driver stm32_dcmi_driver = { | 1865 | static struct platform_driver stm32_dcmi_driver = { |
1818 | .probe = dcmi_probe, | 1866 | .probe = dcmi_probe, |
1819 | .remove = dcmi_remove, | 1867 | .remove = dcmi_remove, |
1820 | .driver = { | 1868 | .driver = { |
1821 | .name = DRV_NAME, | 1869 | .name = DRV_NAME, |
1822 | .of_match_table = of_match_ptr(stm32_dcmi_of_match), | 1870 | .of_match_table = of_match_ptr(stm32_dcmi_of_match), |
1871 | .pm = &dcmi_pm_ops, | ||
1823 | }, | 1872 | }, |
1824 | }; | 1873 | }; |
1825 | 1874 | ||