tegra_asoc_utils.c 5.5 KB
Newer Older
1 2 3 4
/*
 * tegra_asoc_utils.c - Harmony machine ASoC driver
 *
 * Author: Stephen Warren <swarren@nvidia.com>
5
 * Copyright (C) 2010,2012 - NVIDIA, Inc.
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA
 *
 */

#include <linux/clk.h>
24
#include <linux/device.h>
25 26
#include <linux/err.h>
#include <linux/kernel.h>
27
#include <linux/module.h>
28
#include <linux/of.h>
29 30 31

#include "tegra_asoc_utils.h"

32
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
33
			      int mclk)
34 35
{
	int new_baseclock;
36
	bool clk_change;
37 38 39 40 41 42 43
	int err;

	switch (srate) {
	case 11025:
	case 22050:
	case 44100:
	case 88200:
44 45 46 47
		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
			new_baseclock = 56448000;
		else
			new_baseclock = 564480000;
48 49 50 51 52 53 54
		break;
	case 8000:
	case 16000:
	case 32000:
	case 48000:
	case 64000:
	case 96000:
55 56 57 58
		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
			new_baseclock = 73728000;
		else
			new_baseclock = 552960000;
59 60 61 62 63
		break;
	default:
		return -EINVAL;
	}

64
	clk_change = ((new_baseclock != data->set_baseclock) ||
65
			(mclk != data->set_mclk));
66 67
	if (!clk_change)
		return 0;
68

69 70
	data->set_baseclock = 0;
	data->set_mclk = 0;
71

72 73 74
	clk_disable_unprepare(data->clk_cdev1);
	clk_disable_unprepare(data->clk_pll_a_out0);
	clk_disable_unprepare(data->clk_pll_a);
75

76
	err = clk_set_rate(data->clk_pll_a, new_baseclock);
77
	if (err) {
78
		dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
79 80 81
		return err;
	}

82
	err = clk_set_rate(data->clk_pll_a_out0, mclk);
83
	if (err) {
84
		dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
85 86 87
		return err;
	}

88
	/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
89

90
	err = clk_prepare_enable(data->clk_pll_a);
91
	if (err) {
92
		dev_err(data->dev, "Can't enable pll_a: %d\n", err);
93 94 95
		return err;
	}

96
	err = clk_prepare_enable(data->clk_pll_a_out0);
97
	if (err) {
98
		dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
99 100 101
		return err;
	}

102
	err = clk_prepare_enable(data->clk_cdev1);
103
	if (err) {
104
		dev_err(data->dev, "Can't enable cdev1: %d\n", err);
105 106 107
		return err;
	}

108 109
	data->set_baseclock = new_baseclock;
	data->set_mclk = mclk;
110 111 112

	return 0;
}
113
EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
114

115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
{
	const int pll_rate = 73728000;
	const int ac97_rate = 24576000;
	int err;

	clk_disable_unprepare(data->clk_cdev1);
	clk_disable_unprepare(data->clk_pll_a_out0);
	clk_disable_unprepare(data->clk_pll_a);

	/*
	 * AC97 rate is fixed at 24.576MHz and is used for both the host
	 * controller and the external codec
	 */
	err = clk_set_rate(data->clk_pll_a, pll_rate);
	if (err) {
		dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
		return err;
	}

	err = clk_set_rate(data->clk_pll_a_out0, ac97_rate);
	if (err) {
		dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
		return err;
	}

	/* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */

	err = clk_prepare_enable(data->clk_pll_a);
	if (err) {
		dev_err(data->dev, "Can't enable pll_a: %d\n", err);
		return err;
	}

	err = clk_prepare_enable(data->clk_pll_a_out0);
	if (err) {
		dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
		return err;
	}

	err = clk_prepare_enable(data->clk_cdev1);
	if (err) {
		dev_err(data->dev, "Can't enable cdev1: %d\n", err);
		return err;
	}

	data->set_baseclock = pll_rate;
	data->set_mclk = ac97_rate;

	return 0;
}
EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);

168 169
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
			  struct device *dev)
170 171 172
{
	int ret;

173 174
	data->dev = dev;

175
	if (of_machine_is_compatible("nvidia,tegra20"))
176 177 178 179 180 181
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
	else if (of_machine_is_compatible("nvidia,tegra30"))
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
	else
		return -EINVAL;

182 183 184 185
	data->clk_pll_a = clk_get_sys(NULL, "pll_a");
	if (IS_ERR(data->clk_pll_a)) {
		dev_err(data->dev, "Can't retrieve clk pll_a\n");
		ret = PTR_ERR(data->clk_pll_a);
186 187 188
		goto err;
	}

189 190 191 192
	data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
	if (IS_ERR(data->clk_pll_a_out0)) {
		dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
		ret = PTR_ERR(data->clk_pll_a_out0);
193
		goto err_put_pll_a;
194 195
	}

196 197 198 199
	if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
		data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
	else
		data->clk_cdev1 = clk_get_sys("extern1", NULL);
200 201 202
	if (IS_ERR(data->clk_cdev1)) {
		dev_err(data->dev, "Can't retrieve clk cdev1\n");
		ret = PTR_ERR(data->clk_cdev1);
203
		goto err_put_pll_a_out0;
204 205
	}

206 207 208 209
	ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
	if (ret)
		goto err_put_cdev1;

210 211
	return 0;

212 213
err_put_cdev1:
	clk_put(data->clk_cdev1);
214
err_put_pll_a_out0:
215
	clk_put(data->clk_pll_a_out0);
216
err_put_pll_a:
217
	clk_put(data->clk_pll_a);
218 219 220
err:
	return ret;
}
221
EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
222

223
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
224
{
225 226 227
	clk_put(data->clk_cdev1);
	clk_put(data->clk_pll_a_out0);
	clk_put(data->clk_pll_a);
228
}
229
EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
230

231 232 233
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra ASoC utility code");
MODULE_LICENSE("GPL");