diff options
| author | Thierry Reding <treding@nvidia.com> | 2015-08-07 10:00:43 -0400 |
|---|---|---|
| committer | Thierry Reding <treding@nvidia.com> | 2016-07-04 05:38:11 -0400 |
| commit | 5234549b93aa2ada9ee3d628b0e06bf291d97577 (patch) | |
| tree | 711bb1bd3e0b63901de390cc0a4ec3698b262f3b /drivers/gpu/drm/tegra | |
| parent | ef8187d752650fe79239c5de9efc906cb7f6b30d (diff) | |
drm/tegra: hdmi: Implement runtime PM
Use runtime PM to clock-(un)gate and (de)assert reset to the HDMI
controller. This ties in nicely with atomic DPMS in that a runtime PM
reference is taken before a pipe is enabled and dropped after it has
been shut down.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/gpu/drm/tegra')
| -rw-r--r-- | drivers/gpu/drm/tegra/hdmi.c | 135 |
1 files changed, 88 insertions, 47 deletions
diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 529768d977b8..db90ec782f53 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #include <linux/debugfs.h> | 11 | #include <linux/debugfs.h> |
| 12 | #include <linux/gpio.h> | 12 | #include <linux/gpio.h> |
| 13 | #include <linux/hdmi.h> | 13 | #include <linux/hdmi.h> |
| 14 | #include <linux/pm_runtime.h> | ||
| 14 | #include <linux/regulator/consumer.h> | 15 | #include <linux/regulator/consumer.h> |
| 15 | #include <linux/reset.h> | 16 | #include <linux/reset.h> |
| 16 | 17 | ||
| @@ -641,6 +642,29 @@ static void tegra_hdmi_enable_audio(struct tegra_hdmi *hdmi) | |||
| 641 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); | 642 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_HDMI_GENERIC_CTRL); |
| 642 | } | 643 | } |
| 643 | 644 | ||
| 645 | static void tegra_hdmi_write_eld(struct tegra_hdmi *hdmi) | ||
| 646 | { | ||
| 647 | size_t length = drm_eld_size(hdmi->output.connector.eld), i; | ||
| 648 | u32 value; | ||
| 649 | |||
| 650 | for (i = 0; i < length; i++) | ||
| 651 | tegra_hdmi_writel(hdmi, i << 8 | hdmi->output.connector.eld[i], | ||
| 652 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | ||
| 653 | |||
| 654 | /* | ||
| 655 | * The HDA codec will always report an ELD buffer size of 96 bytes and | ||
| 656 | * the HDA codec driver will check that each byte read from the buffer | ||
| 657 | * is valid. Therefore every byte must be written, even if no 96 bytes | ||
| 658 | * were parsed from EDID. | ||
| 659 | */ | ||
| 660 | for (i = length; i < HDMI_ELD_BUFFER_SIZE; i++) | ||
| 661 | tegra_hdmi_writel(hdmi, i << 8 | 0, | ||
| 662 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | ||
| 663 | |||
| 664 | value = SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT; | ||
| 665 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); | ||
| 666 | } | ||
| 667 | |||
| 644 | static inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size) | 668 | static inline u32 tegra_hdmi_subpack(const u8 *ptr, size_t size) |
| 645 | { | 669 | { |
| 646 | u32 value = 0; | 670 | u32 value = 0; |
| @@ -945,29 +969,11 @@ static void tegra_hdmi_encoder_disable(struct drm_encoder *encoder) | |||
| 945 | tegra_hdmi_disable_avi_infoframe(hdmi); | 969 | tegra_hdmi_disable_avi_infoframe(hdmi); |
| 946 | tegra_hdmi_disable_audio(hdmi); | 970 | tegra_hdmi_disable_audio(hdmi); |
| 947 | } | 971 | } |
| 948 | } | ||
| 949 | |||
| 950 | static void tegra_hdmi_write_eld(struct tegra_hdmi *hdmi) | ||
| 951 | { | ||
| 952 | size_t length = drm_eld_size(hdmi->output.connector.eld), i; | ||
| 953 | u32 value; | ||
| 954 | 972 | ||
| 955 | for (i = 0; i < length; i++) | 973 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE); |
| 956 | tegra_hdmi_writel(hdmi, i << 8 | hdmi->output.connector.eld[i], | 974 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK); |
| 957 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | ||
| 958 | |||
| 959 | /* | ||
| 960 | * The HDA codec will always report an ELD buffer size of 96 bytes and | ||
| 961 | * the HDA codec driver will check that each byte read from the buffer | ||
| 962 | * is valid. Therefore every byte must be written, even if no 96 bytes | ||
| 963 | * were parsed from EDID. | ||
| 964 | */ | ||
| 965 | for (i = length; i < HDMI_ELD_BUFFER_SIZE; i++) | ||
| 966 | tegra_hdmi_writel(hdmi, i << 8 | 0, | ||
| 967 | HDMI_NV_PDISP_SOR_AUDIO_HDA_ELD_BUFWR); | ||
| 968 | 975 | ||
| 969 | value = SOR_AUDIO_HDA_PRESENSE_VALID | SOR_AUDIO_HDA_PRESENSE_PRESENT; | 976 | pm_runtime_put(hdmi->dev); |
| 970 | tegra_hdmi_writel(hdmi, value, HDMI_NV_PDISP_SOR_AUDIO_HDA_PRESENSE); | ||
| 971 | } | 977 | } |
| 972 | 978 | ||
| 973 | static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | 979 | static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) |
| @@ -982,6 +988,16 @@ static void tegra_hdmi_encoder_enable(struct drm_encoder *encoder) | |||
| 982 | u32 value; | 988 | u32 value; |
| 983 | int err; | 989 | int err; |
| 984 | 990 | ||
| 991 | pm_runtime_get_sync(hdmi->dev); | ||
| 992 | |||
| 993 | /* | ||
| 994 | * Enable and unmask the HDA codec SCRATCH0 register interrupt. This | ||
| 995 | * is used for interoperability between the HDA codec driver and the | ||
| 996 | * HDMI driver. | ||
| 997 | */ | ||
| 998 | tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_ENABLE); | ||
| 999 | tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_MASK); | ||
| 1000 | |||
| 985 | hdmi->pixel_clock = mode->clock * 1000; | 1001 | hdmi->pixel_clock = mode->clock * 1000; |
| 986 | h_sync_width = mode->hsync_end - mode->hsync_start; | 1002 | h_sync_width = mode->hsync_end - mode->hsync_start; |
| 987 | h_back_porch = mode->htotal - mode->hsync_end; | 1003 | h_back_porch = mode->htotal - mode->hsync_end; |
| @@ -1507,22 +1523,6 @@ static int tegra_hdmi_init(struct host1x_client *client) | |||
| 1507 | return err; | 1523 | return err; |
| 1508 | } | 1524 | } |
| 1509 | 1525 | ||
| 1510 | err = clk_prepare_enable(hdmi->clk); | ||
| 1511 | if (err < 0) { | ||
| 1512 | dev_err(hdmi->dev, "failed to enable clock: %d\n", err); | ||
| 1513 | return err; | ||
| 1514 | } | ||
| 1515 | |||
| 1516 | reset_control_deassert(hdmi->rst); | ||
| 1517 | |||
| 1518 | /* | ||
| 1519 | * Enable and unmask the HDA codec SCRATCH0 register interrupt. This | ||
| 1520 | * is used for interoperability between the HDA codec driver and the | ||
| 1521 | * HDMI driver. | ||
| 1522 | */ | ||
| 1523 | tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_ENABLE); | ||
| 1524 | tegra_hdmi_writel(hdmi, INT_CODEC_SCRATCH0, HDMI_NV_PDISP_INT_MASK); | ||
| 1525 | |||
| 1526 | return 0; | 1526 | return 0; |
| 1527 | } | 1527 | } |
| 1528 | 1528 | ||
| @@ -1530,14 +1530,8 @@ static int tegra_hdmi_exit(struct host1x_client *client) | |||
| 1530 | { | 1530 | { |
| 1531 | struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); | 1531 | struct tegra_hdmi *hdmi = host1x_client_to_hdmi(client); |
| 1532 | 1532 | ||
| 1533 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_MASK); | ||
| 1534 | tegra_hdmi_writel(hdmi, 0, HDMI_NV_PDISP_INT_ENABLE); | ||
| 1535 | |||
| 1536 | tegra_output_exit(&hdmi->output); | 1533 | tegra_output_exit(&hdmi->output); |
| 1537 | 1534 | ||
| 1538 | reset_control_assert(hdmi->rst); | ||
| 1539 | clk_disable_unprepare(hdmi->clk); | ||
| 1540 | |||
| 1541 | regulator_disable(hdmi->vdd); | 1535 | regulator_disable(hdmi->vdd); |
| 1542 | regulator_disable(hdmi->pll); | 1536 | regulator_disable(hdmi->pll); |
| 1543 | regulator_disable(hdmi->hdmi); | 1537 | regulator_disable(hdmi->hdmi); |
| @@ -1752,6 +1746,9 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
| 1752 | return err; | 1746 | return err; |
| 1753 | } | 1747 | } |
| 1754 | 1748 | ||
| 1749 | platform_set_drvdata(pdev, hdmi); | ||
| 1750 | pm_runtime_enable(&pdev->dev); | ||
| 1751 | |||
| 1755 | INIT_LIST_HEAD(&hdmi->client.list); | 1752 | INIT_LIST_HEAD(&hdmi->client.list); |
| 1756 | hdmi->client.ops = &hdmi_client_ops; | 1753 | hdmi->client.ops = &hdmi_client_ops; |
| 1757 | hdmi->client.dev = &pdev->dev; | 1754 | hdmi->client.dev = &pdev->dev; |
| @@ -1763,8 +1760,6 @@ static int tegra_hdmi_probe(struct platform_device *pdev) | |||
| 1763 | return err; | 1760 | return err; |
| 1764 | } | 1761 | } |
| 1765 | 1762 | ||
| 1766 | platform_set_drvdata(pdev, hdmi); | ||
| 1767 | |||
| 1768 | return 0; | 1763 | return 0; |
| 1769 | } | 1764 | } |
| 1770 | 1765 | ||
| @@ -1773,6 +1768,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev) | |||
| 1773 | struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); | 1768 | struct tegra_hdmi *hdmi = platform_get_drvdata(pdev); |
| 1774 | int err; | 1769 | int err; |
| 1775 | 1770 | ||
| 1771 | pm_runtime_disable(&pdev->dev); | ||
| 1772 | |||
| 1776 | err = host1x_client_unregister(&hdmi->client); | 1773 | err = host1x_client_unregister(&hdmi->client); |
| 1777 | if (err < 0) { | 1774 | if (err < 0) { |
| 1778 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", | 1775 | dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", |
| @@ -1782,17 +1779,61 @@ static int tegra_hdmi_remove(struct platform_device *pdev) | |||
| 1782 | 1779 | ||
| 1783 | tegra_output_remove(&hdmi->output); | 1780 | tegra_output_remove(&hdmi->output); |
| 1784 | 1781 | ||
| 1785 | clk_disable_unprepare(hdmi->clk_parent); | 1782 | return 0; |
| 1783 | } | ||
| 1784 | |||
| 1785 | #ifdef CONFIG_PM | ||
| 1786 | static int tegra_hdmi_suspend(struct device *dev) | ||
| 1787 | { | ||
| 1788 | struct tegra_hdmi *hdmi = dev_get_drvdata(dev); | ||
| 1789 | int err; | ||
| 1790 | |||
| 1791 | err = reset_control_assert(hdmi->rst); | ||
| 1792 | if (err < 0) { | ||
| 1793 | dev_err(dev, "failed to assert reset: %d\n", err); | ||
| 1794 | return err; | ||
| 1795 | } | ||
| 1796 | |||
| 1797 | usleep_range(1000, 2000); | ||
| 1798 | |||
| 1786 | clk_disable_unprepare(hdmi->clk); | 1799 | clk_disable_unprepare(hdmi->clk); |
| 1787 | 1800 | ||
| 1788 | return 0; | 1801 | return 0; |
| 1789 | } | 1802 | } |
| 1790 | 1803 | ||
| 1804 | static int tegra_hdmi_resume(struct device *dev) | ||
| 1805 | { | ||
| 1806 | struct tegra_hdmi *hdmi = dev_get_drvdata(dev); | ||
| 1807 | int err; | ||
| 1808 | |||
| 1809 | err = clk_prepare_enable(hdmi->clk); | ||
| 1810 | if (err < 0) { | ||
| 1811 | dev_err(dev, "failed to enable clock: %d\n", err); | ||
| 1812 | return err; | ||
| 1813 | } | ||
| 1814 | |||
| 1815 | usleep_range(1000, 2000); | ||
| 1816 | |||
| 1817 | err = reset_control_deassert(hdmi->rst); | ||
| 1818 | if (err < 0) { | ||
| 1819 | dev_err(dev, "failed to deassert reset: %d\n", err); | ||
| 1820 | clk_disable_unprepare(hdmi->clk); | ||
| 1821 | return err; | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | return 0; | ||
| 1825 | } | ||
| 1826 | #endif | ||
| 1827 | |||
| 1828 | static const struct dev_pm_ops tegra_hdmi_pm_ops = { | ||
| 1829 | SET_RUNTIME_PM_OPS(tegra_hdmi_suspend, tegra_hdmi_resume, NULL) | ||
| 1830 | }; | ||
| 1831 | |||
| 1791 | struct platform_driver tegra_hdmi_driver = { | 1832 | struct platform_driver tegra_hdmi_driver = { |
| 1792 | .driver = { | 1833 | .driver = { |
| 1793 | .name = "tegra-hdmi", | 1834 | .name = "tegra-hdmi", |
| 1794 | .owner = THIS_MODULE, | ||
| 1795 | .of_match_table = tegra_hdmi_of_match, | 1835 | .of_match_table = tegra_hdmi_of_match, |
| 1836 | .pm = &tegra_hdmi_pm_ops, | ||
| 1796 | }, | 1837 | }, |
| 1797 | .probe = tegra_hdmi_probe, | 1838 | .probe = tegra_hdmi_probe, |
| 1798 | .remove = tegra_hdmi_remove, | 1839 | .remove = tegra_hdmi_remove, |
