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        clk_disable_unprepare(data->clk_pll_a_out0);
  64        clk_disable_unprepare(data->clk_pll_a);
  65
  66        err = clk_set_rate(data->clk_pll_a, new_baseclock);
  67        if (err) {
  68                dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
  69                return err;
  70        }
  71
  72        err = clk_set_rate(data->clk_pll_a_out0, mclk);
  73        if (err) {
  74                dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
  75                return err;
  76        }
  77
  78        /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
  79
  80        err = clk_prepare_enable(data->clk_pll_a);
  81        if (err) {
  82                dev_err(data->dev, "Can't enable pll_a: %d\n", err);
  83                return err;
  84        }
  85
  86        err = clk_prepare_enable(data->clk_pll_a_out0);
  87        if (err) {
  88                dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
  89                return err;
  90        }
  91
  92        err = clk_prepare_enable(data->clk_cdev1);
  93        if (err) {
  94                dev_err(data->dev, "Can't enable cdev1: %d\n", err);
  95                return err;
  96        }
  97
  98        data->set_baseclock = new_baseclock;
  99        data->set_mclk = mclk;
 100
 101        return 0;
 102}
 103EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_rate);
 104
 105int tegra_asoc_utils_set_ac97_rate(struct tegra_asoc_utils_data *data)
 106{
 107        const int pll_rate = 73728000;
 108        const int ac97_rate = 24576000;
 109        int err;
 110
 111        clk_disable_unprepare(data->clk_cdev1);
 112        clk_disable_unprepare(data->clk_pll_a_out0);
 113        clk_disable_unprepare(data->clk_pll_a);
 114
 115        /*
 116         * AC97 rate is fixed at 24.576MHz and is used for both the host
 117         * controller and the external codec
 118         */
 119        err = clk_set_rate(data->clk_pll_a, pll_rate);
 120        if (err) {
 121                dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
 122                return err;
 123        }
 124
 125        err = clk_set_rate(data->clk_pll_a_out0, ac97_rate);
 126        if (err) {
 127                dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
 128                return err;
 129        }
 130
 131        /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
 132
 133        err = clk_prepare_enable(data->clk_pll_a);
 134        if (err) {
 135                dev_err(data->dev, "Can't enable pll_a: %d\n", err);
 136                return err;
 137        }
 138
 139        err = clk_prepare_enable(data->clk_pll_a_out0);
 140        if (err) {
 141                dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
 142                return err;
 143        }
 144
 145        err = clk_prepare_enable(data->clk_cdev1);
 146        if (err) {
 147                dev_err(data->dev, "Can't enable cdev1: %d\n", err);
 148                return err;
 149        }
 150
 151        data->set_baseclock = pll_rate;
 152        data->set_mclk = ac97_rate;
 153
 154        return 0;
 155}
 156EXPORT_SYMBOL_GPL(tegra_asoc_utils_set_ac97_rate);
 157
 158int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
 159                          struct device *dev)
 160{
 161        int ret;
 162
 163        data->dev = dev;
 164
 165        if (of_machine_is_compatible("nvidia,tegra20"))
 166                data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA20;
 167        else if (of_machine_is_compatible("nvidia,tegra30"))
 168                data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA30;
 169        else if (of_machine_is_compatible("nvidia,tegra114"))
 170                data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA114;
 171        else if (of_machine_is_compatible("nvidia,tegra124"))
 172                data->soc = TEGRA_ASOC_UTILS_SOC_TEGRA124;
 173        else {
 174                dev_err(data->dev, "SoC unknown to Tegra ASoC utils\n");
 175                return -EINVAL;
 176        }
 177
 178        data->clk_pll_a = clk_get(dev, "pll_a");
 179        if (IS_ERR(data->clk_pll_a)) {
 180                dev_err(data->dev, "Can't retrieve clk pll_a\n");
 181                ret = PTR_ERR(data->clk_pll_a);
 182                goto err;
 183        }
 184
 185        data->clk_pll_a_out0 = clk_get(dev, "pll_a_out0");
 186        if (IS_ERR(data->clk_pll_a_out0)) {
 187                dev_err(data->dev, "Can't retrieve clk pll_a_out0\n");
 188                ret = PTR_ERR(data->clk_pll_a_out0);
 189                goto err_put_pll_a;
 190        }
 191
 192        data->clk_cdev1 = clk_get(dev, "mclk");
 193        if (IS_ERR(data->clk_cdev1)) {
 194                dev_err(data->dev, "Can't retrieve clk cdev1\n");
 195                ret = PTR_ERR(data->clk_cdev1);
 196                goto err_put_pll_a_out0;
 197        }
 198
 199        ret = tegra_asoc_utils_set_rate(data, 44100, 256 * 44100);
 200        if (ret)
 201                goto err_put_cdev1;
 202
 203        return 0;
 204
 205err_put_cdev1:
 206        clk_put(data->clk_cdev1);
 207err_put_pll_a_out0:
 208        clk_put(data->clk_pll_a_out0);
 209err_put_pll_a:
 210        clk_put(data->clk_pll_a);
 211err:
 212        return ret;
 213}
 214EXPORT_SYMBOL_GPL(tegra_asoc_utils_init);
 215
 216void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
 217{
 218        clk_put(data->clk_cdev1);
 219        clk_put(data->clk_pll_a_out0);
 220        clk_put(data->clk_pll_a);
 221}
 222EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
 223
 224MODULE_AUTHOR("Stephen Warren <swarren@nvidia.com>");
 225MODULE_DESCRIPTION("Tegra ASoC utility code");
 226MODULE_LICENSE("GPL");
 227