1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * tegra_asoc_utils.c - Harmony machine ASoC driver
4  *
5  * Author: Stephen Warren <swarren@nvidia.com>
6  * Copyright (C) 2010,2012 - NVIDIA, Inc.
7  */
8 
9 #include <linux/clk.h>
10 #include <linux/device.h>
11 #include <linux/err.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/of.h>
15 
16 #include "tegra_asoc_utils.h"
17 
tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data * data,int srate,int mclk)18 int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
19 			      int mclk)
20 {
21 	int new_baseclock;
22 	bool clk_change;
23 	int err;
24 
25 	switch (srate) {
26 	case 11025:
27 	case 22050:
28 	case 44100:
29 	case 88200:
30 		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
31 			new_baseclock = 56448000;
32 		else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
33 			new_baseclock = 564480000;
34 		else
35 			new_baseclock = 282240000;
36 		break;
37 	case 8000:
38 	case 16000:
39 	case 32000:
40 	case 48000:
41 	case 64000:
42 	case 96000:
43 		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
44 			new_baseclock = 73728000;
45 		else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
46 			new_baseclock = 552960000;
47 		else
48 			new_baseclock = 368640000;
49 		break;
50 	default:
51 		return -EINVAL;
52 	}
53 
54 	clk_change = ((new_baseclock != data->set_baseclock) ||
55 			(mclk != data->set_mclk));
56 	if (!clk_change)
57 		return 0;
58 
59 	data->set_baseclock = 0;
60 	data->set_mclk = 0;
61 
62 	clk_disable_unprepare(data->clk_cdev1);
63 	clk_disable_unprepare(data->clk_pll_a_out0);
64 	clk_disable_unprepare(data->clk_pll_a);
65 
66 	err = clk_set_rate(data->clk_pll_a, new_baseclock);
67 	if (err) {
68 		dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
69 		return err;
70 	}
71 
72 	err = clk_set_rate(data->clk_pll_a_out0, mclk);
73 	if (err) {
74 		dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
75 		return err;
76 	}
77 
78 	/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
79 
80 	err = clk_prepare_enable(data->clk_pll_a);
81 	if (err) {
82 		dev_err(data->dev, "Can't enable pll_a: %d\n", err);
83 		return err;
84 	}
85 
86 	err = clk_prepare_enable(data->clk_pll_a_out0);
87 	if (err) {
88 		dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
89 		return err;
90 	}
91 
92 	err = clk_prepare_enable(data->clk_cdev1);
93 	if (err) {
94 		dev_err(data->dev, "Can't enable cdev1: %d\n", err);
95 		return err;
96 	}
97 
98 	data->set_baseclock = new_baseclock;
99 	data->set_mclk = mclk;
100 
101 	return 0;
102 }
103 EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
104 
tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data * data)105 int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
106 {
107 	const int pll_rate = 73728000;
108 	const int ac97_rate = 24576000;
109 	int err;
110 
111 	clk_disable_unprepare(data->clk_cdev1);
112 	clk_disable_unprepare(data->clk_pll_a_out0);
113 	clk_disable_unprepare(data->clk_pll_a);
114 
115 	/*
116 	 * AC97 rate is fixed at 24.576MHz and is used for both the host
117 	 * controller and the external codec
118 	 */
119 	err = clk_set_rate(data->clk_pll_a, pll_rate);
120 	if (err) {
121 		dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
122 		return err;
123 	}
124 
125 	err = clk_set_rate(data->clk_pll_a_out0, ac97_rate);
126 	if (err) {
127 		dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
128 		return err;
129 	}
130 
131 	/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
132 
133 	err = clk_prepare_enable(data->clk_pll_a);
134 	if (err) {
135 		dev_err(data->dev, "Can't enable pll_a: %d\n", err);
136 		return err;
137 	}
138 
139 	err = clk_prepare_enable(data->clk_pll_a_out0);
140 	if (err) {
141 		dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
142 		return err;
143 	}
144 
145 	err = clk_prepare_enable(data->clk_cdev1);
146 	if (err) {
147 		dev_err(data->dev, "Can't enable cdev1: %d\n", err);
148 		return err;
149 	}
150 
151 	data->set_baseclock = pll_rate;
152 	data->set_mclk = ac97_rate;
153 
154 	return 0;
155 }
156 EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
157 
tegra_asoc_utils_init(struct tegra_asoc_utils_data * data,struct device * dev)158 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
159 			  struct device *dev)
160 {
161 	int ret;
162 
163 	data->dev = dev;
164 
165 	if (of_machine_is_compatible("nvidia,tegra20"))
166 		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
167 	else if (of_machine_is_compatible("nvidia,tegra30"))
168 		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
169 	else if (of_machine_is_compatible("nvidia,tegra114"))
170 		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
171 	else if (of_machine_is_compatible("nvidia,tegra124"))
172 		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124;
173 	else {
174 		dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n");
175 		return -EINVAL;
176 	}
177 
178 	data->clk_pll_a = clk_get(dev, "pll_a");
179 	if (IS_ERR(data->clk_pll_a)) {
180 		dev_err(data->dev, "Can't retrieve clk pll_a\n");
181 		ret = PTR_ERR(data->clk_pll_a);
182 		goto err;
183 	}
184 
185 	data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0");
186 	if (IS_ERR(data->clk_pll_a_out0)) {
187 		dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
188 		ret = PTR_ERR(data->clk_pll_a_out0);
189 		goto err_put_pll_a;
190 	}
191 
192 	data->clk_cdev1 = clk_get(dev, "mclk");
193 	if (IS_ERR(data->clk_cdev1)) {
194 		dev_err(data->dev, "Can't retrieve clk cdev1\n");
195 		ret = PTR_ERR(data->clk_cdev1);
196 		goto err_put_pll_a_out0;
197 	}
198 
199 	ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
200 	if (ret)
201 		goto err_put_cdev1;
202 
203 	return 0;
204 
205 err_put_cdev1:
206 	clk_put(data->clk_cdev1);
207 err_put_pll_a_out0:
208 	clk_put(data->clk_pll_a_out0);
209 err_put_pll_a:
210 	clk_put(data->clk_pll_a);
211 err:
212 	return ret;
213 }
214 EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
215 
tegra_asoc_utils_fini(struct tegra_asoc_utils_data * data)216 void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
217 {
218 	clk_put(data->clk_cdev1);
219 	clk_put(data->clk_pll_a_out0);
220 	clk_put(data->clk_pll_a);
221 }
222 EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
223 
224 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
225 MODULE_DESCRIPTION("Tegra ASoC utility code");
226 MODULE_LICENSE("GPL");
227