1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause)
2 //
3 // Copyright(c) 2021 Mediatek Corporation. All rights reserved.
4 //
5 // Author: YC Hung <yc.hung@mediatek.com>
6 //
7 // Hardware interface for mt8195 DSP clock
8 
9 #include <linux/clk.h>
10 #include <linux/io.h>
11 #include "mt8195.h"
12 #include "mt8195-clk.h"
13 #include "../adsp_helper.h"
14 #include "../../sof-audio.h"
15 
16 static const char *adsp_clks[ADSP_CLK_MAX] = {
17 	[CLK_TOP_ADSP] = "adsp_sel",
18 	[CLK_TOP_CLK26M] = "clk26m_ck",
19 	[CLK_TOP_AUDIO_LOCAL_BUS] = "audio_local_bus",
20 	[CLK_TOP_MAINPLL_D7_D2] = "mainpll_d7_d2",
21 	[CLK_SCP_ADSP_AUDIODSP] = "scp_adsp_audiodsp",
22 	[CLK_TOP_AUDIO_H] = "audio_h",
23 };
24 
25 int mt8195_adsp_init_clock(struct snd_sof_dev *sdev)
26 {
27 	struct device *dev = sdev->dev;
28 	struct adsp_priv *priv = sdev->pdata->hw_pdata;
29 	int i;
30 
31 	priv->clk = devm_kcalloc(dev, ADSP_CLK_MAX, sizeof(*priv->clk), GFP_KERNEL);
32 
33 	if (!priv->clk)
34 		return -ENOMEM;
35 
36 	for (i = 0; i < ADSP_CLK_MAX; i++) {
37 		priv->clk[i] = devm_clk_get(dev, adsp_clks[i]);
38 		if (IS_ERR(priv->clk[i]))
39 			return PTR_ERR(priv->clk[i]);
40 	}
41 
42 	return 0;
43 }
44 
45 static int adsp_enable_all_clock(struct snd_sof_dev *sdev)
46 {
47 	struct device *dev = sdev->dev;
48 	struct adsp_priv *priv = sdev->pdata->hw_pdata;
49 	int ret;
50 
51 	ret = clk_prepare_enable(priv->clk[CLK_TOP_MAINPLL_D7_D2]);
52 	if (ret) {
53 		dev_err(dev, "%s clk_prepare_enable(mainpll_d7_d2) fail %d\n",
54 			__func__, ret);
55 		return ret;
56 	}
57 
58 	ret = clk_prepare_enable(priv->clk[CLK_TOP_ADSP]);
59 	if (ret) {
60 		dev_err(dev, "%s clk_prepare_enable(adsp_sel) fail %d\n",
61 			__func__, ret);
62 		goto disable_mainpll_d7_d2_clk;
63 	}
64 
65 	ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]);
66 	if (ret) {
67 		dev_err(dev, "%s clk_prepare_enable(audio_local_bus) fail %d\n",
68 			__func__, ret);
69 		goto disable_dsp_sel_clk;
70 	}
71 
72 	ret = clk_prepare_enable(priv->clk[CLK_SCP_ADSP_AUDIODSP]);
73 	if (ret) {
74 		dev_err(dev, "%s clk_prepare_enable(scp_adsp_audiodsp) fail %d\n",
75 			__func__, ret);
76 		goto disable_audio_local_bus_clk;
77 	}
78 
79 	ret = clk_prepare_enable(priv->clk[CLK_TOP_AUDIO_H]);
80 	if (ret) {
81 		dev_err(dev, "%s clk_prepare_enable(audio_h) fail %d\n",
82 			__func__, ret);
83 		goto disable_scp_adsp_audiodsp_clk;
84 	}
85 
86 	return 0;
87 
88 disable_scp_adsp_audiodsp_clk:
89 	clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]);
90 disable_audio_local_bus_clk:
91 	clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]);
92 disable_dsp_sel_clk:
93 	clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]);
94 disable_mainpll_d7_d2_clk:
95 	clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]);
96 
97 	return ret;
98 }
99 
100 static void adsp_disable_all_clock(struct snd_sof_dev *sdev)
101 {
102 	struct adsp_priv *priv = sdev->pdata->hw_pdata;
103 
104 	clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_H]);
105 	clk_disable_unprepare(priv->clk[CLK_SCP_ADSP_AUDIODSP]);
106 	clk_disable_unprepare(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS]);
107 	clk_disable_unprepare(priv->clk[CLK_TOP_ADSP]);
108 	clk_disable_unprepare(priv->clk[CLK_TOP_MAINPLL_D7_D2]);
109 }
110 
111 static int adsp_default_clk_init(struct snd_sof_dev *sdev, bool enable)
112 {
113 	struct device *dev = sdev->dev;
114 	struct adsp_priv *priv = sdev->pdata->hw_pdata;
115 	int ret;
116 
117 	dev_dbg(dev, "%s: %s\n", __func__, enable ? "on" : "off");
118 
119 	if (enable) {
120 		ret = clk_set_parent(priv->clk[CLK_TOP_ADSP],
121 				     priv->clk[CLK_TOP_CLK26M]);
122 		if (ret) {
123 			dev_err(dev, "failed to set dsp_sel to clk26m: %d\n", ret);
124 			return ret;
125 		}
126 
127 		ret = clk_set_parent(priv->clk[CLK_TOP_AUDIO_LOCAL_BUS],
128 				     priv->clk[CLK_TOP_MAINPLL_D7_D2]);
129 		if (ret) {
130 			dev_err(dev, "set audio_local_bus failed %d\n", ret);
131 			return ret;
132 		}
133 
134 		ret = clk_set_parent(priv->clk[CLK_TOP_AUDIO_H],
135 				     priv->clk[CLK_TOP_CLK26M]);
136 		if (ret) {
137 			dev_err(dev, "set audio_h_sel failed %d\n", ret);
138 			return ret;
139 		}
140 
141 		ret = adsp_enable_all_clock(sdev);
142 		if (ret) {
143 			dev_err(dev, "failed to adsp_enable_clock: %d\n", ret);
144 			return ret;
145 		}
146 	} else {
147 		adsp_disable_all_clock(sdev);
148 	}
149 
150 	return 0;
151 }
152 
153 int adsp_clock_on(struct snd_sof_dev *sdev)
154 {
155 	/* Open ADSP clock */
156 	return adsp_default_clk_init(sdev, 1);
157 }
158 
159 int adsp_clock_off(struct snd_sof_dev *sdev)
160 {
161 	/* Close ADSP clock */
162 	return adsp_default_clk_init(sdev, 0);
163 }
164 
165