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