tegra_asoc_utils.c 4.4 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(data->clk_cdev1);
	clk_disable(data->clk_pll_a_out0);
	clk_disable(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_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_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_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
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
			  struct device *dev)
117 118 119
{
	int ret;

120 121
	data->dev = dev;

122
	if (of_machine_is_compatible("nvidia,tegra20"))
123 124 125
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
	else if (of_machine_is_compatible("nvidia,tegra30"))
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
126 127 128
	else if (!dev->of_node)
		/* non-DT is always Tegra20 */
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
129
	else
130
		/* DT boot, but unknown SoC */
131 132
		return -EINVAL;

133 134 135 136
	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);
137 138 139
		goto err;
	}

140 141 142 143
	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);
144
		goto err_put_pll_a;
145 146
	}

147 148 149 150
	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);
151 152 153
	if (IS_ERR(data->clk_cdev1)) {
		dev_err(data->dev, "Can't retrieve clk cdev1\n");
		ret = PTR_ERR(data->clk_cdev1);
154
		goto err_put_pll_a_out0;
155 156
	}

157 158 159 160
	ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
	if (ret)
		goto err_put_cdev1;

161 162
	return 0;

163 164
err_put_cdev1:
	clk_put(data->clk_cdev1);
165
err_put_pll_a_out0:
166
	clk_put(data->clk_pll_a_out0);
167
err_put_pll_a:
168
	clk_put(data->clk_pll_a);
169 170 171
err:
	return ret;
}
172
EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
173

174
void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
175
{
176 177 178
	clk_put(data->clk_cdev1);
	clk_put(data->clk_pll_a_out0);
	clk_put(data->clk_pll_a);
179
}
180
EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
181

182 183 184
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra ASoC utility code");
MODULE_LICENSE("GPL");