tegra_asoc_utils.c 5.24 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2 3 4 5
/*
 * tegra_asoc_utils.c - Harmony machine ASoC driver
 *
 * 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 12
#include <linux/err.h>
#include <linux/kernel.h>
13
#include <linux/module.h>
14
#include <linux/of.h>
15 16 17

#include "tegra_asoc_utils.h"

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 24 25 26 27 28 29
	int err;

	switch (srate) {
	case 11025:
	case 22050:
	case 44100:
	case 88200:
30 31
		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
			new_baseclock = 56448000;
32
		else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
33
			new_baseclock = 564480000;
34 35
		else
			new_baseclock = 282240000;
36 37 38 39 40 41 42
		break;
	case 8000:
	case 16000:
	case 32000:
	case 48000:
	case 64000:
	case 96000:
43 44
		if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20)
			new_baseclock = 73728000;
45
		else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30)
46
			new_baseclock = 552960000;
47 48
		else
			new_baseclock = 368640000;
49 50 51 52 53
		break;
	default:
		return -EINVAL;
	}

54
	clk_change = ((new_baseclock != data->set_baseclock) ||
55
			(mclk != data->set_mclk));
56 57
	if (!clk_change)
		return 0;
58

59 60
	data->set_baseclock = 0;
	data->set_mclk = 0;
61

62
	clk_disable_unprepare(data->clk_cdev1);
63

64
	err = clk_set_rate(data->clk_pll_a, new_baseclock);
65
	if (err) {
66
		dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
67 68 69
		return err;
	}

70
	err = clk_set_rate(data->clk_pll_a_out0, mclk);
71
	if (err) {
72
		dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
73 74 75
		return err;
	}

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

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

84 85
	data->set_baseclock = new_baseclock;
	data->set_mclk = mclk;
86 87 88

	return 0;
}
89
EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
90

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
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);

	/*
	 * 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_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);

130 131
int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
			  struct device *dev)
132
{
133
	struct clk *clk_out_1, *clk_extern1;
134 135
	int ret;

136 137
	data->dev = dev;

138
	if (of_machine_is_compatible("nvidia,tegra20"))
139 140 141
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
	else if (of_machine_is_compatible("nvidia,tegra30"))
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
142
	else if (of_machine_is_compatible("nvidia,tegra114"))
143
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
144 145
	else if (of_machine_is_compatible("nvidia,tegra124"))
		data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124;
146
	else {
147
		dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n");
148
		return -EINVAL;
149
	}
150

151
	data->clk_pll_a = devm_clk_get(dev, "pll_a");
152 153
	if (IS_ERR(data->clk_pll_a)) {
		dev_err(data->dev, "Can't retrieve clk pll_a\n");
154
		return PTR_ERR(data->clk_pll_a);
155 156
	}

157
	data->clk_pll_a_out0 = devm_clk_get(dev, "pll_a_out0");
158 159
	if (IS_ERR(data->clk_pll_a_out0)) {
		dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
160
		return PTR_ERR(data->clk_pll_a_out0);
161 162
	}

163
	data->clk_cdev1 = devm_clk_get(dev, "mclk");
164 165
	if (IS_ERR(data->clk_cdev1)) {
		dev_err(data->dev, "Can't retrieve clk cdev1\n");
166
		return PTR_ERR(data->clk_cdev1);
167 168
	}

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
	/*
	 * If clock parents are not set in DT, configure here to use clk_out_1
	 * as mclk and extern1 as parent for Tegra30 and higher.
	 */
	if (!of_find_property(dev->of_node, "assigned-clock-parents", NULL) &&
	    data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) {
		dev_warn(data->dev,
			 "Configuring clocks for a legacy device-tree\n");
		dev_warn(data->dev,
			 "Please update DT to use assigned-clock-parents\n");
		clk_extern1 = devm_clk_get(dev, "extern1");
		if (IS_ERR(clk_extern1)) {
			dev_err(data->dev, "Can't retrieve clk extern1\n");
			return PTR_ERR(clk_extern1);
		}

		ret = clk_set_parent(clk_extern1, data->clk_pll_a_out0);
		if (ret < 0) {
			dev_err(data->dev,
				"Set parent failed for clk extern1\n");
			return ret;
		}

		clk_out_1 = devm_clk_get(dev, "pmc_clk_out_1");
		if (IS_ERR(clk_out_1)) {
			dev_err(data->dev, "Can't retrieve pmc_clk_out_1\n");
			return PTR_ERR(clk_out_1);
		}

		ret = clk_set_parent(clk_out_1, clk_extern1);
		if (ret < 0) {
			dev_err(data->dev,
				"Set parent failed for pmc_clk_out_1\n");
			return ret;
		}

		data->clk_cdev1 = clk_out_1;
	}

208 209
	ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
	if (ret)
210
		return ret;
211

212 213
	return 0;
}
214
EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
215

216 217 218
MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
MODULE_DESCRIPTION("Tegra ASoC utility code");
MODULE_LICENSE("GPL");