ASoC: amd: add RPL Platform pci driver pm-ops
[linux-2.6-microblaze.git] / sound / soc / amd / rpl / rpl-pci-acp6x.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * AMD RPL ACP PCI Driver
4  *
5  * Copyright 2022 Advanced Micro Devices, Inc.
6  */
7
8 #include <linux/pci.h>
9 #include <linux/module.h>
10 #include <linux/io.h>
11 #include <linux/delay.h>
12 #include <linux/platform_device.h>
13 #include <linux/pm_runtime.h>
14
15 #include "rpl_acp6x.h"
16
17 struct rpl_dev_data {
18         void __iomem *acp6x_base;
19 };
20
21 static int rpl_power_on(void __iomem *acp_base)
22 {
23         u32 val;
24         int timeout;
25
26         val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
27
28         if (!val)
29                 return val;
30
31         if ((val & ACP_PGFSM_STATUS_MASK) != ACP_POWER_ON_IN_PROGRESS)
32                 rpl_acp_writel(ACP_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
33         timeout = 0;
34         while (++timeout < 500) {
35                 val = rpl_acp_readl(acp_base + ACP_PGFSM_STATUS);
36                 if (!val)
37                         return 0;
38                 udelay(1);
39         }
40         return -ETIMEDOUT;
41 }
42
43 static int rpl_reset(void __iomem *acp_base)
44 {
45         u32 val;
46         int timeout;
47
48         rpl_acp_writel(1, acp_base + ACP_SOFT_RESET);
49         timeout = 0;
50         while (++timeout < 500) {
51                 val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
52                 if (val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK)
53                         break;
54                 cpu_relax();
55         }
56         rpl_acp_writel(0, acp_base + ACP_SOFT_RESET);
57         timeout = 0;
58         while (++timeout < 500) {
59                 val = rpl_acp_readl(acp_base + ACP_SOFT_RESET);
60                 if (!val)
61                         return 0;
62                 cpu_relax();
63         }
64         return -ETIMEDOUT;
65 }
66
67 static int rpl_init(void __iomem *acp_base)
68 {
69         int ret;
70
71         /* power on */
72         ret = rpl_power_on(acp_base);
73         if (ret) {
74                 pr_err("ACP power on failed\n");
75                 return ret;
76         }
77         rpl_acp_writel(0x01, acp_base + ACP_CONTROL);
78         /* Reset */
79         ret = rpl_reset(acp_base);
80         if (ret) {
81                 pr_err("ACP reset failed\n");
82                 return ret;
83         }
84         rpl_acp_writel(0x03, acp_base + ACP_CLKMUX_SEL);
85         return 0;
86 }
87
88 static int rpl_deinit(void __iomem *acp_base)
89 {
90         int ret;
91
92         /* Reset */
93         ret = rpl_reset(acp_base);
94         if (ret) {
95                 pr_err("ACP reset failed\n");
96                 return ret;
97         }
98         rpl_acp_writel(0x00, acp_base + ACP_CLKMUX_SEL);
99         rpl_acp_writel(0x00, acp_base + ACP_CONTROL);
100         return 0;
101 }
102
103 static int snd_rpl_probe(struct pci_dev *pci,
104                          const struct pci_device_id *pci_id)
105 {
106         struct rpl_dev_data *adata;
107         u32 addr;
108         int ret;
109
110         /* RPL device check */
111         switch (pci->revision) {
112         case 0x62:
113                 break;
114         default:
115                 dev_dbg(&pci->dev, "acp6x pci device not found\n");
116                 return -ENODEV;
117         }
118         if (pci_enable_device(pci)) {
119                 dev_err(&pci->dev, "pci_enable_device failed\n");
120                 return -ENODEV;
121         }
122
123         ret = pci_request_regions(pci, "AMD ACP6x audio");
124         if (ret < 0) {
125                 dev_err(&pci->dev, "pci_request_regions failed\n");
126                 goto disable_pci;
127         }
128
129         adata = devm_kzalloc(&pci->dev, sizeof(struct rpl_dev_data),
130                              GFP_KERNEL);
131         if (!adata) {
132                 ret = -ENOMEM;
133                 goto release_regions;
134         }
135
136         addr = pci_resource_start(pci, 0);
137         adata->acp6x_base = devm_ioremap(&pci->dev, addr,
138                                          pci_resource_len(pci, 0));
139         if (!adata->acp6x_base) {
140                 ret = -ENOMEM;
141                 goto release_regions;
142         }
143         pci_set_master(pci);
144         pci_set_drvdata(pci, adata);
145         ret = rpl_init(adata->acp6x_base);
146         if (ret)
147                 goto release_regions;
148         pm_runtime_set_autosuspend_delay(&pci->dev, ACP_SUSPEND_DELAY_MS);
149         pm_runtime_use_autosuspend(&pci->dev);
150         pm_runtime_put_noidle(&pci->dev);
151         pm_runtime_allow(&pci->dev);
152
153         return 0;
154 release_regions:
155         pci_release_regions(pci);
156 disable_pci:
157         pci_disable_device(pci);
158
159         return ret;
160 }
161
162 static int __maybe_unused snd_rpl_suspend(struct device *dev)
163 {
164         struct rpl_dev_data *adata;
165         int ret;
166
167         adata = dev_get_drvdata(dev);
168         ret = rpl_deinit(adata->acp6x_base);
169         if (ret)
170                 dev_err(dev, "ACP de-init failed\n");
171         return ret;
172 }
173
174 static int __maybe_unused snd_rpl_resume(struct device *dev)
175 {
176         struct rpl_dev_data *adata;
177         int ret;
178
179         adata = dev_get_drvdata(dev);
180         ret = rpl_init(adata->acp6x_base);
181         if (ret)
182                 dev_err(dev, "ACP init failed\n");
183         return ret;
184 }
185
186 static const struct dev_pm_ops rpl_pm = {
187         SET_RUNTIME_PM_OPS(snd_rpl_suspend, snd_rpl_resume, NULL)
188         SET_SYSTEM_SLEEP_PM_OPS(snd_rpl_suspend, snd_rpl_resume)
189 };
190
191 static void snd_rpl_remove(struct pci_dev *pci)
192 {
193         struct rpl_dev_data *adata;
194         int ret;
195
196         adata = pci_get_drvdata(pci);
197         ret = rpl_deinit(adata->acp6x_base);
198         if (ret)
199                 dev_err(&pci->dev, "ACP de-init failed\n");
200         pm_runtime_forbid(&pci->dev);
201         pm_runtime_get_noresume(&pci->dev);
202         pci_release_regions(pci);
203         pci_disable_device(pci);
204 }
205
206 static const struct pci_device_id snd_rpl_ids[] = {
207         { PCI_DEVICE(PCI_VENDOR_ID_AMD, ACP_DEVICE_ID),
208         .class = PCI_CLASS_MULTIMEDIA_OTHER << 8,
209         .class_mask = 0xffffff },
210         { 0, },
211 };
212 MODULE_DEVICE_TABLE(pci, snd_rpl_ids);
213
214 static struct pci_driver rpl_acp6x_driver  = {
215         .name = KBUILD_MODNAME,
216         .id_table = snd_rpl_ids,
217         .probe = snd_rpl_probe,
218         .remove = snd_rpl_remove,
219         .driver = {
220                 .pm = &rpl_pm,
221         }
222 };
223
224 module_pci_driver(rpl_acp6x_driver);
225
226 MODULE_DESCRIPTION("AMD ACP RPL PCI driver");
227 MODULE_LICENSE("GPL v2");