1/* 2 * tegra_asoc_utils.c - Harmony machine ASoC driver 3 * 4 * Author: Stephen Warren <swarren@nvidia.com> 5 * Copyright (C) 2010,2012 - 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#include <linux/of.h> 29 30#include "tegra_asoc_utils.h" 31 32int 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 int err; 38 39 switch (srate) { 40 case 11025: 41 case 22050: 42 case 44100: 43 case 88200: 44 if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) 45 new_baseclock = 56448000; 46 else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30) 47 new_baseclock = 564480000; 48 else 49 new_baseclock = 282240000; 50 break; 51 case 8000: 52 case 16000: 53 case 32000: 54 case 48000: 55 case 64000: 56 case 96000: 57 if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) 58 new_baseclock = 73728000; 59 else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30) 60 new_baseclock = 552960000; 61 else 62 new_baseclock = 368640000; 63 break; 64 default: 65 return -EINVAL; 66 } 67 68 clk_change = ((new_baseclock != data->set_baseclock) || 69 (mclk != data->set_mclk)); 70 if (!clk_change) 71 return 0; 72 73 data->set_baseclock = 0; 74 data->set_mclk = 0; 75 76 clk_disable_unprepare(data->clk_cdev1); 77 clk_disable_unprepare(data->clk_pll_a_out0); 78 clk_disable_unprepare(data->clk_pll_a); 79 80 err = clk_set_rate(data->clk_pll_a, new_baseclock); 81 if (err) { 82 dev_err(data->dev, "Can't set pll_a rate: %d\n", err); 83 return err; 84 } 85 86 err = clk_set_rate(data->clk_pll_a_out0, mclk); 87 if (err) { 88 dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); 89 return err; 90 } 91 92 /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ 93 94 err = clk_prepare_enable(data->clk_pll_a); 95 if (err) { 96 dev_err(data->dev, "Can't enable pll_a: %d\n", err); 97 return err; 98 } 99 100 err = clk_prepare_enable(data->clk_pll_a_out0); 101 if (err) { 102 dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); 103 return err; 104 } 105 106 err = clk_prepare_enable(data->clk_cdev1); 107 if (err) { 108 dev_err(data->dev, "Can't enable cdev1: %d\n", err); 109 return err; 110 } 111 112 data->set_baseclock = new_baseclock; 113 data->set_mclk = mclk; 114 115 return 0; 116} 117EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); 118 119int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data) 120{ 121 const int pll_rate = 73728000; 122 const int ac97_rate = 24576000; 123 int err; 124 125 clk_disable_unprepare(data->clk_cdev1); 126 clk_disable_unprepare(data->clk_pll_a_out0); 127 clk_disable_unprepare(data->clk_pll_a); 128 129 /* 130 * AC97 rate is fixed at 24.576MHz and is used for both the host 131 * controller and the external codec 132 */ 133 err = clk_set_rate(data->clk_pll_a, pll_rate); 134 if (err) { 135 dev_err(data->dev, "Can't set pll_a rate: %d\n", err); 136 return err; 137 } 138 139 err = clk_set_rate(data->clk_pll_a_out0, ac97_rate); 140 if (err) { 141 dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); 142 return err; 143 } 144 145 /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ 146 147 err = clk_prepare_enable(data->clk_pll_a); 148 if (err) { 149 dev_err(data->dev, "Can't enable pll_a: %d\n", err); 150 return err; 151 } 152 153 err = clk_prepare_enable(data->clk_pll_a_out0); 154 if (err) { 155 dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err); 156 return err; 157 } 158 159 err = clk_prepare_enable(data->clk_cdev1); 160 if (err) { 161 dev_err(data->dev, "Can't enable cdev1: %d\n", err); 162 return err; 163 } 164 165 data->set_baseclock = pll_rate; 166 data->set_mclk = ac97_rate; 167 168 return 0; 169} 170EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate); 171 172int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, 173 struct device *dev) 174{ 175 int ret; 176 177 data->dev = dev; 178 179 if (of_machine_is_compatible("nvidia,tegra20")) 180 data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20; 181 else if (of_machine_is_compatible("nvidia,tegra30")) 182 data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30; 183 else if (of_machine_is_compatible("nvidia,tegra114")) 184 data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114; 185 else if (of_machine_is_compatible("nvidia,tegra124")) 186 data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124; 187 else { 188 dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n"); 189 return -EINVAL; 190 } 191 192 data->clk_pll_a = clk_get(dev, "pll_a"); 193 if (IS_ERR(data->clk_pll_a)) { 194 dev_err(data->dev, "Can't retrieve clk pll_a\n"); 195 ret = PTR_ERR(data->clk_pll_a); 196 goto err; 197 } 198 199 data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0"); 200 if (IS_ERR(data->clk_pll_a_out0)) { 201 dev_err(data->dev, "Can't retrieve clk pll_a_out0\n"); 202 ret = PTR_ERR(data->clk_pll_a_out0); 203 goto err_put_pll_a; 204 } 205 206 data->clk_cdev1 = clk_get(dev, "mclk"); 207 if (IS_ERR(data->clk_cdev1)) { 208 dev_err(data->dev, "Can't retrieve clk cdev1\n"); 209 ret = PTR_ERR(data->clk_cdev1); 210 goto err_put_pll_a_out0; 211 } 212 213 ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100); 214 if (ret) 215 goto err_put_cdev1; 216 217 return 0; 218 219err_put_cdev1: 220 clk_put(data->clk_cdev1); 221err_put_pll_a_out0: 222 clk_put(data->clk_pll_a_out0); 223err_put_pll_a: 224 clk_put(data->clk_pll_a); 225err: 226 return ret; 227} 228EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); 229 230void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data) 231{ 232 clk_put(data->clk_cdev1); 233 clk_put(data->clk_pll_a_out0); 234 clk_put(data->clk_pll_a); 235} 236EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini); 237 238MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 239MODULE_DESCRIPTION("Tegra ASoC utility code"); 240MODULE_LICENSE("GPL"); 241