diff options
author | Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | 2016-03-07 00:09:14 -0500 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-03-07 02:41:54 -0500 |
commit | 0102eed57c47371023c03b3b0c564f33d5e94570 (patch) | |
tree | c8c20d2ae4dfda733c23003ea1d3c1d2d7732d6c | |
parent | cbf1494fbcc80d363477af1efefb2380e7660a24 (diff) |
ASoC: rsnd: SRC TIMSEL support for Capture
SRC has Sync/Async mode, and it can't use Sync mode when Capture
with CMD. In Async mode, it needs to care about in/out SRC rate
for settings, but current driver supporting Playback case only.
This patch supports Capture case.
Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
-rw-r--r-- | sound/soc/sh/rcar/adg.c | 204 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 8 | ||||
-rw-r--r-- | sound/soc/sh/rcar/src.c | 26 |
3 files changed, 128 insertions, 110 deletions
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index d74e1ccc0f8f..f7e164c89f33 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c | |||
@@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) | |||
90 | return (0x6 + ws) << 8; | 90 | return (0x6 + ws) << 8; |
91 | } | 91 | } |
92 | 92 | ||
93 | static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, | ||
94 | struct rsnd_dai_stream *io, | ||
95 | unsigned int target_rate, | ||
96 | unsigned int *target_val, | ||
97 | unsigned int *target_en) | ||
98 | { | ||
99 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); | ||
100 | struct device *dev = rsnd_priv_to_dev(priv); | ||
101 | int idx, sel, div, step; | ||
102 | unsigned int val, en; | ||
103 | unsigned int min, diff; | ||
104 | unsigned int sel_rate[] = { | ||
105 | clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ | ||
106 | clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ | ||
107 | clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ | ||
108 | adg->rbga_rate_for_441khz, /* 0011: RBGA */ | ||
109 | adg->rbgb_rate_for_48khz, /* 0100: RBGB */ | ||
110 | }; | ||
111 | |||
112 | min = ~0; | ||
113 | val = 0; | ||
114 | en = 0; | ||
115 | for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { | ||
116 | idx = 0; | ||
117 | step = 2; | ||
118 | |||
119 | if (!sel_rate[sel]) | ||
120 | continue; | ||
121 | |||
122 | for (div = 2; div <= 98304; div += step) { | ||
123 | diff = abs(target_rate - sel_rate[sel] / div); | ||
124 | if (min > diff) { | ||
125 | val = (sel << 8) | idx; | ||
126 | min = diff; | ||
127 | en = 1 << (sel + 1); /* fixme */ | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * step of 0_0000 / 0_0001 / 0_1101 | ||
132 | * are out of order | ||
133 | */ | ||
134 | if ((idx > 2) && (idx % 2)) | ||
135 | step *= 2; | ||
136 | if (idx == 0x1c) { | ||
137 | div += step; | ||
138 | step *= 2; | ||
139 | } | ||
140 | idx++; | ||
141 | } | ||
142 | } | ||
143 | |||
144 | if (min == ~0) { | ||
145 | dev_err(dev, "no Input clock\n"); | ||
146 | return; | ||
147 | } | ||
148 | |||
149 | *target_val = val; | ||
150 | if (target_en) | ||
151 | *target_en = en; | ||
152 | } | ||
153 | |||
154 | static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv, | ||
155 | struct rsnd_dai_stream *io, | ||
156 | unsigned int in_rate, | ||
157 | unsigned int out_rate, | ||
158 | u32 *in, u32 *out, u32 *en) | ||
159 | { | ||
160 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
161 | unsigned int target_rate; | ||
162 | u32 *target_val; | ||
163 | u32 _in; | ||
164 | u32 _out; | ||
165 | u32 _en; | ||
166 | |||
167 | /* default = SSI WS */ | ||
168 | _in = | ||
169 | _out = rsnd_adg_ssi_ws_timing_gen2(io); | ||
170 | |||
171 | target_rate = 0; | ||
172 | target_val = NULL; | ||
173 | _en = 0; | ||
174 | if (runtime->rate != in_rate) { | ||
175 | target_rate = out_rate; | ||
176 | target_val = &_out; | ||
177 | } else if (runtime->rate != out_rate) { | ||
178 | target_rate = in_rate; | ||
179 | target_val = &_in; | ||
180 | } | ||
181 | |||
182 | if (target_rate) | ||
183 | __rsnd_adg_get_timesel_ratio(priv, io, | ||
184 | target_rate, | ||
185 | target_val, &_en); | ||
186 | |||
187 | if (in) | ||
188 | *in = _in; | ||
189 | if (out) | ||
190 | *out = _out; | ||
191 | if (en) | ||
192 | *en = _en; | ||
193 | } | ||
194 | |||
93 | int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, | 195 | int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, |
94 | struct rsnd_dai_stream *io) | 196 | struct rsnd_dai_stream *io) |
95 | { | 197 | { |
@@ -110,25 +212,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod, | |||
110 | return 0; | 212 | return 0; |
111 | } | 213 | } |
112 | 214 | ||
113 | static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, | 215 | int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, |
114 | struct rsnd_dai_stream *io, | 216 | struct rsnd_dai_stream *io, |
115 | u32 timsel) | 217 | unsigned int in_rate, |
218 | unsigned int out_rate) | ||
116 | { | 219 | { |
117 | struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); | 220 | struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); |
118 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); | 221 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); |
119 | struct rsnd_mod *adg_mod = rsnd_mod_get(adg); | 222 | struct rsnd_mod *adg_mod = rsnd_mod_get(adg); |
120 | int is_play = rsnd_io_is_play(io); | 223 | u32 in, out; |
224 | u32 mask, en; | ||
121 | int id = rsnd_mod_id(src_mod); | 225 | int id = rsnd_mod_id(src_mod); |
122 | int shift = (id % 2) ? 16 : 0; | 226 | int shift = (id % 2) ? 16 : 0; |
123 | u32 mask, ws; | ||
124 | u32 in, out; | ||
125 | 227 | ||
126 | rsnd_mod_confirm_src(src_mod); | 228 | rsnd_mod_confirm_src(src_mod); |
127 | 229 | ||
128 | ws = rsnd_adg_ssi_ws_timing_gen2(io); | 230 | rsnd_adg_get_timesel_ratio(priv, io, |
129 | 231 | in_rate, out_rate, | |
130 | in = (is_play) ? timsel : ws; | 232 | &in, &out, &en); |
131 | out = (is_play) ? ws : timsel; | ||
132 | 233 | ||
133 | in = in << shift; | 234 | in = in << shift; |
134 | out = out << shift; | 235 | out = out << shift; |
@@ -157,91 +258,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod, | |||
157 | break; | 258 | break; |
158 | } | 259 | } |
159 | 260 | ||
160 | return 0; | 261 | if (en) |
161 | } | 262 | rsnd_mod_bset(adg_mod, DIV_EN, en, en); |
162 | |||
163 | int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod, | ||
164 | struct rsnd_dai_stream *io, | ||
165 | unsigned int src_rate, | ||
166 | unsigned int dst_rate) | ||
167 | { | ||
168 | struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod); | ||
169 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); | ||
170 | struct rsnd_mod *adg_mod = rsnd_mod_get(adg); | ||
171 | struct device *dev = rsnd_priv_to_dev(priv); | ||
172 | int idx, sel, div, step, ret; | ||
173 | u32 val, en; | ||
174 | unsigned int min, diff; | ||
175 | unsigned int sel_rate [] = { | ||
176 | clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ | ||
177 | clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ | ||
178 | clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ | ||
179 | adg->rbga_rate_for_441khz, /* 0011: RBGA */ | ||
180 | adg->rbgb_rate_for_48khz, /* 0100: RBGB */ | ||
181 | }; | ||
182 | |||
183 | rsnd_mod_confirm_src(src_mod); | ||
184 | |||
185 | min = ~0; | ||
186 | val = 0; | ||
187 | en = 0; | ||
188 | for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { | ||
189 | idx = 0; | ||
190 | step = 2; | ||
191 | |||
192 | if (!sel_rate[sel]) | ||
193 | continue; | ||
194 | |||
195 | for (div = 2; div <= 98304; div += step) { | ||
196 | diff = abs(src_rate - sel_rate[sel] / div); | ||
197 | if (min > diff) { | ||
198 | val = (sel << 8) | idx; | ||
199 | min = diff; | ||
200 | en = 1 << (sel + 1); /* fixme */ | ||
201 | } | ||
202 | |||
203 | /* | ||
204 | * step of 0_0000 / 0_0001 / 0_1101 | ||
205 | * are out of order | ||
206 | */ | ||
207 | if ((idx > 2) && (idx % 2)) | ||
208 | step *= 2; | ||
209 | if (idx == 0x1c) { | ||
210 | div += step; | ||
211 | step *= 2; | ||
212 | } | ||
213 | idx++; | ||
214 | } | ||
215 | } | ||
216 | |||
217 | if (min == ~0) { | ||
218 | dev_err(dev, "no Input clock\n"); | ||
219 | return -EIO; | ||
220 | } | ||
221 | |||
222 | ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val); | ||
223 | if (ret < 0) { | ||
224 | dev_err(dev, "timsel error\n"); | ||
225 | return ret; | ||
226 | } | ||
227 | |||
228 | rsnd_mod_bset(adg_mod, DIV_EN, en, en); | ||
229 | |||
230 | dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate); | ||
231 | 263 | ||
232 | return 0; | 264 | return 0; |
233 | } | 265 | } |
234 | 266 | ||
235 | int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod, | ||
236 | struct rsnd_dai_stream *io) | ||
237 | { | ||
238 | u32 val = rsnd_adg_ssi_ws_timing_gen2(io); | ||
239 | |||
240 | rsnd_mod_confirm_src(src_mod); | ||
241 | |||
242 | return rsnd_adg_set_src_timsel_gen2(src_mod, io, val); | ||
243 | } | ||
244 | |||
245 | static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) | 267 | static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val) |
246 | { | 268 | { |
247 | struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); | 269 | struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod); |
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h index 4b77f33358fb..fc89a67258ca 100644 --- a/sound/soc/sh/rcar/rsnd.h +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -446,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); | |||
446 | int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); | 446 | int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); |
447 | int rsnd_adg_probe(struct rsnd_priv *priv); | 447 | int rsnd_adg_probe(struct rsnd_priv *priv); |
448 | void rsnd_adg_remove(struct rsnd_priv *priv); | 448 | void rsnd_adg_remove(struct rsnd_priv *priv); |
449 | int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, | 449 | int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod, |
450 | struct rsnd_dai_stream *io, | 450 | struct rsnd_dai_stream *io, |
451 | unsigned int src_rate, | 451 | unsigned int in_rate, |
452 | unsigned int dst_rate); | 452 | unsigned int out_rate); |
453 | int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, | ||
454 | struct rsnd_dai_stream *io); | ||
455 | int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, | 453 | int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod, |
456 | struct rsnd_dai_stream *io); | 454 | struct rsnd_dai_stream *io); |
457 | 455 | ||
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c index d1a8741cc446..15d6ffe8be74 100644 --- a/sound/soc/sh/rcar/src.c +++ b/sound/soc/sh/rcar/src.c | |||
@@ -189,7 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, | |||
189 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | 189 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); |
190 | struct device *dev = rsnd_priv_to_dev(priv); | 190 | struct device *dev = rsnd_priv_to_dev(priv); |
191 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | 191 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); |
192 | u32 convert_rate = rsnd_src_convert_rate(io, mod); | 192 | u32 fin, fout; |
193 | u32 ifscr, fsrate, adinr; | 193 | u32 ifscr, fsrate, adinr; |
194 | u32 cr, route; | 194 | u32 cr, route; |
195 | u32 bsdsr, bsisr; | 195 | u32 bsdsr, bsisr; |
@@ -198,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, | |||
198 | if (!runtime) | 198 | if (!runtime) |
199 | return; | 199 | return; |
200 | 200 | ||
201 | fin = rsnd_src_get_in_rate(priv, io); | ||
202 | fout = rsnd_src_get_out_rate(priv, io); | ||
203 | |||
201 | /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ | 204 | /* 6 - 1/6 are very enough ratio for SRC_BSDSR */ |
202 | if (!convert_rate) | 205 | if (fin == fout) |
203 | ratio = 0; | 206 | ratio = 0; |
204 | else if (convert_rate > runtime->rate) | 207 | else if (fin > fout) |
205 | ratio = 100 * convert_rate / runtime->rate; | 208 | ratio = 100 * fin / fout; |
206 | else | 209 | else |
207 | ratio = 100 * runtime->rate / convert_rate; | 210 | ratio = 100 * fout / fin; |
208 | 211 | ||
209 | if (ratio > 600) { | 212 | if (ratio > 600) { |
210 | dev_err(dev, "FSO/FSI ratio error\n"); | 213 | dev_err(dev, "FSO/FSI ratio error\n"); |
@@ -222,9 +225,9 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, | |||
222 | */ | 225 | */ |
223 | ifscr = 0; | 226 | ifscr = 0; |
224 | fsrate = 0; | 227 | fsrate = 0; |
225 | if (convert_rate) { | 228 | if (fin != fout) { |
226 | ifscr = 1; | 229 | ifscr = 1; |
227 | fsrate = 0x0400000 / convert_rate * runtime->rate; | 230 | fsrate = 0x0400000 / fout * fin; |
228 | } | 231 | } |
229 | 232 | ||
230 | /* | 233 | /* |
@@ -232,7 +235,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, | |||
232 | */ | 235 | */ |
233 | cr = 0x00011110; | 236 | cr = 0x00011110; |
234 | route = 0x0; | 237 | route = 0x0; |
235 | if (convert_rate) { | 238 | if (fin != fout) { |
236 | route = 0x1; | 239 | route = 0x1; |
237 | 240 | ||
238 | if (rsnd_src_sync_is_enabled(mod)) { | 241 | if (rsnd_src_sync_is_enabled(mod)) { |
@@ -274,12 +277,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io, | |||
274 | rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); | 277 | rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1); |
275 | rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); | 278 | rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io)); |
276 | 279 | ||
277 | if (convert_rate) | 280 | rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout); |
278 | rsnd_adg_set_convert_clk_gen2(mod, io, | ||
279 | runtime->rate, | ||
280 | convert_rate); | ||
281 | else | ||
282 | rsnd_adg_set_convert_timing_gen2(mod, io); | ||
283 | } | 281 | } |
284 | 282 | ||
285 | static int rsnd_src_irq(struct rsnd_mod *mod, | 283 | static int rsnd_src_irq(struct rsnd_mod *mod, |