xref: /linux/sound/soc/amd/acp7x/acp7x-common.c (revision 0eaed89c18aeedf0898baf2dbf5ff027c6795152)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * AMD ACP PCI driver callback routines for ACP7.x
4  * platforms.
5  *
6  * Copyright 2026 Advanced Micro Devices, Inc.
7  */
8 
9 #include <linux/device.h>
10 #include <linux/io.h>
11 #include <linux/iopoll.h>
12 #include <linux/types.h>
13 
14 #include "acp7x.h"
15 
16 static int acp7x_power_on(void __iomem *acp_base)
17 {
18 	u32 val = 0;
19 
20 	val = readl(acp_base + ACP_PGFSM_STATUS);
21 	if (!(val & ACP7X_PGFSM_STATUS_MASK))
22 		return 0;
23 
24 	writel(ACP7X_PGFSM_CNTL_POWER_ON_MASK, acp_base + ACP_PGFSM_CONTROL);
25 	val = readl(acp_base + ACP_PGFSM_CONTROL);
26 	return readl_poll_timeout(acp_base + ACP_PGFSM_STATUS, val,
27 				  ((val & ACP7X_PGFSM_STATUS_MASK) == 0), DELAY_US, ACP7X_TIMEOUT);
28 }
29 
30 static int acp7x_reset(void __iomem *acp_base)
31 {
32 	u32 val;
33 	int ret;
34 
35 	writel(1, acp_base + ACP_SOFT_RESET);
36 	ret = readl_poll_timeout(acp_base + ACP_SOFT_RESET, val,
37 				 val & ACP_SOFT_RESET_SOFTRESET_AUDDONE_MASK,
38 				 DELAY_US, ACP7X_TIMEOUT);
39 	if (ret)
40 		return ret;
41 
42 	writel(0, acp_base + ACP_SOFT_RESET);
43 	return readl_poll_timeout(acp_base + ACP_SOFT_RESET, val, !val, DELAY_US, ACP7X_TIMEOUT);
44 }
45 
46 static int acp7x_init(void __iomem *acp_base, struct device *dev)
47 {
48 	int ret;
49 
50 	ret = acp7x_power_on(acp_base);
51 	if (ret) {
52 		dev_err(dev, "ACP power on failed\n");
53 		return ret;
54 	}
55 	writel(0x01, acp_base + ACP_CONTROL);
56 	ret = acp7x_reset(acp_base);
57 	if (ret) {
58 		dev_err(dev, "ACP reset failed\n");
59 		return ret;
60 	}
61 	writel(0, acp_base + ACP_ZSC_DSP_CTRL);
62 	return 0;
63 }
64 
65 static int acp7x_deinit(void __iomem *acp_base, struct device *dev)
66 {
67 	int ret;
68 
69 	ret = acp7x_reset(acp_base);
70 	if (ret) {
71 		dev_err(dev, "ACP reset failed\n");
72 		return ret;
73 	}
74 	writel(0x01, acp_base + ACP_ZSC_DSP_CTRL);
75 	return 0;
76 }
77 
78 static int __maybe_unused snd_acp7x_suspend(struct device *dev)
79 {
80 	struct acp7x_dev_data *adata;
81 	int ret;
82 
83 	adata = dev_get_drvdata(dev);
84 	ret = acp_hw_deinit(adata, dev);
85 	if (ret)
86 		dev_err(dev, "ACP de-init failed\n");
87 	return ret;
88 }
89 
90 static int __maybe_unused snd_acp7x_runtime_resume(struct device *dev)
91 {
92 	struct acp7x_dev_data *adata;
93 	int ret;
94 
95 	adata = dev_get_drvdata(dev);
96 	ret = acp_hw_init(adata, dev);
97 	if (ret) {
98 		dev_err(dev, "ACP init failed\n");
99 		return ret;
100 	}
101 	return 0;
102 }
103 
104 static int __maybe_unused snd_acp7x_resume(struct device *dev)
105 {
106 	struct acp7x_dev_data *adata;
107 	int ret;
108 
109 	adata = dev_get_drvdata(dev);
110 	ret = acp_hw_init(adata, dev);
111 	if (ret)
112 		dev_err(dev, "ACP init failed\n");
113 
114 	return ret;
115 }
116 
117 void acp7x_hw_init_ops(struct acp_hw_ops *hw_ops)
118 {
119 	hw_ops->acp_init = acp7x_init;
120 	hw_ops->acp_deinit = acp7x_deinit;
121 	hw_ops->acp_suspend = snd_acp7x_suspend;
122 	hw_ops->acp_resume = snd_acp7x_resume;
123 	hw_ops->acp_suspend_runtime = snd_acp7x_suspend;
124 	hw_ops->acp_resume_runtime = snd_acp7x_runtime_resume;
125 }
126