linux/drivers/clk/tegra/clk-tegra-audio.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2012, 2013, NVIDIA CORPORATION.  All rights reserved.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms and conditions of the GNU General Public License,
   6 * version 2, as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License
  14 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  15 */
  16
  17#include <linux/io.h>
  18#include <linux/clk-provider.h>
  19#include <linux/of.h>
  20#include <linux/of_address.h>
  21#include <linux/delay.h>
  22#include <linux/export.h>
  23#include <linux/clk/tegra.h>
  24
  25#include "clk.h"
  26#include "clk-id.h"
  27
  28#define AUDIO_SYNC_CLK_I2S0 0x4a0
  29#define AUDIO_SYNC_CLK_I2S1 0x4a4
  30#define AUDIO_SYNC_CLK_I2S2 0x4a8
  31#define AUDIO_SYNC_CLK_I2S3 0x4ac
  32#define AUDIO_SYNC_CLK_I2S4 0x4b0
  33#define AUDIO_SYNC_CLK_SPDIF 0x4b4
  34
  35#define AUDIO_SYNC_DOUBLER 0x49c
  36
  37#define PLLA_OUT 0xb4
  38
  39struct tegra_sync_source_initdata {
  40        char            *name;
  41        unsigned long   rate;
  42        unsigned long   max_rate;
  43        int             clk_id;
  44};
  45
  46#define SYNC(_name) \
  47        {\
  48                .name           = #_name,\
  49                .rate           = 24000000,\
  50                .max_rate       = 24000000,\
  51                .clk_id         = tegra_clk_ ## _name,\
  52        }
  53
  54struct tegra_audio_clk_initdata {
  55        char            *gate_name;
  56        char            *mux_name;
  57        u32             offset;
  58        int             gate_clk_id;
  59        int             mux_clk_id;
  60};
  61
  62#define AUDIO(_name, _offset) \
  63        {\
  64                .gate_name      = #_name,\
  65                .mux_name       = #_name"_mux",\
  66                .offset         = _offset,\
  67                .gate_clk_id    = tegra_clk_ ## _name,\
  68                .mux_clk_id     = tegra_clk_ ## _name ## _mux,\
  69        }
  70
  71struct tegra_audio2x_clk_initdata {
  72        char            *parent;
  73        char            *gate_name;
  74        char            *name_2x;
  75        char            *div_name;
  76        int             clk_id;
  77        int             clk_num;
  78        u8              div_offset;
  79};
  80
  81#define AUDIO2X(_name, _num, _offset) \
  82        {\
  83                .parent         = #_name,\
  84                .gate_name      = #_name"_2x",\
  85                .name_2x        = #_name"_doubler",\
  86                .div_name       = #_name"_div",\
  87                .clk_id         = tegra_clk_ ## _name ## _2x,\
  88                .clk_num        = _num,\
  89                .div_offset     = _offset,\
  90        }
  91
  92static DEFINE_SPINLOCK(clk_doubler_lock);
  93
  94static const char *mux_audio_sync_clk[] = { "spdif_in_sync", "i2s0_sync",
  95        "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "vimclk_sync",
  96};
  97
  98static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
  99        SYNC(spdif_in_sync),
 100        SYNC(i2s0_sync),
 101        SYNC(i2s1_sync),
 102        SYNC(i2s2_sync),
 103        SYNC(i2s3_sync),
 104        SYNC(i2s4_sync),
 105        SYNC(vimclk_sync),
 106};
 107
 108static struct tegra_audio_clk_initdata audio_clks[] = {
 109        AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
 110        AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
 111        AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
 112        AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
 113        AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
 114        AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
 115};
 116
 117static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
 118        AUDIO2X(audio0, 113, 24),
 119        AUDIO2X(audio1, 114, 25),
 120        AUDIO2X(audio2, 115, 26),
 121        AUDIO2X(audio3, 116, 27),
 122        AUDIO2X(audio4, 117, 28),
 123        AUDIO2X(spdif, 118, 29),
 124};
 125
 126void __init tegra_audio_clk_init(void __iomem *clk_base,
 127                        void __iomem *pmc_base, struct tegra_clk *tegra_clks,
 128                        struct tegra_audio_clk_info *audio_info,
 129                        unsigned int num_plls)
 130{
 131        struct clk *clk;
 132        struct clk **dt_clk;
 133        int i;
 134
 135        if (!audio_info || num_plls < 1) {
 136                pr_err("No audio data passed to tegra_audio_clk_init\n");
 137                WARN_ON(1);
 138                return;
 139        }
 140
 141        for (i = 0; i < num_plls; i++) {
 142                struct tegra_audio_clk_info *info = &audio_info[i];
 143
 144                dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks);
 145                if (dt_clk) {
 146                        clk = tegra_clk_register_pll(info->name, info->parent,
 147                                        clk_base, pmc_base, 0, info->pll_params,
 148                                        NULL);
 149                        *dt_clk = clk;
 150                }
 151        }
 152
 153        /* PLLA_OUT0 */
 154        dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
 155        if (dt_clk) {
 156                clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
 157                                clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
 158                                8, 8, 1, NULL);
 159                clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
 160                                clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
 161                                CLK_SET_RATE_PARENT, 0, NULL);
 162                *dt_clk = clk;
 163        }
 164
 165        for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
 166                struct tegra_sync_source_initdata *data;
 167
 168                data = &sync_source_clks[i];
 169
 170                dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
 171                if (!dt_clk)
 172                        continue;
 173
 174                clk = tegra_clk_register_sync_source(data->name,
 175                                        data->rate, data->max_rate);
 176                *dt_clk = clk;
 177        }
 178
 179        for (i = 0; i < ARRAY_SIZE(audio_clks); i++) {
 180                struct tegra_audio_clk_initdata *data;
 181
 182                data = &audio_clks[i];
 183                dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
 184
 185                if (!dt_clk)
 186                        continue;
 187                clk = clk_register_mux(NULL, data->mux_name, mux_audio_sync_clk,
 188                                        ARRAY_SIZE(mux_audio_sync_clk),
 189                                        CLK_SET_RATE_NO_REPARENT,
 190                                        clk_base + data->offset, 0, 3, 0,
 191                                        NULL);
 192                *dt_clk = clk;
 193
 194                dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
 195                if (!dt_clk)
 196                        continue;
 197
 198                clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
 199                                        0, clk_base + data->offset, 4,
 200                                        CLK_GATE_SET_TO_DISABLE, NULL);
 201                *dt_clk = clk;
 202        }
 203
 204        for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
 205                struct tegra_audio2x_clk_initdata *data;
 206
 207                data = &audio2x_clks[i];
 208                dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
 209                if (!dt_clk)
 210                        continue;
 211
 212                clk = clk_register_fixed_factor(NULL, data->name_2x,
 213                                data->parent, CLK_SET_RATE_PARENT, 2, 1);
 214                clk = tegra_clk_register_divider(data->div_name,
 215                                data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
 216                                0, 0, data->div_offset, 1, 0,
 217                                &clk_doubler_lock);
 218                clk = tegra_clk_register_periph_gate(data->gate_name,
 219                                data->div_name, TEGRA_PERIPH_NO_RESET,
 220                                clk_base, CLK_SET_RATE_PARENT, data->clk_num,
 221                                periph_clk_enb_refcnt);
 222                *dt_clk = clk;
 223        }
 224}
 225
 226