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
mt8195_adsp_init_clock(struct snd_sof_dev * sdev)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
adsp_enable_all_clock(struct snd_sof_dev * sdev)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
adsp_disable_all_clock(struct snd_sof_dev * sdev)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
adsp_default_clk_init(struct snd_sof_dev * sdev,bool enable)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
adsp_clock_on(struct snd_sof_dev * sdev)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
adsp_clock_off(struct snd_sof_dev * sdev)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