Commit dfc9403b7c1f566bb099a12c58aee20589e390f1

Authored by Kuninori Morimoto
Committed by Mark Brown
1 parent 07539c1de8

ASoC: add Renesas R-Car ADG feature

Renesas R-Car series sound circuit consists of SSI and its peripheral.
But this peripheral circuit is different between
R-Car Generation1 (E1/M1/H1) and Generation2 (E2/M2/H2)
(Actually, there are many difference in Generation1 chips)

This patch adds ADG feature which controls sound clock

Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@linaro.org>

Showing 6 changed files with 288 additions and 4 deletions Side-by-side Diff

include/sound/rcar_snd.h
... ... @@ -15,10 +15,12 @@
15 15 #include <linux/sh_clk.h>
16 16  
17 17 #define RSND_GEN1_SRU 0
  18 +#define RSND_GEN1_ADG 1
18 19  
19 20 #define RSND_GEN2_SRU 0
  21 +#define RSND_GEN2_ADG 1
20 22  
21   -#define RSND_BASE_MAX 1
  23 +#define RSND_BASE_MAX 2
22 24  
23 25 struct rsnd_scu_platform_info {
24 26 u32 flags;
sound/soc/sh/rcar/Makefile
1   -snd-soc-rcar-objs := core.o gen.o scu.o
  1 +snd-soc-rcar-objs := core.o gen.o scu.o adg.o
2 2 obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
sound/soc/sh/rcar/adg.c
  1 +/*
  2 + * Helper routines for R-Car sound ADG.
  3 + *
  4 + * Copyright (C) 2013 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
  5 + *
  6 + * This file is subject to the terms and conditions of the GNU General Public
  7 + * License. See the file "COPYING" in the main directory of this archive
  8 + * for more details.
  9 + */
  10 +#include <linux/sh_clk.h>
  11 +#include <mach/clock.h>
  12 +#include "rsnd.h"
  13 +
  14 +#define CLKA 0
  15 +#define CLKB 1
  16 +#define CLKC 2
  17 +#define CLKI 3
  18 +#define CLKMAX 4
  19 +
  20 +struct rsnd_adg {
  21 + struct clk *clk[CLKMAX];
  22 +
  23 + int rate_of_441khz_div_6;
  24 + int rate_of_48khz_div_6;
  25 +};
  26 +
  27 +#define for_each_rsnd_clk(pos, adg, i) \
  28 + for (i = 0, (pos) = adg->clk[i]; \
  29 + i < CLKMAX; \
  30 + i++, (pos) = adg->clk[i])
  31 +#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
  32 +
  33 +static enum rsnd_reg rsnd_adg_ssi_reg_get(int id)
  34 +{
  35 + enum rsnd_reg reg;
  36 +
  37 + /*
  38 + * SSI 8 is not connected to ADG.
  39 + * it works with SSI 7
  40 + */
  41 + if (id == 8)
  42 + return RSND_REG_MAX;
  43 +
  44 + if (0 <= id && id <= 3)
  45 + reg = RSND_REG_AUDIO_CLK_SEL0;
  46 + else if (4 <= id && id <= 7)
  47 + reg = RSND_REG_AUDIO_CLK_SEL1;
  48 + else
  49 + reg = RSND_REG_AUDIO_CLK_SEL2;
  50 +
  51 + return reg;
  52 +}
  53 +
  54 +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
  55 +{
  56 + struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
  57 + enum rsnd_reg reg;
  58 + int id;
  59 +
  60 + /*
  61 + * "mod" = "ssi" here.
  62 + * we can get "ssi id" from mod
  63 + */
  64 + id = rsnd_mod_id(mod);
  65 + reg = rsnd_adg_ssi_reg_get(id);
  66 +
  67 + rsnd_write(priv, mod, reg, 0);
  68 +
  69 + return 0;
  70 +}
  71 +
  72 +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
  73 +{
  74 + struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
  75 + struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
  76 + struct device *dev = rsnd_priv_to_dev(priv);
  77 + struct clk *clk;
  78 + enum rsnd_reg reg;
  79 + int id, shift, i;
  80 + u32 data;
  81 + int sel_table[] = {
  82 + [CLKA] = 0x1,
  83 + [CLKB] = 0x2,
  84 + [CLKC] = 0x3,
  85 + [CLKI] = 0x0,
  86 + };
  87 +
  88 + dev_dbg(dev, "request clock = %d\n", rate);
  89 +
  90 + /*
  91 + * find suitable clock from
  92 + * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
  93 + */
  94 + data = 0;
  95 + for_each_rsnd_clk(clk, adg, i) {
  96 + if (rate == clk_get_rate(clk)) {
  97 + data = sel_table[i];
  98 + goto found_clock;
  99 + }
  100 + }
  101 +
  102 + /*
  103 + * find 1/6 clock from BRGA/BRGB
  104 + */
  105 + if (rate == adg->rate_of_441khz_div_6) {
  106 + data = 0x10;
  107 + goto found_clock;
  108 + }
  109 +
  110 + if (rate == adg->rate_of_48khz_div_6) {
  111 + data = 0x20;
  112 + goto found_clock;
  113 + }
  114 +
  115 + return -EIO;
  116 +
  117 +found_clock:
  118 +
  119 + /*
  120 + * This "mod" = "ssi" here.
  121 + * we can get "ssi id" from mod
  122 + */
  123 + id = rsnd_mod_id(mod);
  124 + reg = rsnd_adg_ssi_reg_get(id);
  125 +
  126 + dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate);
  127 +
  128 + /*
  129 + * Enable SSIx clock
  130 + */
  131 + shift = (id % 4) * 8;
  132 +
  133 + rsnd_bset(priv, mod, reg,
  134 + 0xFF << shift,
  135 + data << shift);
  136 +
  137 + return 0;
  138 +}
  139 +
  140 +static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
  141 +{
  142 + struct clk *clk;
  143 + unsigned long rate;
  144 + u32 ckr;
  145 + int i;
  146 + int brg_table[] = {
  147 + [CLKA] = 0x0,
  148 + [CLKB] = 0x1,
  149 + [CLKC] = 0x4,
  150 + [CLKI] = 0x2,
  151 + };
  152 +
  153 + /*
  154 + * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
  155 + * have 44.1kHz or 48kHz base clocks for now.
  156 + *
  157 + * SSI itself can divide parent clock by 1/1 - 1/16
  158 + * So, BRGA outputs 44.1kHz base parent clock 1/32,
  159 + * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
  160 + * see
  161 + * rsnd_adg_ssi_clk_try_start()
  162 + */
  163 + ckr = 0;
  164 + adg->rate_of_441khz_div_6 = 0;
  165 + adg->rate_of_48khz_div_6 = 0;
  166 + for_each_rsnd_clk(clk, adg, i) {
  167 + rate = clk_get_rate(clk);
  168 +
  169 + if (0 == rate) /* not used */
  170 + continue;
  171 +
  172 + /* RBGA */
  173 + if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) {
  174 + adg->rate_of_441khz_div_6 = rate / 6;
  175 + ckr |= brg_table[i] << 20;
  176 + }
  177 +
  178 + /* RBGB */
  179 + if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) {
  180 + adg->rate_of_48khz_div_6 = rate / 6;
  181 + ckr |= brg_table[i] << 16;
  182 + }
  183 + }
  184 +
  185 + rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr);
  186 + rsnd_priv_write(priv, BRRA, 0x00000002); /* 1/6 */
  187 + rsnd_priv_write(priv, BRRB, 0x00000002); /* 1/6 */
  188 +}
  189 +
  190 +int rsnd_adg_probe(struct platform_device *pdev,
  191 + struct rcar_snd_info *info,
  192 + struct rsnd_priv *priv)
  193 +{
  194 + struct rsnd_adg *adg;
  195 + struct device *dev = rsnd_priv_to_dev(priv);
  196 + struct clk *clk;
  197 + int i;
  198 +
  199 + adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
  200 + if (!adg) {
  201 + dev_err(dev, "ADG allocate failed\n");
  202 + return -ENOMEM;
  203 + }
  204 +
  205 + adg->clk[CLKA] = clk_get(NULL, "audio_clk_a");
  206 + adg->clk[CLKB] = clk_get(NULL, "audio_clk_b");
  207 + adg->clk[CLKC] = clk_get(NULL, "audio_clk_c");
  208 + adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal");
  209 + for_each_rsnd_clk(clk, adg, i) {
  210 + if (IS_ERR(clk)) {
  211 + dev_err(dev, "Audio clock failed\n");
  212 + return -EIO;
  213 + }
  214 + }
  215 +
  216 + rsnd_adg_ssi_clk_init(priv, adg);
  217 +
  218 + priv->adg = adg;
  219 +
  220 + dev_dbg(dev, "adg probed\n");
  221 +
  222 + return 0;
  223 +}
  224 +
  225 +void rsnd_adg_remove(struct platform_device *pdev,
  226 + struct rsnd_priv *priv)
  227 +{
  228 + struct rsnd_adg *adg = priv->adg;
  229 + struct clk *clk;
  230 + int i;
  231 +
  232 + for_each_rsnd_clk(clk, adg, i)
  233 + clk_put(clk);
  234 +}
sound/soc/sh/rcar/core.c
... ... @@ -635,6 +635,10 @@
635 635 if (ret < 0)
636 636 return ret;
637 637  
  638 + ret = rsnd_adg_probe(pdev, info, priv);
  639 + if (ret < 0)
  640 + return ret;
  641 +
638 642 /*
639 643 * asoc register
640 644 */
... ... @@ -673,6 +677,7 @@
673 677 /*
674 678 * remove each module
675 679 */
  680 + rsnd_adg_remove(pdev, priv);
676 681 rsnd_scu_remove(pdev, priv);
677 682 rsnd_dai_remove(pdev, priv);
678 683 rsnd_gen_remove(pdev, priv);
sound/soc/sh/rcar/gen.c
... ... @@ -111,6 +111,15 @@
111 111 {
112 112 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0);
113 113 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4);
  114 +
  115 + RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00);
  116 + RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04);
  117 + RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08);
  118 + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c);
  119 + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10);
  120 + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18);
  121 + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c);
  122 + RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20);
114 123 }
115 124  
116 125 static int rsnd_gen1_probe(struct platform_device *pdev,
117 126  
... ... @@ -120,12 +129,15 @@
120 129 struct device *dev = rsnd_priv_to_dev(priv);
121 130 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
122 131 struct resource *sru_res;
  132 + struct resource *adg_res;
123 133  
124 134 /*
125 135 * map address
126 136 */
127 137 sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
128   - if (!sru_res) {
  138 + adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
  139 + if (!sru_res ||
  140 + !adg_res) {
129 141 dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n");
130 142 return -ENODEV;
131 143 }
... ... @@ -133,7 +145,9 @@
133 145 gen->ops = &rsnd_gen1_ops;
134 146  
135 147 gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
136   - if (!gen->base[RSND_GEN1_SRU]) {
  148 + gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
  149 + if (!gen->base[RSND_GEN1_SRU] ||
  150 + !gen->base[RSND_GEN1_ADG]) {
137 151 dev_err(dev, "SRU/SSI/ADG ioremap failed\n");
138 152 return -ENODEV;
139 153 }
... ... @@ -143,6 +157,8 @@
143 157 dev_dbg(dev, "Gen1 device probed\n");
144 158 dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start,
145 159 gen->base[RSND_GEN1_SRU]);
  160 + dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start,
  161 + gen->base[RSND_GEN1_ADG]);
146 162  
147 163 return 0;
148 164 }
sound/soc/sh/rcar/rsnd.h
... ... @@ -32,6 +32,17 @@
32 32 RSND_REG_SSI_MODE0,
33 33 RSND_REG_SSI_MODE1,
34 34  
  35 + /* ADG */
  36 + RSND_REG_BRRA,
  37 + RSND_REG_BRRB,
  38 + RSND_REG_SSICKR,
  39 + RSND_REG_AUDIO_CLK_SEL0,
  40 + RSND_REG_AUDIO_CLK_SEL1,
  41 + RSND_REG_AUDIO_CLK_SEL2,
  42 + RSND_REG_AUDIO_CLK_SEL3,
  43 + RSND_REG_AUDIO_CLK_SEL4,
  44 + RSND_REG_AUDIO_CLK_SEL5,
  45 +
35 46 RSND_REG_MAX,
36 47 };
37 48  
... ... @@ -163,6 +174,17 @@
163 174 enum rsnd_reg reg);
164 175  
165 176 /*
  177 + * R-Car ADG
  178 + */
  179 +int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
  180 +int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
  181 +int rsnd_adg_probe(struct platform_device *pdev,
  182 + struct rcar_snd_info *info,
  183 + struct rsnd_priv *priv);
  184 +void rsnd_adg_remove(struct platform_device *pdev,
  185 + struct rsnd_priv *priv);
  186 +
  187 +/*
166 188 * R-Car sound priv
167 189 */
168 190 struct rsnd_priv {
... ... @@ -181,6 +203,11 @@
181 203 */
182 204 void *scu;
183 205 int scu_nr;
  206 +
  207 + /*
  208 + * below value will be filled on rsnd_adg_probe()
  209 + */
  210 + void *adg;
184 211  
185 212 /*
186 213 * below value will be filled on rsnd_dai_probe()