Commit | Line | Data |
---|---|---|
a50a399b SW |
1 | /* |
2 | * tegra_asoc_utils.c - Harmony machine ASoC driver | |
3 | * | |
4 | * Author: Stephen Warren <swarren@nvidia.com> | |
5 | * Copyright (C) 2010 - NVIDIA, Inc. | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU General Public License | |
9 | * version 2 as published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |
19 | * 02110-1301 USA | |
20 | * | |
21 | */ | |
22 | ||
23 | #include <linux/clk.h> | |
d64e57ce | 24 | #include <linux/device.h> |
a50a399b SW |
25 | #include <linux/err.h> |
26 | #include <linux/kernel.h> | |
27 | ||
28 | #include "tegra_asoc_utils.h" | |
29 | ||
d64e57ce | 30 | int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, |
07541396 | 31 | int mclk) |
a50a399b SW |
32 | { |
33 | int new_baseclock; | |
07541396 | 34 | bool clk_change; |
a50a399b SW |
35 | int err; |
36 | ||
37 | switch (srate) { | |
38 | case 11025: | |
39 | case 22050: | |
40 | case 44100: | |
41 | case 88200: | |
42 | new_baseclock = 56448000; | |
43 | break; | |
44 | case 8000: | |
45 | case 16000: | |
46 | case 32000: | |
47 | case 48000: | |
48 | case 64000: | |
49 | case 96000: | |
50 | new_baseclock = 73728000; | |
51 | break; | |
52 | default: | |
53 | return -EINVAL; | |
54 | } | |
55 | ||
07541396 | 56 | clk_change = ((new_baseclock != data->set_baseclock) || |
d64e57ce | 57 | (mclk != data->set_mclk)); |
07541396 SW |
58 | if (!clk_change) |
59 | return 0; | |
a50a399b | 60 | |
d64e57ce SW |
61 | data->set_baseclock = 0; |
62 | data->set_mclk = 0; | |
a50a399b | 63 | |
d64e57ce SW |
64 | clk_disable(data->clk_cdev1); |
65 | clk_disable(data->clk_pll_a_out0); | |
66 | clk_disable(data->clk_pll_a); | |
a50a399b | 67 | |
d64e57ce | 68 | err = clk_set_rate(data->clk_pll_a, new_baseclock); |
a50a399b | 69 | if (err) { |
d64e57ce | 70 | dev_err(data->dev, "Can't set pll_a rate: %d\n", err); |
a50a399b SW |
71 | return err; |
72 | } | |
73 | ||
d64e57ce | 74 | err = clk_set_rate(data->clk_pll_a_out0, mclk); |
a50a399b | 75 | if (err) { |
d64e57ce | 76 | dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); |
a50a399b SW |
77 | return err; |
78 | } | |
79 | ||
80 | /* Don't set cdev1 rate; its locked to pll_a_out0 */ | |
81 | ||
d64e57ce | 82 | err = clk_enable(data->clk_pll_a); |
a50a399b | 83 | if (err) { |
d64e57ce | 84 | dev_err(data->dev, "Can't enable pll_a: %d\n", err); |
a50a399b SW |
85 | return err; |
86 | } | |
87 | ||
d64e57ce | 88 | err = clk_enable(data->clk_pll_a_out0); |
a50a399b | 89 | if (err) { |
d64e57ce | 90 | dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); |
a50a399b SW |
91 | return err; |
92 | } | |
93 | ||
d64e57ce | 94 | err = clk_enable(data->clk_cdev1); |
a50a399b | 95 | if (err) { |
d64e57ce | 96 | dev_err(data->dev, "Can't enable cdev1: %d\n", err); |
a50a399b SW |
97 | return err; |
98 | } | |
99 | ||
d64e57ce SW |
100 | data->set_baseclock = new_baseclock; |
101 | data->set_mclk = mclk; | |
a50a399b SW |
102 | |
103 | return 0; | |
104 | } | |
a3cd50de | 105 | EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); |
a50a399b | 106 | |
d64e57ce SW |
107 | int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, |
108 | struct device *dev) | |
a50a399b SW |
109 | { |
110 | int ret; | |
111 | ||
d64e57ce SW |
112 | data->dev = dev; |
113 | ||
114 | data->clk_pll_a = clk_get_sys(NULL, "pll_a"); | |
115 | if (IS_ERR(data->clk_pll_a)) { | |
116 | dev_err(data->dev, "Can't retrieve clk pll_a\n"); | |
117 | ret = PTR_ERR(data->clk_pll_a); | |
a50a399b SW |
118 | goto err; |
119 | } | |
120 | ||
d64e57ce SW |
121 | data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); |
122 | if (IS_ERR(data->clk_pll_a_out0)) { | |
123 | dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); | |
124 | ret = PTR_ERR(data->clk_pll_a_out0); | |
422650e6 | 125 | goto err_put_pll_a; |
a50a399b SW |
126 | } |
127 | ||
d64e57ce SW |
128 | data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); |
129 | if (IS_ERR(data->clk_cdev1)) { | |
130 | dev_err(data->dev, "Can't retrieve clk cdev1\n"); | |
131 | ret = PTR_ERR(data->clk_cdev1); | |
422650e6 | 132 | goto err_put_pll_a_out0; |
a50a399b SW |
133 | } |
134 | ||
135 | return 0; | |
136 | ||
422650e6 | 137 | err_put_pll_a_out0: |
d64e57ce | 138 | clk_put(data->clk_pll_a_out0); |
422650e6 | 139 | err_put_pll_a: |
d64e57ce | 140 | clk_put(data->clk_pll_a); |
a50a399b | 141 | err: |
a50a399b SW |
142 | return ret; |
143 | } | |
a3cd50de | 144 | EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); |
a50a399b | 145 | |
d64e57ce | 146 | void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) |
a50a399b | 147 | { |
d64e57ce SW |
148 | clk_put(data->clk_cdev1); |
149 | clk_put(data->clk_pll_a_out0); | |
150 | clk_put(data->clk_pll_a); | |
a50a399b | 151 | } |
a3cd50de | 152 | EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); |
a50a399b | 153 | |
a3cd50de SW |
154 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
155 | MODULE_DESCRIPTION("Tegra ASoC utility code"); | |
156 | MODULE_LICENSE("GPL"); |