linux/sound/soc/tegra/tegra_asoc_utils.c
<<
>>
Prefs
   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