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#define AUDIO_SYNC_CLK_DMIC1 0x560
  35#define AUDIO_SYNC_CLK_DMIC2 0x564
  36#define AUDIO_SYNC_CLK_DMIC3 0x6b8
  37
  38#define AUDIO_SYNC_DOUBLER 0x49c
  39
  40#define PLLA_OUT 0xb4
  41
  42struct tegra_sync_source_initdata {
  43        char            *name;
  44        unsigned long   rate;
  45        unsigned long   max_rate;
  46        int             clk_id;
  47};
  48
  49#define SYNC(_name) \
  50        {\
  51                .name           = #_name,\
  52                .rate           = 24000000,\
  53                .max_rate       = 24000000,\
  54                .clk_id         = tegra_clk_ ## _name,\
  55        }
  56
  57struct tegra_audio_clk_initdata {
  58        char            *gate_name;
  59        char            *mux_name;
  60        u32             offset;
  61        int             gate_clk_id;
  62        int             mux_clk_id;
  63};
  64
  65#define AUDIO(_name, _offset) \
  66        {\
  67                .gate_name      = #_name,\
  68                .mux_name       = #_name"_mux",\
  69                .offset         = _offset,\
  70                .gate_clk_id    = tegra_clk_ ## _name,\
  71                .mux_clk_id     = tegra_clk_ ## _name ## _mux,\
  72        }
  73
  74struct tegra_audio2x_clk_initdata {
  75        char            *parent;
  76        char            *gate_name;
  77        char            *name_2x;
  78        char            *div_name;
  79        int             clk_id;
  80        int             clk_num;
  81        u8              div_offset;
  82};
  83
  84#define AUDIO2X(_name, _num, _offset) \
  85        {\
  86                .parent         = #_name,\
  87                .gate_name      = #_name"_2x",\
  88                .name_2x        = #_name"_doubler",\
  89                .div_name       = #_name"_div",\
  90                .clk_id         = tegra_clk_ ## _name ## _2x,\
  91                .clk_num        = _num,\
  92                .div_offset     = _offset,\
  93        }
  94
  95static DEFINE_SPINLOCK(clk_doubler_lock);
  96
  97static const char * const mux_audio_sync_clk[] = { "spdif_in_sync",
  98        "i2s0_sync", "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync",
  99        "pll_a_out0", "vimclk_sync",
 100};
 101
 102static const char * const mux_dmic_sync_clk[] = { "unused", "i2s0_sync",
 103        "i2s1_sync", "i2s2_sync", "i2s3_sync", "i2s4_sync", "pll_a_out0",
 104        "vimclk_sync",
 105};
 106
 107static struct tegra_sync_source_initdata sync_source_clks[] __initdata = {
 108        SYNC(spdif_in_sync),
 109        SYNC(i2s0_sync),
 110        SYNC(i2s1_sync),
 111        SYNC(i2s2_sync),
 112        SYNC(i2s3_sync),
 113        SYNC(i2s4_sync),
 114        SYNC(vimclk_sync),
 115};
 116
 117static struct tegra_audio_clk_initdata audio_clks[] = {
 118        AUDIO(audio0, AUDIO_SYNC_CLK_I2S0),
 119        AUDIO(audio1, AUDIO_SYNC_CLK_I2S1),
 120        AUDIO(audio2, AUDIO_SYNC_CLK_I2S2),
 121        AUDIO(audio3, AUDIO_SYNC_CLK_I2S3),
 122        AUDIO(audio4, AUDIO_SYNC_CLK_I2S4),
 123        AUDIO(spdif, AUDIO_SYNC_CLK_SPDIF),
 124};
 125
 126static struct tegra_audio_clk_initdata dmic_clks[] = {
 127        AUDIO(dmic1_sync_clk, AUDIO_SYNC_CLK_DMIC1),
 128        AUDIO(dmic2_sync_clk, AUDIO_SYNC_CLK_DMIC2),
 129        AUDIO(dmic3_sync_clk, AUDIO_SYNC_CLK_DMIC3),
 130};
 131
 132static struct tegra_audio2x_clk_initdata audio2x_clks[] = {
 133        AUDIO2X(audio0, 113, 24),
 134        AUDIO2X(audio1, 114, 25),
 135        AUDIO2X(audio2, 115, 26),
 136        AUDIO2X(audio3, 116, 27),
 137        AUDIO2X(audio4, 117, 28),
 138        AUDIO2X(spdif, 118, 29),
 139};
 140
 141static void __init tegra_audio_sync_clk_init(void __iomem *clk_base,
 142                                      struct tegra_clk *tegra_clks,
 143                                      struct tegra_audio_clk_initdata *sync,
 144                                      int num_sync_clks,
 145                                      const char * const *mux_names,
 146                                      int num_mux_inputs)
 147{
 148        struct clk *clk;
 149        struct clk **dt_clk;
 150        struct tegra_audio_clk_initdata *data;
 151        int i;
 152
 153        for (i = 0, data = sync; i < num_sync_clks; i++, data++) {
 154                dt_clk = tegra_lookup_dt_id(data->mux_clk_id, tegra_clks);
 155                if (!dt_clk)
 156                        continue;
 157
 158                clk = clk_register_mux(NULL, data->mux_name, mux_names,
 159                                        num_mux_inputs,
 160                                        CLK_SET_RATE_NO_REPARENT,
 161                                        clk_base + data->offset, 0, 3, 0,
 162                                        NULL);
 163                *dt_clk = clk;
 164
 165                dt_clk = tegra_lookup_dt_id(data->gate_clk_id, tegra_clks);
 166                if (!dt_clk)
 167                        continue;
 168
 169                clk = clk_register_gate(NULL, data->gate_name, data->mux_name,
 170                                        0, clk_base + data->offset, 4,
 171                                        CLK_GATE_SET_TO_DISABLE, NULL);
 172                *dt_clk = clk;
 173        }
 174}
 175
 176void __init tegra_audio_clk_init(void __iomem *clk_base,
 177                        void __iomem *pmc_base, struct tegra_clk *tegra_clks,
 178                        struct tegra_audio_clk_info *audio_info,
 179                        unsigned int num_plls)
 180{
 181        struct clk *clk;
 182        struct clk **dt_clk;
 183        int i;
 184
 185        if (!audio_info || num_plls < 1) {
 186                pr_err("No audio data passed to tegra_audio_clk_init\n");
 187                WARN_ON(1);
 188                return;
 189        }
 190
 191        for (i = 0; i < num_plls; i++) {
 192                struct tegra_audio_clk_info *info = &audio_info[i];
 193
 194                dt_clk = tegra_lookup_dt_id(info->clk_id, tegra_clks);
 195                if (dt_clk) {
 196                        clk = tegra_clk_register_pll(info->name, info->parent,
 197                                        clk_base, pmc_base, 0, info->pll_params,
 198                                        NULL);
 199                        *dt_clk = clk;
 200                }
 201        }
 202
 203        /* PLLA_OUT0 */
 204        dt_clk = tegra_lookup_dt_id(tegra_clk_pll_a_out0, tegra_clks);
 205        if (dt_clk) {
 206                clk = tegra_clk_register_divider("pll_a_out0_div", "pll_a",
 207                                clk_base + PLLA_OUT, 0, TEGRA_DIVIDER_ROUND_UP,
 208                                8, 8, 1, NULL);
 209                clk = tegra_clk_register_pll_out("pll_a_out0", "pll_a_out0_div",
 210                                clk_base + PLLA_OUT, 1, 0, CLK_IGNORE_UNUSED |
 211                                CLK_SET_RATE_PARENT, 0, NULL);
 212                *dt_clk = clk;
 213        }
 214
 215        for (i = 0; i < ARRAY_SIZE(sync_source_clks); i++) {
 216                struct tegra_sync_source_initdata *data;
 217
 218                data = &sync_source_clks[i];
 219
 220                dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
 221                if (!dt_clk)
 222                        continue;
 223
 224                clk = tegra_clk_register_sync_source(data->name,
 225                                        data->rate, data->max_rate);
 226                *dt_clk = clk;
 227        }
 228
 229        tegra_audio_sync_clk_init(clk_base, tegra_clks, audio_clks,
 230                                  ARRAY_SIZE(audio_clks), mux_audio_sync_clk,
 231                                  ARRAY_SIZE(mux_audio_sync_clk));
 232
 233        /* make sure the DMIC sync clocks have a valid parent */
 234        for (i = 0; i < ARRAY_SIZE(dmic_clks); i++)
 235                writel_relaxed(1, clk_base + dmic_clks[i].offset);
 236
 237        tegra_audio_sync_clk_init(clk_base, tegra_clks, dmic_clks,
 238                                  ARRAY_SIZE(dmic_clks), mux_dmic_sync_clk,
 239                                  ARRAY_SIZE(mux_dmic_sync_clk));
 240
 241        for (i = 0; i < ARRAY_SIZE(audio2x_clks); i++) {
 242                struct tegra_audio2x_clk_initdata *data;
 243
 244                data = &audio2x_clks[i];
 245                dt_clk = tegra_lookup_dt_id(data->clk_id, tegra_clks);
 246                if (!dt_clk)
 247                        continue;
 248
 249                clk = clk_register_fixed_factor(NULL, data->name_2x,
 250                                data->parent, CLK_SET_RATE_PARENT, 2, 1);
 251                clk = tegra_clk_register_divider(data->div_name,
 252                                data->name_2x, clk_base + AUDIO_SYNC_DOUBLER,
 253                                0, 0, data->div_offset, 1, 0,
 254                                &clk_doubler_lock);
 255                clk = tegra_clk_register_periph_gate(data->gate_name,
 256                                data->div_name, TEGRA_PERIPH_NO_RESET,
 257                                clk_base, CLK_SET_RATE_PARENT, data->clk_num,
 258                                periph_clk_enb_refcnt);
 259                *dt_clk = clk;
 260        }
 261}
 262
 263