1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * tegra_asoc_utils.c - Harmony machine ASoC driver 4 * 5 * 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#include <linux/err.h> 12#include <linux/kernel.h> 13#include <linux/module.h> 14#include <linux/of.h> 15 16#include "tegra_asoc_utils.h" 17 18int 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 int err; 24 25 switch (srate) { 26 case 11025: 27 case 22050: 28 case 44100: 29 case 88200: 30 if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) 31 new_baseclock = 56448000; 32 else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30) 33 new_baseclock = 564480000; 34 else 35 new_baseclock = 282240000; 36 break; 37 case 8000: 38 case 16000: 39 case 32000: 40 case 48000: 41 case 64000: 42 case 96000: 43 if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA20) 44 new_baseclock = 73728000; 45 else if (data->soc == TEGRA_ASOC_UTILS_SOC_TEGRA30) 46 new_baseclock = 552960000; 47 else 48 new_baseclock = 368640000; 49 break; 50 default: 51 return -EINVAL; 52 } 53 54 clk_change = ((new_baseclock != data->set_baseclock) || 55 (mclk != data->set_mclk)); 56 if (!clk_change) 57 return 0; 58 59 data->set_baseclock = 0; 60 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 return err; 68 } 69 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 return err; 74 } 75 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 return err; 82 } 83 84 data->set_baseclock = new_baseclock; 85 data->set_mclk = mclk; 86 87 return 0; 88} 89EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate); 90 91int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data) 92{ 93 const int pll_rate = 73728000; 94 const int ac97_rate = 24576000; 95 int err; 96 97 clk_disable_unprepare(data->clk_cdev1); 98 99 /* 100 * AC97 rate is fixed at 24.576MHz and is used for both the host 101 * controller and the external codec 102 */ 103 err = clk_set_rate(data->clk_pll_a, pll_rate); 104 if (err) { 105 dev_err(data->dev, "Can't set pll_a rate: %d\n", err); 106 return err; 107 } 108 109 err = clk_set_rate(data->clk_pll_a_out0, ac97_rate); 110 if (err) { 111 dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err); 112 return err; 113 } 114 115 /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */ 116 117 err = clk_prepare_enable(data->clk_cdev1); 118 if (err) { 119 dev_err(data->dev, "Can't enable cdev1: %d\n", err); 120 return err; 121 } 122 123 data->set_baseclock = pll_rate; 124 data->set_mclk = ac97_rate; 125 126 return 0; 127} 128EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate); 129 130int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data, 131 struct device *dev) 132{ 133 struct clk *clk_out_1, *clk_extern1; 134 int ret; 135 136 data->dev = dev; 137 138 if (of_machine_is_compatible("nvidia,tegra20")) 139 data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20; 140 else if (of_machine_is_compatible("nvidia,tegra30")) 141 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 else if (of_machine_is_compatible("nvidia,tegra124")) 145 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 if (IS_ERR(data->clk_pll_a)) { 153 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 if (IS_ERR(data->clk_pll_a_out0)) { 159 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 if (IS_ERR(data->clk_cdev1)) { 165 dev_err(data->dev, "Can't retrieve clk cdev1\n"); 166 return PTR_ERR(data->clk_cdev1); 167 } 168 169 /* 170 * If clock parents are not set in DT, configure here to use clk_out_1 171 * as mclk and extern1 as parent for Tegra30 and higher. 172 */ 173 if (!of_find_property(dev->of_node, "assigned-clock-parents", NULL) && 174 data->soc > TEGRA_ASOC_UTILS_SOC_TEGRA20) { 175 dev_warn(data->dev, 176 "Configuring clocks for a legacy device-tree\n"); 177 dev_warn(data->dev, 178 "Please update DT to use assigned-clock-parents\n"); 179 clk_extern1 = devm_clk_get(dev, "extern1"); 180 if (IS_ERR(clk_extern1)) { 181 dev_err(data->dev, "Can't retrieve clk extern1\n"); 182 return PTR_ERR(clk_extern1); 183 } 184 185 ret = clk_set_parent(clk_extern1, data->clk_pll_a_out0); 186 if (ret < 0) { 187 dev_err(data->dev, 188 "Set parent failed for clk extern1\n"); 189 return ret; 190 } 191 192 clk_out_1 = devm_clk_get(dev, "pmc_clk_out_1"); 193 if (IS_ERR(clk_out_1)) { 194 dev_err(data->dev, "Can't retrieve pmc_clk_out_1\n"); 195 return PTR_ERR(clk_out_1); 196 } 197 198 ret = clk_set_parent(clk_out_1, clk_extern1); 199 if (ret < 0) { 200 dev_err(data->dev, 201 "Set parent failed for pmc_clk_out_1\n"); 202 return ret; 203 } 204 205 data->clk_cdev1 = clk_out_1; 206 } 207 208 /* 209 * FIXME: There is some unknown dependency between audio mclk disable 210 * and suspend-resume functionality on Tegra30, although audio mclk is 211 * only needed for audio. 212 */ 213 ret = clk_prepare_enable(data->clk_cdev1); 214 if (ret) { 215 dev_err(data->dev, "Can't enable cdev1: %d\n", ret); 216 return ret; 217 } 218 219 return 0; 220} 221EXPORT_SYMBOL_GPL(tegra_asoc_utils_init); 222 223MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>"); 224MODULE_DESCRIPTION("Tegra ASoC utility code"); 225MODULE_LICENSE("GPL"); 226