Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

soundwire: amd: add runtime pm ops for AMD SoundWire manager driver

Add support for runtime pm ops for AMD SoundWire manager driver.

Signed-off-by: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
Signed-off-by: Mastan Katragadda <Mastan.Katragadda@amd.com>
Reviewed-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Link: https://lore.kernel.org/lkml/20230227154801.50319-7-Vijendar.Mukunda@amd.com
Link: https://lore.kernel.org/r/20230321050901.115439-7-Vijendar.Mukunda@amd.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>

authored by

Vijendar Mukunda and committed by
Vinod Koul
81ff58ff 65f93e40

+160
+140
drivers/soundwire/amd_manager.c
··· 14 14 #include <linux/slab.h> 15 15 #include <linux/soundwire/sdw.h> 16 16 #include <linux/soundwire/sdw_registers.h> 17 + #include <linux/pm_runtime.h> 17 18 #include <linux/wait.h> 18 19 #include <sound/pcm_params.h> 19 20 #include <sound/soc.h> ··· 132 131 writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_0TO7); 133 132 writel(0x00, amd_manager->mmio + ACP_SW_STATE_CHANGE_STATUS_MASK_8TO11); 134 133 writel(0x00, amd_manager->mmio + ACP_SW_ERROR_INTR_MASK); 134 + } 135 + 136 + static int amd_deinit_sdw_manager(struct amd_sdw_manager *amd_manager) 137 + { 138 + amd_disable_sdw_interrupts(amd_manager); 139 + return amd_disable_sdw_manager(amd_manager); 135 140 } 136 141 137 142 static void amd_sdw_set_frameshape(struct amd_sdw_manager *amd_manager) ··· 873 866 return; 874 867 amd_sdw_set_frameshape(amd_manager); 875 868 } 869 + /* Enable runtime PM */ 870 + pm_runtime_set_autosuspend_delay(amd_manager->dev, AMD_SDW_MASTER_SUSPEND_DELAY_MS); 871 + pm_runtime_use_autosuspend(amd_manager->dev); 872 + pm_runtime_mark_last_busy(amd_manager->dev); 873 + pm_runtime_set_active(amd_manager->dev); 874 + pm_runtime_enable(amd_manager->dev); 876 875 } 877 876 878 877 static int amd_sdw_manager_probe(struct platform_device *pdev) ··· 966 953 { 967 954 struct amd_sdw_manager *amd_manager = dev_get_drvdata(&pdev->dev); 968 955 956 + pm_runtime_disable(&pdev->dev); 969 957 cancel_work_sync(&amd_manager->probe_work); 970 958 amd_disable_sdw_interrupts(amd_manager); 971 959 sdw_bus_master_delete(&amd_manager->bus); 972 960 return amd_disable_sdw_manager(amd_manager); 973 961 } 974 962 963 + static int amd_sdw_clock_stop(struct amd_sdw_manager *amd_manager) 964 + { 965 + u32 val; 966 + int ret; 967 + 968 + ret = sdw_bus_prep_clk_stop(&amd_manager->bus); 969 + if (ret < 0 && ret != -ENODATA) { 970 + dev_err(amd_manager->dev, "prepare clock stop failed %d", ret); 971 + return 0; 972 + } 973 + ret = sdw_bus_clk_stop(&amd_manager->bus); 974 + if (ret < 0 && ret != -ENODATA) { 975 + dev_err(amd_manager->dev, "bus clock stop failed %d", ret); 976 + return 0; 977 + } 978 + 979 + ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val, 980 + (val & AMD_SDW_CLK_STOP_DONE), ACP_DELAY_US, AMD_SDW_TIMEOUT); 981 + if (ret) { 982 + dev_err(amd_manager->dev, "SDW%x clock stop failed\n", amd_manager->instance); 983 + return 0; 984 + } 985 + 986 + amd_manager->clk_stopped = true; 987 + if (amd_manager->wake_en_mask) 988 + writel(0x01, amd_manager->acp_mmio + ACP_SW_WAKE_EN(amd_manager->instance)); 989 + 990 + dev_dbg(amd_manager->dev, "SDW%x clock stop successful\n", amd_manager->instance); 991 + return 0; 992 + } 993 + 994 + static int amd_sdw_clock_stop_exit(struct amd_sdw_manager *amd_manager) 995 + { 996 + int ret; 997 + u32 val; 998 + 999 + if (amd_manager->clk_stopped) { 1000 + val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); 1001 + val |= AMD_SDW_CLK_RESUME_REQ; 1002 + writel(val, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); 1003 + ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val, 1004 + (val & AMD_SDW_CLK_RESUME_DONE), ACP_DELAY_US, 1005 + AMD_SDW_TIMEOUT); 1006 + if (val & AMD_SDW_CLK_RESUME_DONE) { 1007 + writel(0, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); 1008 + ret = sdw_bus_exit_clk_stop(&amd_manager->bus); 1009 + if (ret < 0) 1010 + dev_err(amd_manager->dev, "bus failed to exit clock stop %d\n", 1011 + ret); 1012 + amd_manager->clk_stopped = false; 1013 + } 1014 + } 1015 + if (amd_manager->clk_stopped) { 1016 + dev_err(amd_manager->dev, "SDW%x clock stop exit failed\n", amd_manager->instance); 1017 + return 0; 1018 + } 1019 + dev_dbg(amd_manager->dev, "SDW%x clock stop exit successful\n", amd_manager->instance); 1020 + return 0; 1021 + } 1022 + 1023 + static int __maybe_unused amd_suspend_runtime(struct device *dev) 1024 + { 1025 + struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev); 1026 + struct sdw_bus *bus = &amd_manager->bus; 1027 + int ret; 1028 + 1029 + if (bus->prop.hw_disabled) { 1030 + dev_dbg(bus->dev, "SoundWire manager %d is disabled,\n", 1031 + bus->link_id); 1032 + return 0; 1033 + } 1034 + if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) { 1035 + return amd_sdw_clock_stop(amd_manager); 1036 + } else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) { 1037 + ret = amd_sdw_clock_stop(amd_manager); 1038 + if (ret) 1039 + return ret; 1040 + return amd_deinit_sdw_manager(amd_manager); 1041 + } 1042 + return 0; 1043 + } 1044 + 1045 + static int __maybe_unused amd_resume_runtime(struct device *dev) 1046 + { 1047 + struct amd_sdw_manager *amd_manager = dev_get_drvdata(dev); 1048 + struct sdw_bus *bus = &amd_manager->bus; 1049 + int ret; 1050 + u32 val; 1051 + 1052 + if (bus->prop.hw_disabled) { 1053 + dev_dbg(bus->dev, "SoundWire manager %d is disabled, ignoring\n", 1054 + bus->link_id); 1055 + return 0; 1056 + } 1057 + 1058 + if (amd_manager->power_mode_mask & AMD_SDW_CLK_STOP_MODE) { 1059 + return amd_sdw_clock_stop_exit(amd_manager); 1060 + } else if (amd_manager->power_mode_mask & AMD_SDW_POWER_OFF_MODE) { 1061 + val = readl(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); 1062 + if (val) { 1063 + val |= AMD_SDW_CLK_RESUME_REQ; 1064 + writel(val, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); 1065 + ret = readl_poll_timeout(amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL, val, 1066 + (val & AMD_SDW_CLK_RESUME_DONE), ACP_DELAY_US, 1067 + AMD_SDW_TIMEOUT); 1068 + if (val & AMD_SDW_CLK_RESUME_DONE) { 1069 + writel(0, amd_manager->mmio + ACP_SW_CLK_RESUME_CTRL); 1070 + amd_manager->clk_stopped = false; 1071 + } 1072 + } 1073 + sdw_clear_slave_status(bus, SDW_UNATTACH_REQUEST_MASTER_RESET); 1074 + amd_init_sdw_manager(amd_manager); 1075 + amd_enable_sdw_interrupts(amd_manager); 1076 + ret = amd_enable_sdw_manager(amd_manager); 1077 + if (ret) 1078 + return ret; 1079 + amd_sdw_set_frameshape(amd_manager); 1080 + } 1081 + return 0; 1082 + } 1083 + 1084 + static const struct dev_pm_ops amd_pm = { 1085 + SET_RUNTIME_PM_OPS(amd_suspend_runtime, amd_resume_runtime, NULL) 1086 + }; 1087 + 975 1088 static struct platform_driver amd_sdw_driver = { 976 1089 .probe = &amd_sdw_manager_probe, 977 1090 .remove = &amd_sdw_manager_remove, 978 1091 .driver = { 979 1092 .name = "amd_sdw_manager", 1093 + .pm = &amd_pm, 980 1094 } 981 1095 }; 982 1096 module_platform_driver(amd_sdw_driver);
+3
drivers/soundwire/amd_manager.h
··· 186 186 #define AMD_SDW0_PAD_KEEPER_DISABLE_MASK 0x1e 187 187 #define AMD_SDW1_PAD_KEEPER_DISABLE_MASK 0xf 188 188 #define AMD_SDW_PREQ_INTR_STAT BIT(19) 189 + #define AMD_SDW_CLK_STOP_DONE 1 190 + #define AMD_SDW_CLK_RESUME_REQ 2 191 + #define AMD_SDW_CLK_RESUME_DONE 3 189 192 190 193 static u32 amd_sdw_freq_tbl[AMD_SDW_MAX_FREQ_NUM] = { 191 194 AMD_SDW_DEFAULT_CLK_FREQ,
+17
include/linux/soundwire/sdw_amd.h
··· 8 8 9 9 #include <linux/soundwire/sdw.h> 10 10 11 + /* AMD pm_runtime quirk definitions */ 12 + 13 + /* 14 + * Force the clock to stop(ClockStopMode0) when suspend callback 15 + * is invoked. 16 + */ 17 + #define AMD_SDW_CLK_STOP_MODE 1 18 + 19 + /* 20 + * Stop the bus when runtime suspend/system level suspend callback 21 + * is invoked. If set, a complete bus reset and re-enumeration will 22 + * be performed when the bus restarts. In-band wake interrupts are 23 + * not supported in this mode. 24 + */ 25 + #define AMD_SDW_POWER_OFF_MODE 2 11 26 #define ACP_SDW0 0 12 27 #define ACP_SDW1 1 13 28 ··· 72 57 * @instance: SoundWire manager instance 73 58 * @quirks: SoundWire manager quirks 74 59 * @wake_en_mask: wake enable mask per SoundWire manager 60 + * @clk_stopped: flag set to true when clock is stopped 75 61 * @power_mode_mask: flag interprets amd SoundWire manager power mode 76 62 * @dai_runtime_array: dai runtime array 77 63 */ ··· 102 86 u32 quirks; 103 87 u32 wake_en_mask; 104 88 u32 power_mode_mask; 89 + bool clk_stopped; 105 90 106 91 struct sdw_amd_dai_runtime **dai_runtime_array; 107 92 };