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>
24 #include <linux/device.h>
25 #include <linux/err.h>
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 
29 #include "tegra_asoc_utils.h"
30 
tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data * data,int srate,int mclk)31 int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
32 			      int mclk)
33 {
34 	int new_baseclock;
35 	bool clk_change;
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 
57 	clk_change = ((new_baseclock != data->set_baseclock) ||
58 			(mclk != data->set_mclk));
59 	if (!clk_change)
60 		return 0;
61 
62 	data->set_baseclock = 0;
63 	data->set_mclk = 0;
64 
65 	clk_disable(data->clk_cdev1);
66 	clk_disable(data->clk_pll_a_out0);
67 	clk_disable(data->clk_pll_a);
68 
69 	err = clk_set_rate(data->clk_pll_a, new_baseclock);
70 	if (err) {
71 		dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
72 		return err;
73 	}
74 
75 	err = clk_set_rate(data->clk_pll_a_out0, mclk);
76 	if (err) {
77 		dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
78 		return err;
79 	}
80 
81 	/* Don't set cdev1 rate; its locked to pll_a_out0 */
82 
83 	err = clk_enable(data->clk_pll_a);
84 	if (err) {
85 		dev_err(data->dev, "Can't enable pll_a: %d\n", err);
86 		return err;
87 	}
88 
89 	err = clk_enable(data->clk_pll_a_out0);
90 	if (err) {
91 		dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
92 		return err;
93 	}
94 
95 	err = clk_enable(data->clk_cdev1);
96 	if (err) {
97 		dev_err(data->dev, "Can't enable cdev1: %d\n", err);
98 		return err;
99 	}
100 
101 	data->set_baseclock = new_baseclock;
102 	data->set_mclk = mclk;
103 
104 	return 0;
105 }
106 EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
107 
tegra_asoc_utils_init(struct tegra_asoc_utils_data * data,struct device * dev)108 int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
109 			  struct device *dev)
110 {
111 	int ret;
112 
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);
119 		goto err;
120 	}
121 
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);
126 		goto err_put_pll_a;
127 	}
128 
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);
133 		goto err_put_pll_a_out0;
134 	}
135 
136 	return 0;
137 
138 err_put_pll_a_out0:
139 	clk_put(data->clk_pll_a_out0);
140 err_put_pll_a:
141 	clk_put(data->clk_pll_a);
142 err:
143 	return ret;
144 }
145 EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
146 
tegra_asoc_utils_fini(struct tegra_asoc_utils_data * data)147 void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
148 {
149 	clk_put(data->clk_cdev1);
150 	clk_put(data->clk_pll_a_out0);
151 	clk_put(data->clk_pll_a);
152 }
153 EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
154 
155 MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
156 MODULE_DESCRIPTION("Tegra ASoC utility code");
157 MODULE_LICENSE("GPL");
158