Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  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
 18int 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}
103EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
104
105int 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}
156EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
157
158int 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
205err_put_cdev1:
206	clk_put(data->clk_cdev1);
207err_put_pll_a_out0:
208	clk_put(data->clk_pll_a_out0);
209err_put_pll_a:
210	clk_put(data->clk_pll_a);
211err:
212	return ret;
213}
214EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
215
216void 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}
222EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
223
224MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
225MODULE_DESCRIPTION("Tegra ASoC utility code");
226MODULE_LICENSE("GPL");