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> | |
da155d5b | 27 | #include <linux/module.h> |
a50a399b SW |
28 | |
29 | #include "tegra_asoc_utils.h" | |
30 | ||
d64e57ce | 31 | int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate, |
07541396 | 32 | int mclk) |
a50a399b SW |
33 | { |
34 | int new_baseclock; | |
07541396 | 35 | bool clk_change; |
a50a399b SW |
36 | int err; |
37 | ||
38 | switch (srate) { | |
39 | case 11025: | |
40 | case 22050: | |
41 | case 44100: | |
42 | case 88200: | |
43 | new_baseclock = 56448000; | |
44 | break; | |
45 | case 8000: | |
46 | case 16000: | |
47 | case 32000: | |
48 | case 48000: | |
49 | case 64000: | |
50 | case 96000: | |
51 | new_baseclock = 73728000; | |
52 | break; | |
53 | default: | |
54 | return -EINVAL; | |
55 | } | |
56 | ||
07541396 | 57 | clk_change = ((new_baseclock != data->set_baseclock) || |
d64e57ce | 58 | (mclk != data->set_mclk)); |
07541396 SW |
59 | if (!clk_change) |
60 | return 0; | |
a50a399b | 61 | |
d64e57ce SW |
62 | data->set_baseclock = 0; |
63 | data->set_mclk = 0; | |
a50a399b | 64 | |
d64e57ce SW |
65 | clk_disable(data->clk_cdev1); |
66 | clk_disable(data->clk_pll_a_out0); | |
67 | clk_disable(data->clk_pll_a); | |
a50a399b | 68 | |
d64e57ce | 69 | err = clk_set_rate(data->clk_pll_a, new_baseclock); |
a50a399b | 70 | if (err) { |
d64e57ce | 71 | dev_err(data->dev, "Can't set pll_a rate: %d\n", err); |
a50a399b SW |
72 | return err; |
73 | } | |
74 | ||
d64e57ce | 75 | err = clk_set_rate(data->clk_pll_a_out0, mclk); |
a50a399b | 76 | if (err) { |
d64e57ce | 77 | dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); |
a50a399b SW |
78 | return err; |
79 | } | |
80 | ||
81 | /* Don't set cdev1 rate; its locked to pll_a_out0 */ | |
82 | ||
d64e57ce | 83 | err = clk_enable(data->clk_pll_a); |
a50a399b | 84 | if (err) { |
d64e57ce | 85 | dev_err(data->dev, "Can't enable pll_a: %d\n", err); |
a50a399b SW |
86 | return err; |
87 | } | |
88 | ||
d64e57ce | 89 | err = clk_enable(data->clk_pll_a_out0); |
a50a399b | 90 | if (err) { |
d64e57ce | 91 | dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); |
a50a399b SW |
92 | return err; |
93 | } | |
94 | ||
d64e57ce | 95 | err = clk_enable(data->clk_cdev1); |
a50a399b | 96 | if (err) { |
d64e57ce | 97 | dev_err(data->dev, "Can't enable cdev1: %d\n", err); |
a50a399b SW |
98 | return err; |
99 | } | |
100 | ||
d64e57ce SW |
101 | data->set_baseclock = new_baseclock; |
102 | data->set_mclk = mclk; | |
a50a399b SW |
103 | |
104 | return 0; | |
105 | } | |
a3cd50de | 106 | EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); |
a50a399b | 107 | |
d64e57ce SW |
108 | int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, |
109 | struct device *dev) | |
a50a399b SW |
110 | { |
111 | int ret; | |
112 | ||
d64e57ce SW |
113 | data->dev = dev; |
114 | ||
115 | data->clk_pll_a = clk_get_sys(NULL, "pll_a"); | |
116 | if (IS_ERR(data->clk_pll_a)) { | |
117 | dev_err(data->dev, "Can't retrieve clk pll_a\n"); | |
118 | ret = PTR_ERR(data->clk_pll_a); | |
a50a399b SW |
119 | goto err; |
120 | } | |
121 | ||
d64e57ce SW |
122 | data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0"); |
123 | if (IS_ERR(data->clk_pll_a_out0)) { | |
124 | dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); | |
125 | ret = PTR_ERR(data->clk_pll_a_out0); | |
422650e6 | 126 | goto err_put_pll_a; |
a50a399b SW |
127 | } |
128 | ||
d64e57ce SW |
129 | data->clk_cdev1 = clk_get_sys(NULL, "cdev1"); |
130 | if (IS_ERR(data->clk_cdev1)) { | |
131 | dev_err(data->dev, "Can't retrieve clk cdev1\n"); | |
132 | ret = PTR_ERR(data->clk_cdev1); | |
422650e6 | 133 | goto err_put_pll_a_out0; |
a50a399b SW |
134 | } |
135 | ||
136 | return 0; | |
137 | ||
422650e6 | 138 | err_put_pll_a_out0: |
d64e57ce | 139 | clk_put(data->clk_pll_a_out0); |
422650e6 | 140 | err_put_pll_a: |
d64e57ce | 141 | clk_put(data->clk_pll_a); |
a50a399b | 142 | err: |
a50a399b SW |
143 | return ret; |
144 | } | |
a3cd50de | 145 | EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); |
a50a399b | 146 | |
d64e57ce | 147 | void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) |
a50a399b | 148 | { |
d64e57ce SW |
149 | clk_put(data->clk_cdev1); |
150 | clk_put(data->clk_pll_a_out0); | |
151 | clk_put(data->clk_pll_a); | |
a50a399b | 152 | } |
a3cd50de | 153 | EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); |
a50a399b | 154 | |
a3cd50de SW |
155 | MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); |
156 | MODULE_DESCRIPTION("Tegra ASoC utility code"); | |
157 | MODULE_LICENSE("GPL"); |