diff options
Diffstat (limited to 'sound/soc/sh/rcar/adg.c')
-rw-r--r-- | sound/soc/sh/rcar/adg.c | 228 |
1 files changed, 192 insertions, 36 deletions
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c index a53235c4d1b0..953f1cce982d 100644 --- a/sound/soc/sh/rcar/adg.c +++ b/sound/soc/sh/rcar/adg.c | |||
@@ -25,15 +25,165 @@ struct rsnd_adg { | |||
25 | }; | 25 | }; |
26 | 26 | ||
27 | #define for_each_rsnd_clk(pos, adg, i) \ | 27 | #define for_each_rsnd_clk(pos, adg, i) \ |
28 | for (i = 0, (pos) = adg->clk[i]; \ | 28 | for (i = 0; \ |
29 | i < CLKMAX; \ | 29 | (i < CLKMAX) && \ |
30 | i++, (pos) = adg->clk[i]) | 30 | ((pos) = adg->clk[i]); \ |
31 | i++) | ||
31 | #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) | 32 | #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg) |
32 | 33 | ||
33 | static int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, | 34 | |
34 | struct rsnd_mod *mod, | 35 | static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io) |
35 | unsigned int src_rate, | 36 | { |
36 | unsigned int dst_rate) | 37 | struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io); |
38 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
39 | int id = rsnd_mod_id(mod); | ||
40 | int ws = id; | ||
41 | |||
42 | if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) { | ||
43 | switch (id) { | ||
44 | case 1: | ||
45 | case 2: | ||
46 | ws = 0; | ||
47 | break; | ||
48 | case 4: | ||
49 | ws = 3; | ||
50 | break; | ||
51 | case 8: | ||
52 | ws = 7; | ||
53 | break; | ||
54 | } | ||
55 | } | ||
56 | |||
57 | return (0x6 + ws) << 8; | ||
58 | } | ||
59 | |||
60 | static int rsnd_adg_set_src_timsel_gen2(struct rsnd_dai *rdai, | ||
61 | struct rsnd_mod *mod, | ||
62 | struct rsnd_dai_stream *io, | ||
63 | u32 timsel) | ||
64 | { | ||
65 | int is_play = rsnd_dai_is_play(rdai, io); | ||
66 | int id = rsnd_mod_id(mod); | ||
67 | int shift = (id % 2) ? 16 : 0; | ||
68 | u32 mask, ws; | ||
69 | u32 in, out; | ||
70 | |||
71 | ws = rsnd_adg_ssi_ws_timing_gen2(io); | ||
72 | |||
73 | in = (is_play) ? timsel : ws; | ||
74 | out = (is_play) ? ws : timsel; | ||
75 | |||
76 | in = in << shift; | ||
77 | out = out << shift; | ||
78 | mask = 0xffff << shift; | ||
79 | |||
80 | switch (id / 2) { | ||
81 | case 0: | ||
82 | rsnd_mod_bset(mod, SRCIN_TIMSEL0, mask, in); | ||
83 | rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out); | ||
84 | break; | ||
85 | case 1: | ||
86 | rsnd_mod_bset(mod, SRCIN_TIMSEL1, mask, in); | ||
87 | rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out); | ||
88 | break; | ||
89 | case 2: | ||
90 | rsnd_mod_bset(mod, SRCIN_TIMSEL2, mask, in); | ||
91 | rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out); | ||
92 | break; | ||
93 | case 3: | ||
94 | rsnd_mod_bset(mod, SRCIN_TIMSEL3, mask, in); | ||
95 | rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out); | ||
96 | break; | ||
97 | case 4: | ||
98 | rsnd_mod_bset(mod, SRCIN_TIMSEL4, mask, in); | ||
99 | rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out); | ||
100 | break; | ||
101 | } | ||
102 | |||
103 | return 0; | ||
104 | } | ||
105 | |||
106 | int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod, | ||
107 | struct rsnd_dai *rdai, | ||
108 | struct rsnd_dai_stream *io, | ||
109 | unsigned int src_rate, | ||
110 | unsigned int dst_rate) | ||
111 | { | ||
112 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
113 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); | ||
114 | struct device *dev = rsnd_priv_to_dev(priv); | ||
115 | int idx, sel, div, step, ret; | ||
116 | u32 val, en; | ||
117 | unsigned int min, diff; | ||
118 | unsigned int sel_rate [] = { | ||
119 | clk_get_rate(adg->clk[CLKA]), /* 0000: CLKA */ | ||
120 | clk_get_rate(adg->clk[CLKB]), /* 0001: CLKB */ | ||
121 | clk_get_rate(adg->clk[CLKC]), /* 0010: CLKC */ | ||
122 | adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */ | ||
123 | adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */ | ||
124 | }; | ||
125 | |||
126 | min = ~0; | ||
127 | val = 0; | ||
128 | en = 0; | ||
129 | for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) { | ||
130 | idx = 0; | ||
131 | step = 2; | ||
132 | |||
133 | if (!sel_rate[sel]) | ||
134 | continue; | ||
135 | |||
136 | for (div = 2; div <= 98304; div += step) { | ||
137 | diff = abs(src_rate - sel_rate[sel] / div); | ||
138 | if (min > diff) { | ||
139 | val = (sel << 8) | idx; | ||
140 | min = diff; | ||
141 | en = 1 << (sel + 1); /* fixme */ | ||
142 | } | ||
143 | |||
144 | /* | ||
145 | * step of 0_0000 / 0_0001 / 0_1101 | ||
146 | * are out of order | ||
147 | */ | ||
148 | if ((idx > 2) && (idx % 2)) | ||
149 | step *= 2; | ||
150 | if (idx == 0x1c) { | ||
151 | div += step; | ||
152 | step *= 2; | ||
153 | } | ||
154 | idx++; | ||
155 | } | ||
156 | } | ||
157 | |||
158 | if (min == ~0) { | ||
159 | dev_err(dev, "no Input clock\n"); | ||
160 | return -EIO; | ||
161 | } | ||
162 | |||
163 | ret = rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); | ||
164 | if (ret < 0) { | ||
165 | dev_err(dev, "timsel error\n"); | ||
166 | return ret; | ||
167 | } | ||
168 | |||
169 | rsnd_mod_bset(mod, DIV_EN, en, en); | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod, | ||
175 | struct rsnd_dai *rdai, | ||
176 | struct rsnd_dai_stream *io) | ||
177 | { | ||
178 | u32 val = rsnd_adg_ssi_ws_timing_gen2(io); | ||
179 | |||
180 | return rsnd_adg_set_src_timsel_gen2(rdai, mod, io, val); | ||
181 | } | ||
182 | |||
183 | int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv, | ||
184 | struct rsnd_mod *mod, | ||
185 | unsigned int src_rate, | ||
186 | unsigned int dst_rate) | ||
37 | { | 187 | { |
38 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); | 188 | struct rsnd_adg *adg = rsnd_priv_to_adg(priv); |
39 | struct device *dev = rsnd_priv_to_dev(priv); | 189 | struct device *dev = rsnd_priv_to_dev(priv); |
@@ -91,18 +241,6 @@ find_rate: | |||
91 | return 0; | 241 | return 0; |
92 | } | 242 | } |
93 | 243 | ||
94 | int rsnd_adg_set_convert_clk(struct rsnd_priv *priv, | ||
95 | struct rsnd_mod *mod, | ||
96 | unsigned int src_rate, | ||
97 | unsigned int dst_rate) | ||
98 | { | ||
99 | if (rsnd_is_gen1(priv)) | ||
100 | return rsnd_adg_set_convert_clk_gen1(priv, mod, | ||
101 | src_rate, dst_rate); | ||
102 | |||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) | 244 | static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val) |
107 | { | 245 | { |
108 | int id = rsnd_mod_id(mod); | 246 | int id = rsnd_mod_id(mod); |
@@ -254,13 +392,13 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg) | |||
254 | } | 392 | } |
255 | 393 | ||
256 | int rsnd_adg_probe(struct platform_device *pdev, | 394 | int rsnd_adg_probe(struct platform_device *pdev, |
257 | struct rcar_snd_info *info, | ||
258 | struct rsnd_priv *priv) | 395 | struct rsnd_priv *priv) |
259 | { | 396 | { |
260 | struct rsnd_adg *adg; | 397 | struct rsnd_adg *adg; |
261 | struct device *dev = rsnd_priv_to_dev(priv); | 398 | struct device *dev = rsnd_priv_to_dev(priv); |
262 | struct clk *clk; | 399 | struct clk *clk, *clk_orig; |
263 | int i; | 400 | int i; |
401 | bool use_old_style = false; | ||
264 | 402 | ||
265 | adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); | 403 | adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL); |
266 | if (!adg) { | 404 | if (!adg) { |
@@ -268,10 +406,39 @@ int rsnd_adg_probe(struct platform_device *pdev, | |||
268 | return -ENOMEM; | 406 | return -ENOMEM; |
269 | } | 407 | } |
270 | 408 | ||
271 | adg->clk[CLKA] = clk_get(NULL, "audio_clk_a"); | 409 | clk_orig = devm_clk_get(dev, NULL); |
272 | adg->clk[CLKB] = clk_get(NULL, "audio_clk_b"); | 410 | adg->clk[CLKA] = devm_clk_get(dev, "clk_a"); |
273 | adg->clk[CLKC] = clk_get(NULL, "audio_clk_c"); | 411 | adg->clk[CLKB] = devm_clk_get(dev, "clk_b"); |
274 | adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal"); | 412 | adg->clk[CLKC] = devm_clk_get(dev, "clk_c"); |
413 | adg->clk[CLKI] = devm_clk_get(dev, "clk_i"); | ||
414 | |||
415 | /* | ||
416 | * It request device dependent audio clock. | ||
417 | * But above all clks will indicate rsnd module clock | ||
418 | * if platform doesn't it | ||
419 | */ | ||
420 | for_each_rsnd_clk(clk, adg, i) { | ||
421 | if (clk_orig == clk) { | ||
422 | dev_warn(dev, | ||
423 | "doesn't have device dependent clock, use independent clock\n"); | ||
424 | use_old_style = true; | ||
425 | break; | ||
426 | } | ||
427 | } | ||
428 | |||
429 | /* | ||
430 | * note: | ||
431 | * these exist in order to keep compatible with | ||
432 | * platform which has device independent audio clock, | ||
433 | * but will be removed soon | ||
434 | */ | ||
435 | if (use_old_style) { | ||
436 | adg->clk[CLKA] = devm_clk_get(NULL, "audio_clk_a"); | ||
437 | adg->clk[CLKB] = devm_clk_get(NULL, "audio_clk_b"); | ||
438 | adg->clk[CLKC] = devm_clk_get(NULL, "audio_clk_c"); | ||
439 | adg->clk[CLKI] = devm_clk_get(NULL, "audio_clk_internal"); | ||
440 | } | ||
441 | |||
275 | for_each_rsnd_clk(clk, adg, i) { | 442 | for_each_rsnd_clk(clk, adg, i) { |
276 | if (IS_ERR(clk)) { | 443 | if (IS_ERR(clk)) { |
277 | dev_err(dev, "Audio clock failed\n"); | 444 | dev_err(dev, "Audio clock failed\n"); |
@@ -287,14 +454,3 @@ int rsnd_adg_probe(struct platform_device *pdev, | |||
287 | 454 | ||
288 | return 0; | 455 | return 0; |
289 | } | 456 | } |
290 | |||
291 | void rsnd_adg_remove(struct platform_device *pdev, | ||
292 | struct rsnd_priv *priv) | ||
293 | { | ||
294 | struct rsnd_adg *adg = priv->adg; | ||
295 | struct clk *clk; | ||
296 | int i; | ||
297 | |||
298 | for_each_rsnd_clk(clk, adg, i) | ||
299 | clk_put(clk); | ||
300 | } | ||