aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/sh
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2013-07-22 00:36:46 -0400
committerMark Brown <broonie@linaro.org>2013-07-28 14:34:09 -0400
commitdfc9403b7c1f566bb099a12c58aee20589e390f1 (patch)
treecc868032b3a32cdefeded82cd13ad0d61cfb93c1 /sound/soc/sh
parent07539c1de82cdc0ecbe72b413762b2e920407227 (diff)
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>
Diffstat (limited to 'sound/soc/sh')
-rw-r--r--sound/soc/sh/rcar/Makefile2
-rw-r--r--sound/soc/sh/rcar/adg.c234
-rw-r--r--sound/soc/sh/rcar/core.c5
-rw-r--r--sound/soc/sh/rcar/gen.c20
-rw-r--r--sound/soc/sh/rcar/rsnd.h27
5 files changed, 285 insertions, 3 deletions
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
index 112b2cfd793b..c11280cffcfe 100644
--- a/sound/soc/sh/rcar/Makefile
+++ b/sound/soc/sh/rcar/Makefile
@@ -1,2 +1,2 @@
1snd-soc-rcar-objs := core.o gen.o scu.o 1snd-soc-rcar-objs := core.o gen.o scu.o adg.o
2obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file 2obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o \ No newline at end of file
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
new file mode 100644
index 000000000000..d80deb7ccf13
--- /dev/null
+++ b/sound/soc/sh/rcar/adg.c
@@ -0,0 +1,234 @@
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
20struct 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
33static 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
54int 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
72int 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
117found_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
140static 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
190int 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
225void 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}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 02d736bb4f54..e588d8a8ae40 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -635,6 +635,10 @@ static int rsnd_probe(struct platform_device *pdev)
635 if (ret < 0) 635 if (ret < 0)
636 return ret; 636 return ret;
637 637
638 ret = rsnd_adg_probe(pdev, info, priv);
639 if (ret < 0)
640 return ret;
641
638 /* 642 /*
639 * asoc register 643 * asoc register
640 */ 644 */
@@ -673,6 +677,7 @@ static int rsnd_remove(struct platform_device *pdev)
673 /* 677 /*
674 * remove each module 678 * remove each module
675 */ 679 */
680 rsnd_adg_remove(pdev, priv);
676 rsnd_scu_remove(pdev, priv); 681 rsnd_scu_remove(pdev, priv);
677 rsnd_dai_remove(pdev, priv); 682 rsnd_dai_remove(pdev, priv);
678 rsnd_gen_remove(pdev, priv); 683 rsnd_gen_remove(pdev, priv);
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 2934c0d731c8..ed21a136354f 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -111,6 +111,15 @@ static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
111{ 111{
112 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); 112 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0);
113 RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); 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
116static int rsnd_gen1_probe(struct platform_device *pdev, 125static int rsnd_gen1_probe(struct platform_device *pdev,
@@ -120,12 +129,15 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
120 struct device *dev = rsnd_priv_to_dev(priv); 129 struct device *dev = rsnd_priv_to_dev(priv);
121 struct rsnd_gen *gen = rsnd_priv_to_gen(priv); 130 struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
122 struct resource *sru_res; 131 struct resource *sru_res;
132 struct resource *adg_res;
123 133
124 /* 134 /*
125 * map address 135 * map address
126 */ 136 */
127 sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); 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 dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n"); 141 dev_err(dev, "Not enough SRU/SSI/ADG platform resources.\n");
130 return -ENODEV; 142 return -ENODEV;
131 } 143 }
@@ -133,7 +145,9 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
133 gen->ops = &rsnd_gen1_ops; 145 gen->ops = &rsnd_gen1_ops;
134 146
135 gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); 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 dev_err(dev, "SRU/SSI/ADG ioremap failed\n"); 151 dev_err(dev, "SRU/SSI/ADG ioremap failed\n");
138 return -ENODEV; 152 return -ENODEV;
139 } 153 }
@@ -143,6 +157,8 @@ static int rsnd_gen1_probe(struct platform_device *pdev,
143 dev_dbg(dev, "Gen1 device probed\n"); 157 dev_dbg(dev, "Gen1 device probed\n");
144 dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, 158 dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start,
145 gen->base[RSND_GEN1_SRU]); 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 return 0; 163 return 0;
148} 164}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 95a391ff0627..344fd59cb7fd 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -32,6 +32,17 @@ enum rsnd_reg {
32 RSND_REG_SSI_MODE0, 32 RSND_REG_SSI_MODE0,
33 RSND_REG_SSI_MODE1, 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 RSND_REG_MAX, 46 RSND_REG_MAX,
36}; 47};
37 48
@@ -163,6 +174,17 @@ void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
163 enum rsnd_reg reg); 174 enum rsnd_reg reg);
164 175
165/* 176/*
177 * R-Car ADG
178 */
179int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
180int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
181int rsnd_adg_probe(struct platform_device *pdev,
182 struct rcar_snd_info *info,
183 struct rsnd_priv *priv);
184void rsnd_adg_remove(struct platform_device *pdev,
185 struct rsnd_priv *priv);
186
187/*
166 * R-Car sound priv 188 * R-Car sound priv
167 */ 189 */
168struct rsnd_priv { 190struct rsnd_priv {
@@ -183,6 +205,11 @@ struct rsnd_priv {
183 int scu_nr; 205 int scu_nr;
184 206
185 /* 207 /*
208 * below value will be filled on rsnd_adg_probe()
209 */
210 void *adg;
211
212 /*
186 * below value will be filled on rsnd_dai_probe() 213 * below value will be filled on rsnd_dai_probe()
187 */ 214 */
188 struct snd_soc_dai_driver *daidrv; 215 struct snd_soc_dai_driver *daidrv;