diff options
author | Mark Brown <broonie@linaro.org> | 2013-08-22 09:28:47 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2013-08-22 09:28:47 -0400 |
commit | b008387ab5f5369f925649d0201ace5055008530 (patch) | |
tree | abc9571bb77201fe97088d9f6cb24659b4e6d439 | |
parent | 5388d48047def3c30e42c7ba86c3c9ff0d2bd40c (diff) | |
parent | 2460719c79854a3bebe569cbfbfa0b1caa1dc434 (diff) |
Merge remote-tracking branch 'asoc/topic/rcar' into asoc-next
-rw-r--r-- | include/sound/rcar_snd.h | 84 | ||||
-rw-r--r-- | sound/soc/sh/Kconfig | 7 | ||||
-rw-r--r-- | sound/soc/sh/Makefile | 3 | ||||
-rw-r--r-- | sound/soc/sh/rcar/Makefile | 2 | ||||
-rw-r--r-- | sound/soc/sh/rcar/adg.c | 234 | ||||
-rw-r--r-- | sound/soc/sh/rcar/core.c | 861 | ||||
-rw-r--r-- | sound/soc/sh/rcar/gen.c | 280 | ||||
-rw-r--r-- | sound/soc/sh/rcar/rsnd.h | 302 | ||||
-rw-r--r-- | sound/soc/sh/rcar/scu.c | 236 | ||||
-rw-r--r-- | sound/soc/sh/rcar/ssi.c | 728 |
10 files changed, 2737 insertions, 0 deletions
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h new file mode 100644 index 000000000000..d35412ae03b3 --- /dev/null +++ b/include/sound/rcar_snd.h | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * Renesas R-Car SRU/SCU/SSIU/SSI support | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #ifndef RCAR_SND_H | ||
13 | #define RCAR_SND_H | ||
14 | |||
15 | #include <linux/sh_clk.h> | ||
16 | |||
17 | #define RSND_GEN1_SRU 0 | ||
18 | #define RSND_GEN1_ADG 1 | ||
19 | #define RSND_GEN1_SSI 2 | ||
20 | |||
21 | #define RSND_GEN2_SRU 0 | ||
22 | #define RSND_GEN2_ADG 1 | ||
23 | #define RSND_GEN2_SSIU 2 | ||
24 | #define RSND_GEN2_SSI 3 | ||
25 | |||
26 | #define RSND_BASE_MAX 4 | ||
27 | |||
28 | /* | ||
29 | * flags | ||
30 | * | ||
31 | * 0xAB000000 | ||
32 | * | ||
33 | * A : clock sharing settings | ||
34 | * B : SSI direction | ||
35 | */ | ||
36 | #define RSND_SSI_CLK_PIN_SHARE (1 << 31) | ||
37 | #define RSND_SSI_CLK_FROM_ADG (1 << 30) /* clock parent is master */ | ||
38 | #define RSND_SSI_SYNC (1 << 29) /* SSI34_sync etc */ | ||
39 | #define RSND_SSI_DEPENDENT (1 << 28) /* SSI needs SRU/SCU */ | ||
40 | |||
41 | #define RSND_SSI_PLAY (1 << 24) | ||
42 | |||
43 | #define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags) \ | ||
44 | { .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags } | ||
45 | #define RSND_SSI_UNUSED \ | ||
46 | { .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 } | ||
47 | |||
48 | struct rsnd_ssi_platform_info { | ||
49 | int dai_id; | ||
50 | int dma_id; | ||
51 | int pio_irq; | ||
52 | u32 flags; | ||
53 | }; | ||
54 | |||
55 | /* | ||
56 | * flags | ||
57 | */ | ||
58 | #define RSND_SCU_USB_HPBIF (1 << 31) /* it needs RSND_SSI_DEPENDENT */ | ||
59 | |||
60 | struct rsnd_scu_platform_info { | ||
61 | u32 flags; | ||
62 | }; | ||
63 | |||
64 | /* | ||
65 | * flags | ||
66 | * | ||
67 | * 0x0000000A | ||
68 | * | ||
69 | * A : generation | ||
70 | */ | ||
71 | #define RSND_GEN1 (1 << 0) /* fixme */ | ||
72 | #define RSND_GEN2 (2 << 0) /* fixme */ | ||
73 | |||
74 | struct rcar_snd_info { | ||
75 | u32 flags; | ||
76 | struct rsnd_ssi_platform_info *ssi_info; | ||
77 | int ssi_info_nr; | ||
78 | struct rsnd_scu_platform_info *scu_info; | ||
79 | int scu_info_nr; | ||
80 | int (*start)(int id); | ||
81 | int (*stop)(int id); | ||
82 | }; | ||
83 | |||
84 | #endif | ||
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig index 6bcb1164d599..56d8ff6a402d 100644 --- a/sound/soc/sh/Kconfig +++ b/sound/soc/sh/Kconfig | |||
@@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU | |||
34 | select SH_DMAE | 34 | select SH_DMAE |
35 | select FW_LOADER | 35 | select FW_LOADER |
36 | 36 | ||
37 | config SND_SOC_RCAR | ||
38 | tristate "R-Car series SRU/SCU/SSIU/SSI support" | ||
39 | select SND_SIMPLE_CARD | ||
40 | select RCAR_CLK_ADG | ||
41 | help | ||
42 | This option enables R-Car SUR/SCU/SSIU/SSI sound support | ||
43 | |||
37 | ## | 44 | ## |
38 | ## Boards | 45 | ## Boards |
39 | ## | 46 | ## |
diff --git a/sound/soc/sh/Makefile b/sound/soc/sh/Makefile index 849b387d17d9..aaf3dcd1ee2a 100644 --- a/sound/soc/sh/Makefile +++ b/sound/soc/sh/Makefile | |||
@@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o | |||
12 | obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o | 12 | obj-$(CONFIG_SND_SOC_SH4_FSI) += snd-soc-fsi.o |
13 | obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o | 13 | obj-$(CONFIG_SND_SOC_SH4_SIU) += snd-soc-siu.o |
14 | 14 | ||
15 | ## audio units for R-Car | ||
16 | obj-$(CONFIG_SND_SOC_RCAR) += rcar/ | ||
17 | |||
15 | ## boards | 18 | ## boards |
16 | snd-soc-sh7760-ac97-objs := sh7760-ac97.o | 19 | snd-soc-sh7760-ac97-objs := sh7760-ac97.o |
17 | snd-soc-migor-objs := migor.o | 20 | snd-soc-migor-objs := migor.o |
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile new file mode 100644 index 000000000000..0ff492df7929 --- /dev/null +++ b/sound/soc/sh/rcar/Makefile | |||
@@ -0,0 +1,2 @@ | |||
1 | snd-soc-rcar-objs := core.o gen.o scu.o adg.o ssi.o | ||
2 | obj-$(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 | |||
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 | } | ||
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c new file mode 100644 index 000000000000..a35706028514 --- /dev/null +++ b/sound/soc/sh/rcar/core.c | |||
@@ -0,0 +1,861 @@ | |||
1 | /* | ||
2 | * Renesas R-Car SRU/SCU/SSIU/SSI support | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * Based on fsi.c | ||
8 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | /* | ||
16 | * Renesas R-Car sound device structure | ||
17 | * | ||
18 | * Gen1 | ||
19 | * | ||
20 | * SRU : Sound Routing Unit | ||
21 | * - SRC : Sampling Rate Converter | ||
22 | * - CMD | ||
23 | * - CTU : Channel Count Conversion Unit | ||
24 | * - MIX : Mixer | ||
25 | * - DVC : Digital Volume and Mute Function | ||
26 | * - SSI : Serial Sound Interface | ||
27 | * | ||
28 | * Gen2 | ||
29 | * | ||
30 | * SCU : Sampling Rate Converter Unit | ||
31 | * - SRC : Sampling Rate Converter | ||
32 | * - CMD | ||
33 | * - CTU : Channel Count Conversion Unit | ||
34 | * - MIX : Mixer | ||
35 | * - DVC : Digital Volume and Mute Function | ||
36 | * SSIU : Serial Sound Interface Unit | ||
37 | * - SSI : Serial Sound Interface | ||
38 | */ | ||
39 | |||
40 | /* | ||
41 | * driver data Image | ||
42 | * | ||
43 | * rsnd_priv | ||
44 | * | | ||
45 | * | ** this depends on Gen1/Gen2 | ||
46 | * | | ||
47 | * +- gen | ||
48 | * | | ||
49 | * | ** these depend on data path | ||
50 | * | ** gen and platform data control it | ||
51 | * | | ||
52 | * +- rdai[0] | ||
53 | * | | sru ssiu ssi | ||
54 | * | +- playback -> [mod] -> [mod] -> [mod] -> ... | ||
55 | * | | | ||
56 | * | | sru ssiu ssi | ||
57 | * | +- capture -> [mod] -> [mod] -> [mod] -> ... | ||
58 | * | | ||
59 | * +- rdai[1] | ||
60 | * | | sru ssiu ssi | ||
61 | * | +- playback -> [mod] -> [mod] -> [mod] -> ... | ||
62 | * | | | ||
63 | * | | sru ssiu ssi | ||
64 | * | +- capture -> [mod] -> [mod] -> [mod] -> ... | ||
65 | * ... | ||
66 | * | | ||
67 | * | ** these control ssi | ||
68 | * | | ||
69 | * +- ssi | ||
70 | * | | | ||
71 | * | +- ssi[0] | ||
72 | * | +- ssi[1] | ||
73 | * | +- ssi[2] | ||
74 | * | ... | ||
75 | * | | ||
76 | * | ** these control scu | ||
77 | * | | ||
78 | * +- scu | ||
79 | * | | ||
80 | * +- scu[0] | ||
81 | * +- scu[1] | ||
82 | * +- scu[2] | ||
83 | * ... | ||
84 | * | ||
85 | * | ||
86 | * for_each_rsnd_dai(xx, priv, xx) | ||
87 | * rdai[0] => rdai[1] => rdai[2] => ... | ||
88 | * | ||
89 | * for_each_rsnd_mod(xx, rdai, xx) | ||
90 | * [mod] => [mod] => [mod] => ... | ||
91 | * | ||
92 | * rsnd_dai_call(xxx, fn ) | ||
93 | * [mod]->fn() -> [mod]->fn() -> [mod]->fn()... | ||
94 | * | ||
95 | */ | ||
96 | #include <linux/pm_runtime.h> | ||
97 | #include "rsnd.h" | ||
98 | |||
99 | #define RSND_RATES SNDRV_PCM_RATE_8000_96000 | ||
100 | #define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE) | ||
101 | |||
102 | /* | ||
103 | * rsnd_platform functions | ||
104 | */ | ||
105 | #define rsnd_platform_call(priv, dai, func, param...) \ | ||
106 | (!(priv->info->func) ? -ENODEV : \ | ||
107 | priv->info->func(param)) | ||
108 | |||
109 | |||
110 | /* | ||
111 | * basic function | ||
112 | */ | ||
113 | u32 rsnd_read(struct rsnd_priv *priv, | ||
114 | struct rsnd_mod *mod, enum rsnd_reg reg) | ||
115 | { | ||
116 | void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); | ||
117 | |||
118 | BUG_ON(!base); | ||
119 | |||
120 | return ioread32(base); | ||
121 | } | ||
122 | |||
123 | void rsnd_write(struct rsnd_priv *priv, | ||
124 | struct rsnd_mod *mod, | ||
125 | enum rsnd_reg reg, u32 data) | ||
126 | { | ||
127 | void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); | ||
128 | struct device *dev = rsnd_priv_to_dev(priv); | ||
129 | |||
130 | BUG_ON(!base); | ||
131 | |||
132 | dev_dbg(dev, "w %p : %08x\n", base, data); | ||
133 | |||
134 | iowrite32(data, base); | ||
135 | } | ||
136 | |||
137 | void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, | ||
138 | enum rsnd_reg reg, u32 mask, u32 data) | ||
139 | { | ||
140 | void __iomem *base = rsnd_gen_reg_get(priv, mod, reg); | ||
141 | struct device *dev = rsnd_priv_to_dev(priv); | ||
142 | u32 val; | ||
143 | |||
144 | BUG_ON(!base); | ||
145 | |||
146 | val = ioread32(base); | ||
147 | val &= ~mask; | ||
148 | val |= data & mask; | ||
149 | iowrite32(val, base); | ||
150 | |||
151 | dev_dbg(dev, "s %p : %08x\n", base, val); | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * rsnd_mod functions | ||
156 | */ | ||
157 | char *rsnd_mod_name(struct rsnd_mod *mod) | ||
158 | { | ||
159 | if (!mod || !mod->ops) | ||
160 | return "unknown"; | ||
161 | |||
162 | return mod->ops->name; | ||
163 | } | ||
164 | |||
165 | void rsnd_mod_init(struct rsnd_priv *priv, | ||
166 | struct rsnd_mod *mod, | ||
167 | struct rsnd_mod_ops *ops, | ||
168 | int id) | ||
169 | { | ||
170 | mod->priv = priv; | ||
171 | mod->id = id; | ||
172 | mod->ops = ops; | ||
173 | INIT_LIST_HEAD(&mod->list); | ||
174 | } | ||
175 | |||
176 | /* | ||
177 | * rsnd_dma functions | ||
178 | */ | ||
179 | static void rsnd_dma_continue(struct rsnd_dma *dma) | ||
180 | { | ||
181 | /* push next A or B plane */ | ||
182 | dma->submit_loop = 1; | ||
183 | schedule_work(&dma->work); | ||
184 | } | ||
185 | |||
186 | void rsnd_dma_start(struct rsnd_dma *dma) | ||
187 | { | ||
188 | /* push both A and B plane*/ | ||
189 | dma->submit_loop = 2; | ||
190 | schedule_work(&dma->work); | ||
191 | } | ||
192 | |||
193 | void rsnd_dma_stop(struct rsnd_dma *dma) | ||
194 | { | ||
195 | dma->submit_loop = 0; | ||
196 | cancel_work_sync(&dma->work); | ||
197 | dmaengine_terminate_all(dma->chan); | ||
198 | } | ||
199 | |||
200 | static void rsnd_dma_complete(void *data) | ||
201 | { | ||
202 | struct rsnd_dma *dma = (struct rsnd_dma *)data; | ||
203 | struct rsnd_priv *priv = dma->priv; | ||
204 | unsigned long flags; | ||
205 | |||
206 | rsnd_lock(priv, flags); | ||
207 | |||
208 | dma->complete(dma); | ||
209 | |||
210 | if (dma->submit_loop) | ||
211 | rsnd_dma_continue(dma); | ||
212 | |||
213 | rsnd_unlock(priv, flags); | ||
214 | } | ||
215 | |||
216 | static void rsnd_dma_do_work(struct work_struct *work) | ||
217 | { | ||
218 | struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work); | ||
219 | struct rsnd_priv *priv = dma->priv; | ||
220 | struct device *dev = rsnd_priv_to_dev(priv); | ||
221 | struct dma_async_tx_descriptor *desc; | ||
222 | dma_addr_t buf; | ||
223 | size_t len; | ||
224 | int i; | ||
225 | |||
226 | for (i = 0; i < dma->submit_loop; i++) { | ||
227 | |||
228 | if (dma->inquiry(dma, &buf, &len) < 0) | ||
229 | return; | ||
230 | |||
231 | desc = dmaengine_prep_slave_single( | ||
232 | dma->chan, buf, len, dma->dir, | ||
233 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
234 | if (!desc) { | ||
235 | dev_err(dev, "dmaengine_prep_slave_sg() fail\n"); | ||
236 | return; | ||
237 | } | ||
238 | |||
239 | desc->callback = rsnd_dma_complete; | ||
240 | desc->callback_param = dma; | ||
241 | |||
242 | if (dmaengine_submit(desc) < 0) { | ||
243 | dev_err(dev, "dmaengine_submit() fail\n"); | ||
244 | return; | ||
245 | } | ||
246 | |||
247 | } | ||
248 | |||
249 | dma_async_issue_pending(dma->chan); | ||
250 | } | ||
251 | |||
252 | int rsnd_dma_available(struct rsnd_dma *dma) | ||
253 | { | ||
254 | return !!dma->chan; | ||
255 | } | ||
256 | |||
257 | static bool rsnd_dma_filter(struct dma_chan *chan, void *param) | ||
258 | { | ||
259 | chan->private = param; | ||
260 | |||
261 | return true; | ||
262 | } | ||
263 | |||
264 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | ||
265 | int is_play, int id, | ||
266 | int (*inquiry)(struct rsnd_dma *dma, | ||
267 | dma_addr_t *buf, int *len), | ||
268 | int (*complete)(struct rsnd_dma *dma)) | ||
269 | { | ||
270 | struct device *dev = rsnd_priv_to_dev(priv); | ||
271 | dma_cap_mask_t mask; | ||
272 | |||
273 | if (dma->chan) { | ||
274 | dev_err(dev, "it already has dma channel\n"); | ||
275 | return -EIO; | ||
276 | } | ||
277 | |||
278 | dma_cap_zero(mask); | ||
279 | dma_cap_set(DMA_SLAVE, mask); | ||
280 | |||
281 | dma->slave.shdma_slave.slave_id = id; | ||
282 | |||
283 | dma->chan = dma_request_channel(mask, rsnd_dma_filter, | ||
284 | &dma->slave.shdma_slave); | ||
285 | if (!dma->chan) { | ||
286 | dev_err(dev, "can't get dma channel\n"); | ||
287 | return -EIO; | ||
288 | } | ||
289 | |||
290 | dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; | ||
291 | dma->priv = priv; | ||
292 | dma->inquiry = inquiry; | ||
293 | dma->complete = complete; | ||
294 | INIT_WORK(&dma->work, rsnd_dma_do_work); | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | void rsnd_dma_quit(struct rsnd_priv *priv, | ||
300 | struct rsnd_dma *dma) | ||
301 | { | ||
302 | if (dma->chan) | ||
303 | dma_release_channel(dma->chan); | ||
304 | |||
305 | dma->chan = NULL; | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * rsnd_dai functions | ||
310 | */ | ||
311 | #define rsnd_dai_call(rdai, io, fn) \ | ||
312 | ({ \ | ||
313 | struct rsnd_mod *mod, *n; \ | ||
314 | int ret = 0; \ | ||
315 | for_each_rsnd_mod(mod, n, io) { \ | ||
316 | ret = rsnd_mod_call(mod, fn, rdai, io); \ | ||
317 | if (ret < 0) \ | ||
318 | break; \ | ||
319 | } \ | ||
320 | ret; \ | ||
321 | }) | ||
322 | |||
323 | int rsnd_dai_connect(struct rsnd_dai *rdai, | ||
324 | struct rsnd_mod *mod, | ||
325 | struct rsnd_dai_stream *io) | ||
326 | { | ||
327 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
328 | struct device *dev = rsnd_priv_to_dev(priv); | ||
329 | |||
330 | if (!mod) { | ||
331 | dev_err(dev, "NULL mod\n"); | ||
332 | return -EIO; | ||
333 | } | ||
334 | |||
335 | if (!list_empty(&mod->list)) { | ||
336 | dev_err(dev, "%s%d is not empty\n", | ||
337 | rsnd_mod_name(mod), | ||
338 | rsnd_mod_id(mod)); | ||
339 | return -EIO; | ||
340 | } | ||
341 | |||
342 | list_add_tail(&mod->list, &io->head); | ||
343 | |||
344 | return 0; | ||
345 | } | ||
346 | |||
347 | int rsnd_dai_disconnect(struct rsnd_mod *mod) | ||
348 | { | ||
349 | list_del_init(&mod->list); | ||
350 | |||
351 | return 0; | ||
352 | } | ||
353 | |||
354 | int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai) | ||
355 | { | ||
356 | int id = rdai - priv->rdai; | ||
357 | |||
358 | if ((id < 0) || (id >= rsnd_dai_nr(priv))) | ||
359 | return -EINVAL; | ||
360 | |||
361 | return id; | ||
362 | } | ||
363 | |||
364 | struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id) | ||
365 | { | ||
366 | return priv->rdai + id; | ||
367 | } | ||
368 | |||
369 | static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai) | ||
370 | { | ||
371 | struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); | ||
372 | |||
373 | return rsnd_dai_get(priv, dai->id); | ||
374 | } | ||
375 | |||
376 | int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io) | ||
377 | { | ||
378 | return &rdai->playback == io; | ||
379 | } | ||
380 | |||
381 | /* | ||
382 | * rsnd_soc_dai functions | ||
383 | */ | ||
384 | int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional) | ||
385 | { | ||
386 | struct snd_pcm_substream *substream = io->substream; | ||
387 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
388 | int pos = io->byte_pos + additional; | ||
389 | |||
390 | pos %= (runtime->periods * io->byte_per_period); | ||
391 | |||
392 | return pos; | ||
393 | } | ||
394 | |||
395 | void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte) | ||
396 | { | ||
397 | io->byte_pos += byte; | ||
398 | |||
399 | if (io->byte_pos >= io->next_period_byte) { | ||
400 | struct snd_pcm_substream *substream = io->substream; | ||
401 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
402 | |||
403 | io->period_pos++; | ||
404 | io->next_period_byte += io->byte_per_period; | ||
405 | |||
406 | if (io->period_pos >= runtime->periods) { | ||
407 | io->byte_pos = 0; | ||
408 | io->period_pos = 0; | ||
409 | io->next_period_byte = io->byte_per_period; | ||
410 | } | ||
411 | |||
412 | snd_pcm_period_elapsed(substream); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | static int rsnd_dai_stream_init(struct rsnd_dai_stream *io, | ||
417 | struct snd_pcm_substream *substream) | ||
418 | { | ||
419 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
420 | |||
421 | if (!list_empty(&io->head)) | ||
422 | return -EIO; | ||
423 | |||
424 | INIT_LIST_HEAD(&io->head); | ||
425 | io->substream = substream; | ||
426 | io->byte_pos = 0; | ||
427 | io->period_pos = 0; | ||
428 | io->byte_per_period = runtime->period_size * | ||
429 | runtime->channels * | ||
430 | samples_to_bytes(runtime, 1); | ||
431 | io->next_period_byte = io->byte_per_period; | ||
432 | |||
433 | return 0; | ||
434 | } | ||
435 | |||
436 | static | ||
437 | struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream) | ||
438 | { | ||
439 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
440 | |||
441 | return rtd->cpu_dai; | ||
442 | } | ||
443 | |||
444 | static | ||
445 | struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai, | ||
446 | struct snd_pcm_substream *substream) | ||
447 | { | ||
448 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
449 | return &rdai->playback; | ||
450 | else | ||
451 | return &rdai->capture; | ||
452 | } | ||
453 | |||
454 | static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd, | ||
455 | struct snd_soc_dai *dai) | ||
456 | { | ||
457 | struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai); | ||
458 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | ||
459 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); | ||
460 | struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv, | ||
461 | rsnd_dai_id(priv, rdai), | ||
462 | rsnd_dai_is_play(rdai, io)); | ||
463 | int ssi_id = rsnd_mod_id(mod); | ||
464 | int ret; | ||
465 | unsigned long flags; | ||
466 | |||
467 | rsnd_lock(priv, flags); | ||
468 | |||
469 | switch (cmd) { | ||
470 | case SNDRV_PCM_TRIGGER_START: | ||
471 | ret = rsnd_dai_stream_init(io, substream); | ||
472 | if (ret < 0) | ||
473 | goto dai_trigger_end; | ||
474 | |||
475 | ret = rsnd_platform_call(priv, dai, start, ssi_id); | ||
476 | if (ret < 0) | ||
477 | goto dai_trigger_end; | ||
478 | |||
479 | ret = rsnd_gen_path_init(priv, rdai, io); | ||
480 | if (ret < 0) | ||
481 | goto dai_trigger_end; | ||
482 | |||
483 | ret = rsnd_dai_call(rdai, io, init); | ||
484 | if (ret < 0) | ||
485 | goto dai_trigger_end; | ||
486 | |||
487 | ret = rsnd_dai_call(rdai, io, start); | ||
488 | if (ret < 0) | ||
489 | goto dai_trigger_end; | ||
490 | break; | ||
491 | case SNDRV_PCM_TRIGGER_STOP: | ||
492 | ret = rsnd_dai_call(rdai, io, stop); | ||
493 | if (ret < 0) | ||
494 | goto dai_trigger_end; | ||
495 | |||
496 | ret = rsnd_dai_call(rdai, io, quit); | ||
497 | if (ret < 0) | ||
498 | goto dai_trigger_end; | ||
499 | |||
500 | ret = rsnd_gen_path_exit(priv, rdai, io); | ||
501 | if (ret < 0) | ||
502 | goto dai_trigger_end; | ||
503 | |||
504 | ret = rsnd_platform_call(priv, dai, stop, ssi_id); | ||
505 | if (ret < 0) | ||
506 | goto dai_trigger_end; | ||
507 | break; | ||
508 | default: | ||
509 | ret = -EINVAL; | ||
510 | } | ||
511 | |||
512 | dai_trigger_end: | ||
513 | rsnd_unlock(priv, flags); | ||
514 | |||
515 | return ret; | ||
516 | } | ||
517 | |||
518 | static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) | ||
519 | { | ||
520 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | ||
521 | |||
522 | /* set master/slave audio interface */ | ||
523 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
524 | case SND_SOC_DAIFMT_CBM_CFM: | ||
525 | rdai->clk_master = 1; | ||
526 | break; | ||
527 | case SND_SOC_DAIFMT_CBS_CFS: | ||
528 | rdai->clk_master = 0; | ||
529 | break; | ||
530 | default: | ||
531 | return -EINVAL; | ||
532 | } | ||
533 | |||
534 | /* set clock inversion */ | ||
535 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { | ||
536 | case SND_SOC_DAIFMT_NB_IF: | ||
537 | rdai->bit_clk_inv = 0; | ||
538 | rdai->frm_clk_inv = 1; | ||
539 | break; | ||
540 | case SND_SOC_DAIFMT_IB_NF: | ||
541 | rdai->bit_clk_inv = 1; | ||
542 | rdai->frm_clk_inv = 0; | ||
543 | break; | ||
544 | case SND_SOC_DAIFMT_IB_IF: | ||
545 | rdai->bit_clk_inv = 1; | ||
546 | rdai->frm_clk_inv = 1; | ||
547 | break; | ||
548 | case SND_SOC_DAIFMT_NB_NF: | ||
549 | default: | ||
550 | rdai->bit_clk_inv = 0; | ||
551 | rdai->frm_clk_inv = 0; | ||
552 | break; | ||
553 | } | ||
554 | |||
555 | /* set format */ | ||
556 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
557 | case SND_SOC_DAIFMT_I2S: | ||
558 | rdai->sys_delay = 0; | ||
559 | rdai->data_alignment = 0; | ||
560 | break; | ||
561 | case SND_SOC_DAIFMT_LEFT_J: | ||
562 | rdai->sys_delay = 1; | ||
563 | rdai->data_alignment = 0; | ||
564 | break; | ||
565 | case SND_SOC_DAIFMT_RIGHT_J: | ||
566 | rdai->sys_delay = 1; | ||
567 | rdai->data_alignment = 1; | ||
568 | break; | ||
569 | } | ||
570 | |||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | static const struct snd_soc_dai_ops rsnd_soc_dai_ops = { | ||
575 | .trigger = rsnd_soc_dai_trigger, | ||
576 | .set_fmt = rsnd_soc_dai_set_fmt, | ||
577 | }; | ||
578 | |||
579 | static int rsnd_dai_probe(struct platform_device *pdev, | ||
580 | struct rcar_snd_info *info, | ||
581 | struct rsnd_priv *priv) | ||
582 | { | ||
583 | struct snd_soc_dai_driver *drv; | ||
584 | struct rsnd_dai *rdai; | ||
585 | struct rsnd_mod *pmod, *cmod; | ||
586 | struct device *dev = rsnd_priv_to_dev(priv); | ||
587 | int dai_nr; | ||
588 | int i; | ||
589 | |||
590 | /* get max dai nr */ | ||
591 | for (dai_nr = 0; dai_nr < 32; dai_nr++) { | ||
592 | pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1); | ||
593 | cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0); | ||
594 | |||
595 | if (!pmod && !cmod) | ||
596 | break; | ||
597 | } | ||
598 | |||
599 | if (!dai_nr) { | ||
600 | dev_err(dev, "no dai\n"); | ||
601 | return -EIO; | ||
602 | } | ||
603 | |||
604 | drv = devm_kzalloc(dev, sizeof(*drv) * dai_nr, GFP_KERNEL); | ||
605 | rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL); | ||
606 | if (!drv || !rdai) { | ||
607 | dev_err(dev, "dai allocate failed\n"); | ||
608 | return -ENOMEM; | ||
609 | } | ||
610 | |||
611 | for (i = 0; i < dai_nr; i++) { | ||
612 | |||
613 | pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1); | ||
614 | cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0); | ||
615 | |||
616 | /* | ||
617 | * init rsnd_dai | ||
618 | */ | ||
619 | INIT_LIST_HEAD(&rdai[i].playback.head); | ||
620 | INIT_LIST_HEAD(&rdai[i].capture.head); | ||
621 | |||
622 | snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i); | ||
623 | |||
624 | /* | ||
625 | * init snd_soc_dai_driver | ||
626 | */ | ||
627 | drv[i].name = rdai[i].name; | ||
628 | drv[i].ops = &rsnd_soc_dai_ops; | ||
629 | if (pmod) { | ||
630 | drv[i].playback.rates = RSND_RATES; | ||
631 | drv[i].playback.formats = RSND_FMTS; | ||
632 | drv[i].playback.channels_min = 2; | ||
633 | drv[i].playback.channels_max = 2; | ||
634 | } | ||
635 | if (cmod) { | ||
636 | drv[i].capture.rates = RSND_RATES; | ||
637 | drv[i].capture.formats = RSND_FMTS; | ||
638 | drv[i].capture.channels_min = 2; | ||
639 | drv[i].capture.channels_max = 2; | ||
640 | } | ||
641 | |||
642 | dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name, | ||
643 | pmod ? "play" : " -- ", | ||
644 | cmod ? "capture" : " -- "); | ||
645 | } | ||
646 | |||
647 | priv->dai_nr = dai_nr; | ||
648 | priv->daidrv = drv; | ||
649 | priv->rdai = rdai; | ||
650 | |||
651 | return 0; | ||
652 | } | ||
653 | |||
654 | static void rsnd_dai_remove(struct platform_device *pdev, | ||
655 | struct rsnd_priv *priv) | ||
656 | { | ||
657 | } | ||
658 | |||
659 | /* | ||
660 | * pcm ops | ||
661 | */ | ||
662 | static struct snd_pcm_hardware rsnd_pcm_hardware = { | ||
663 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
664 | SNDRV_PCM_INFO_MMAP | | ||
665 | SNDRV_PCM_INFO_MMAP_VALID | | ||
666 | SNDRV_PCM_INFO_PAUSE, | ||
667 | .formats = RSND_FMTS, | ||
668 | .rates = RSND_RATES, | ||
669 | .rate_min = 8000, | ||
670 | .rate_max = 192000, | ||
671 | .channels_min = 2, | ||
672 | .channels_max = 2, | ||
673 | .buffer_bytes_max = 64 * 1024, | ||
674 | .period_bytes_min = 32, | ||
675 | .period_bytes_max = 8192, | ||
676 | .periods_min = 1, | ||
677 | .periods_max = 32, | ||
678 | .fifo_size = 256, | ||
679 | }; | ||
680 | |||
681 | static int rsnd_pcm_open(struct snd_pcm_substream *substream) | ||
682 | { | ||
683 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
684 | int ret = 0; | ||
685 | |||
686 | snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware); | ||
687 | |||
688 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
689 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
690 | |||
691 | return ret; | ||
692 | } | ||
693 | |||
694 | static int rsnd_hw_params(struct snd_pcm_substream *substream, | ||
695 | struct snd_pcm_hw_params *hw_params) | ||
696 | { | ||
697 | return snd_pcm_lib_malloc_pages(substream, | ||
698 | params_buffer_bytes(hw_params)); | ||
699 | } | ||
700 | |||
701 | static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream) | ||
702 | { | ||
703 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
704 | struct snd_soc_dai *dai = rsnd_substream_to_dai(substream); | ||
705 | struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai); | ||
706 | struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream); | ||
707 | |||
708 | return bytes_to_frames(runtime, io->byte_pos); | ||
709 | } | ||
710 | |||
711 | static struct snd_pcm_ops rsnd_pcm_ops = { | ||
712 | .open = rsnd_pcm_open, | ||
713 | .ioctl = snd_pcm_lib_ioctl, | ||
714 | .hw_params = rsnd_hw_params, | ||
715 | .hw_free = snd_pcm_lib_free_pages, | ||
716 | .pointer = rsnd_pointer, | ||
717 | }; | ||
718 | |||
719 | /* | ||
720 | * snd_soc_platform | ||
721 | */ | ||
722 | |||
723 | #define PREALLOC_BUFFER (32 * 1024) | ||
724 | #define PREALLOC_BUFFER_MAX (32 * 1024) | ||
725 | |||
726 | static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd) | ||
727 | { | ||
728 | return snd_pcm_lib_preallocate_pages_for_all( | ||
729 | rtd->pcm, | ||
730 | SNDRV_DMA_TYPE_DEV, | ||
731 | rtd->card->snd_card->dev, | ||
732 | PREALLOC_BUFFER, PREALLOC_BUFFER_MAX); | ||
733 | } | ||
734 | |||
735 | static void rsnd_pcm_free(struct snd_pcm *pcm) | ||
736 | { | ||
737 | snd_pcm_lib_preallocate_free_for_all(pcm); | ||
738 | } | ||
739 | |||
740 | static struct snd_soc_platform_driver rsnd_soc_platform = { | ||
741 | .ops = &rsnd_pcm_ops, | ||
742 | .pcm_new = rsnd_pcm_new, | ||
743 | .pcm_free = rsnd_pcm_free, | ||
744 | }; | ||
745 | |||
746 | static const struct snd_soc_component_driver rsnd_soc_component = { | ||
747 | .name = "rsnd", | ||
748 | }; | ||
749 | |||
750 | /* | ||
751 | * rsnd probe | ||
752 | */ | ||
753 | static int rsnd_probe(struct platform_device *pdev) | ||
754 | { | ||
755 | struct rcar_snd_info *info; | ||
756 | struct rsnd_priv *priv; | ||
757 | struct device *dev = &pdev->dev; | ||
758 | int ret; | ||
759 | |||
760 | info = pdev->dev.platform_data; | ||
761 | if (!info) { | ||
762 | dev_err(dev, "driver needs R-Car sound information\n"); | ||
763 | return -ENODEV; | ||
764 | } | ||
765 | |||
766 | /* | ||
767 | * init priv data | ||
768 | */ | ||
769 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); | ||
770 | if (!priv) { | ||
771 | dev_err(dev, "priv allocate failed\n"); | ||
772 | return -ENODEV; | ||
773 | } | ||
774 | |||
775 | priv->dev = dev; | ||
776 | priv->info = info; | ||
777 | spin_lock_init(&priv->lock); | ||
778 | |||
779 | /* | ||
780 | * init each module | ||
781 | */ | ||
782 | ret = rsnd_gen_probe(pdev, info, priv); | ||
783 | if (ret < 0) | ||
784 | return ret; | ||
785 | |||
786 | ret = rsnd_scu_probe(pdev, info, priv); | ||
787 | if (ret < 0) | ||
788 | return ret; | ||
789 | |||
790 | ret = rsnd_adg_probe(pdev, info, priv); | ||
791 | if (ret < 0) | ||
792 | return ret; | ||
793 | |||
794 | ret = rsnd_ssi_probe(pdev, info, priv); | ||
795 | if (ret < 0) | ||
796 | return ret; | ||
797 | |||
798 | ret = rsnd_dai_probe(pdev, info, priv); | ||
799 | if (ret < 0) | ||
800 | return ret; | ||
801 | |||
802 | /* | ||
803 | * asoc register | ||
804 | */ | ||
805 | ret = snd_soc_register_platform(dev, &rsnd_soc_platform); | ||
806 | if (ret < 0) { | ||
807 | dev_err(dev, "cannot snd soc register\n"); | ||
808 | return ret; | ||
809 | } | ||
810 | |||
811 | ret = snd_soc_register_component(dev, &rsnd_soc_component, | ||
812 | priv->daidrv, rsnd_dai_nr(priv)); | ||
813 | if (ret < 0) { | ||
814 | dev_err(dev, "cannot snd dai register\n"); | ||
815 | goto exit_snd_soc; | ||
816 | } | ||
817 | |||
818 | dev_set_drvdata(dev, priv); | ||
819 | |||
820 | pm_runtime_enable(dev); | ||
821 | |||
822 | dev_info(dev, "probed\n"); | ||
823 | return ret; | ||
824 | |||
825 | exit_snd_soc: | ||
826 | snd_soc_unregister_platform(dev); | ||
827 | |||
828 | return ret; | ||
829 | } | ||
830 | |||
831 | static int rsnd_remove(struct platform_device *pdev) | ||
832 | { | ||
833 | struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev); | ||
834 | |||
835 | pm_runtime_disable(&pdev->dev); | ||
836 | |||
837 | /* | ||
838 | * remove each module | ||
839 | */ | ||
840 | rsnd_ssi_remove(pdev, priv); | ||
841 | rsnd_adg_remove(pdev, priv); | ||
842 | rsnd_scu_remove(pdev, priv); | ||
843 | rsnd_dai_remove(pdev, priv); | ||
844 | rsnd_gen_remove(pdev, priv); | ||
845 | |||
846 | return 0; | ||
847 | } | ||
848 | |||
849 | static struct platform_driver rsnd_driver = { | ||
850 | .driver = { | ||
851 | .name = "rcar_sound", | ||
852 | }, | ||
853 | .probe = rsnd_probe, | ||
854 | .remove = rsnd_remove, | ||
855 | }; | ||
856 | module_platform_driver(rsnd_driver); | ||
857 | |||
858 | MODULE_LICENSE("GPL"); | ||
859 | MODULE_DESCRIPTION("Renesas R-Car audio driver"); | ||
860 | MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); | ||
861 | MODULE_ALIAS("platform:rcar-pcm-audio"); | ||
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c new file mode 100644 index 000000000000..babb203b43b7 --- /dev/null +++ b/sound/soc/sh/rcar/gen.c | |||
@@ -0,0 +1,280 @@ | |||
1 | /* | ||
2 | * Renesas R-Car Gen1 SRU/SSI support | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include "rsnd.h" | ||
12 | |||
13 | struct rsnd_gen_ops { | ||
14 | int (*path_init)(struct rsnd_priv *priv, | ||
15 | struct rsnd_dai *rdai, | ||
16 | struct rsnd_dai_stream *io); | ||
17 | int (*path_exit)(struct rsnd_priv *priv, | ||
18 | struct rsnd_dai *rdai, | ||
19 | struct rsnd_dai_stream *io); | ||
20 | }; | ||
21 | |||
22 | struct rsnd_gen_reg_map { | ||
23 | int index; /* -1 : not supported */ | ||
24 | u32 offset_id; /* offset of ssi0, ssi1, ssi2... */ | ||
25 | u32 offset_adr; /* offset of SSICR, SSISR, ... */ | ||
26 | }; | ||
27 | |||
28 | struct rsnd_gen { | ||
29 | void __iomem *base[RSND_BASE_MAX]; | ||
30 | |||
31 | struct rsnd_gen_reg_map reg_map[RSND_REG_MAX]; | ||
32 | struct rsnd_gen_ops *ops; | ||
33 | }; | ||
34 | |||
35 | #define rsnd_priv_to_gen(p) ((struct rsnd_gen *)(p)->gen) | ||
36 | |||
37 | /* | ||
38 | * Gen2 | ||
39 | * will be filled in the future | ||
40 | */ | ||
41 | |||
42 | /* | ||
43 | * Gen1 | ||
44 | */ | ||
45 | static int rsnd_gen1_path_init(struct rsnd_priv *priv, | ||
46 | struct rsnd_dai *rdai, | ||
47 | struct rsnd_dai_stream *io) | ||
48 | { | ||
49 | struct rsnd_mod *mod; | ||
50 | int ret; | ||
51 | int id; | ||
52 | |||
53 | /* | ||
54 | * Gen1 is created by SRU/SSI, and this SRU is base module of | ||
55 | * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU) | ||
56 | * | ||
57 | * Easy image is.. | ||
58 | * Gen1 SRU = Gen2 SCU + SSIU + etc | ||
59 | * | ||
60 | * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is | ||
61 | * using fixed path. | ||
62 | * | ||
63 | * Then, SSI id = SCU id here | ||
64 | */ | ||
65 | |||
66 | /* get SSI's ID */ | ||
67 | mod = rsnd_ssi_mod_get_frm_dai(priv, | ||
68 | rsnd_dai_id(priv, rdai), | ||
69 | rsnd_dai_is_play(rdai, io)); | ||
70 | id = rsnd_mod_id(mod); | ||
71 | |||
72 | /* SSI */ | ||
73 | mod = rsnd_ssi_mod_get(priv, id); | ||
74 | ret = rsnd_dai_connect(rdai, mod, io); | ||
75 | if (ret < 0) | ||
76 | return ret; | ||
77 | |||
78 | /* SCU */ | ||
79 | mod = rsnd_scu_mod_get(priv, id); | ||
80 | ret = rsnd_dai_connect(rdai, mod, io); | ||
81 | |||
82 | return ret; | ||
83 | } | ||
84 | |||
85 | static int rsnd_gen1_path_exit(struct rsnd_priv *priv, | ||
86 | struct rsnd_dai *rdai, | ||
87 | struct rsnd_dai_stream *io) | ||
88 | { | ||
89 | struct rsnd_mod *mod, *n; | ||
90 | int ret = 0; | ||
91 | |||
92 | /* | ||
93 | * remove all mod from rdai | ||
94 | */ | ||
95 | for_each_rsnd_mod(mod, n, io) | ||
96 | ret |= rsnd_dai_disconnect(mod); | ||
97 | |||
98 | return ret; | ||
99 | } | ||
100 | |||
101 | static struct rsnd_gen_ops rsnd_gen1_ops = { | ||
102 | .path_init = rsnd_gen1_path_init, | ||
103 | .path_exit = rsnd_gen1_path_exit, | ||
104 | }; | ||
105 | |||
106 | #define RSND_GEN1_REG_MAP(g, s, i, oi, oa) \ | ||
107 | do { \ | ||
108 | (g)->reg_map[RSND_REG_##i].index = RSND_GEN1_##s; \ | ||
109 | (g)->reg_map[RSND_REG_##i].offset_id = oi; \ | ||
110 | (g)->reg_map[RSND_REG_##i].offset_adr = oa; \ | ||
111 | } while (0) | ||
112 | |||
113 | static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen) | ||
114 | { | ||
115 | RSND_GEN1_REG_MAP(gen, SRU, SRC_ROUTE_SEL, 0x0, 0x00); | ||
116 | RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL0, 0x0, 0x08); | ||
117 | RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL1, 0x0, 0x0c); | ||
118 | RSND_GEN1_REG_MAP(gen, SRU, SRC_TMG_SEL2, 0x0, 0x10); | ||
119 | RSND_GEN1_REG_MAP(gen, SRU, SRC_CTRL, 0x0, 0xc0); | ||
120 | RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE0, 0x0, 0xD0); | ||
121 | RSND_GEN1_REG_MAP(gen, SRU, SSI_MODE1, 0x0, 0xD4); | ||
122 | RSND_GEN1_REG_MAP(gen, SRU, BUSIF_MODE, 0x4, 0x20); | ||
123 | RSND_GEN1_REG_MAP(gen, SRU, BUSIF_ADINR, 0x40, 0x214); | ||
124 | |||
125 | RSND_GEN1_REG_MAP(gen, ADG, BRRA, 0x0, 0x00); | ||
126 | RSND_GEN1_REG_MAP(gen, ADG, BRRB, 0x0, 0x04); | ||
127 | RSND_GEN1_REG_MAP(gen, ADG, SSICKR, 0x0, 0x08); | ||
128 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL0, 0x0, 0x0c); | ||
129 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL1, 0x0, 0x10); | ||
130 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL3, 0x0, 0x18); | ||
131 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL4, 0x0, 0x1c); | ||
132 | RSND_GEN1_REG_MAP(gen, ADG, AUDIO_CLK_SEL5, 0x0, 0x20); | ||
133 | |||
134 | RSND_GEN1_REG_MAP(gen, SSI, SSICR, 0x40, 0x00); | ||
135 | RSND_GEN1_REG_MAP(gen, SSI, SSISR, 0x40, 0x04); | ||
136 | RSND_GEN1_REG_MAP(gen, SSI, SSITDR, 0x40, 0x08); | ||
137 | RSND_GEN1_REG_MAP(gen, SSI, SSIRDR, 0x40, 0x0c); | ||
138 | RSND_GEN1_REG_MAP(gen, SSI, SSIWSR, 0x40, 0x20); | ||
139 | } | ||
140 | |||
141 | static int rsnd_gen1_probe(struct platform_device *pdev, | ||
142 | struct rcar_snd_info *info, | ||
143 | struct rsnd_priv *priv) | ||
144 | { | ||
145 | struct device *dev = rsnd_priv_to_dev(priv); | ||
146 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
147 | struct resource *sru_res; | ||
148 | struct resource *adg_res; | ||
149 | struct resource *ssi_res; | ||
150 | |||
151 | /* | ||
152 | * map address | ||
153 | */ | ||
154 | sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU); | ||
155 | adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG); | ||
156 | ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI); | ||
157 | |||
158 | gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res); | ||
159 | gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res); | ||
160 | gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res); | ||
161 | if (IS_ERR(gen->base[RSND_GEN1_SRU]) || | ||
162 | IS_ERR(gen->base[RSND_GEN1_ADG]) || | ||
163 | IS_ERR(gen->base[RSND_GEN1_SSI])) | ||
164 | return -ENODEV; | ||
165 | |||
166 | gen->ops = &rsnd_gen1_ops; | ||
167 | rsnd_gen1_reg_map_init(gen); | ||
168 | |||
169 | dev_dbg(dev, "Gen1 device probed\n"); | ||
170 | dev_dbg(dev, "SRU : %08x => %p\n", sru_res->start, | ||
171 | gen->base[RSND_GEN1_SRU]); | ||
172 | dev_dbg(dev, "ADG : %08x => %p\n", adg_res->start, | ||
173 | gen->base[RSND_GEN1_ADG]); | ||
174 | dev_dbg(dev, "SSI : %08x => %p\n", ssi_res->start, | ||
175 | gen->base[RSND_GEN1_SSI]); | ||
176 | |||
177 | return 0; | ||
178 | |||
179 | } | ||
180 | |||
181 | static void rsnd_gen1_remove(struct platform_device *pdev, | ||
182 | struct rsnd_priv *priv) | ||
183 | { | ||
184 | } | ||
185 | |||
186 | /* | ||
187 | * Gen | ||
188 | */ | ||
189 | int rsnd_gen_path_init(struct rsnd_priv *priv, | ||
190 | struct rsnd_dai *rdai, | ||
191 | struct rsnd_dai_stream *io) | ||
192 | { | ||
193 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
194 | |||
195 | return gen->ops->path_init(priv, rdai, io); | ||
196 | } | ||
197 | |||
198 | int rsnd_gen_path_exit(struct rsnd_priv *priv, | ||
199 | struct rsnd_dai *rdai, | ||
200 | struct rsnd_dai_stream *io) | ||
201 | { | ||
202 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
203 | |||
204 | return gen->ops->path_exit(priv, rdai, io); | ||
205 | } | ||
206 | |||
207 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | ||
208 | struct rsnd_mod *mod, | ||
209 | enum rsnd_reg reg) | ||
210 | { | ||
211 | struct rsnd_gen *gen = rsnd_priv_to_gen(priv); | ||
212 | struct device *dev = rsnd_priv_to_dev(priv); | ||
213 | int index; | ||
214 | u32 offset_id, offset_adr; | ||
215 | |||
216 | if (reg >= RSND_REG_MAX) { | ||
217 | dev_err(dev, "rsnd_reg reg error\n"); | ||
218 | return NULL; | ||
219 | } | ||
220 | |||
221 | index = gen->reg_map[reg].index; | ||
222 | offset_id = gen->reg_map[reg].offset_id; | ||
223 | offset_adr = gen->reg_map[reg].offset_adr; | ||
224 | |||
225 | if (index < 0) { | ||
226 | dev_err(dev, "unsupported reg access %d\n", reg); | ||
227 | return NULL; | ||
228 | } | ||
229 | |||
230 | if (offset_id && mod) | ||
231 | offset_id *= rsnd_mod_id(mod); | ||
232 | |||
233 | /* | ||
234 | * index/offset were set on gen1/gen2 | ||
235 | */ | ||
236 | |||
237 | return gen->base[index] + offset_id + offset_adr; | ||
238 | } | ||
239 | |||
240 | int rsnd_gen_probe(struct platform_device *pdev, | ||
241 | struct rcar_snd_info *info, | ||
242 | struct rsnd_priv *priv) | ||
243 | { | ||
244 | struct device *dev = rsnd_priv_to_dev(priv); | ||
245 | struct rsnd_gen *gen; | ||
246 | int i; | ||
247 | |||
248 | gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL); | ||
249 | if (!gen) { | ||
250 | dev_err(dev, "GEN allocate failed\n"); | ||
251 | return -ENOMEM; | ||
252 | } | ||
253 | |||
254 | priv->gen = gen; | ||
255 | |||
256 | /* | ||
257 | * see | ||
258 | * rsnd_reg_get() | ||
259 | * rsnd_gen_probe() | ||
260 | */ | ||
261 | for (i = 0; i < RSND_REG_MAX; i++) | ||
262 | gen->reg_map[i].index = -1; | ||
263 | |||
264 | /* | ||
265 | * init each module | ||
266 | */ | ||
267 | if (rsnd_is_gen1(priv)) | ||
268 | return rsnd_gen1_probe(pdev, info, priv); | ||
269 | |||
270 | dev_err(dev, "unknown generation R-Car sound device\n"); | ||
271 | |||
272 | return -ENODEV; | ||
273 | } | ||
274 | |||
275 | void rsnd_gen_remove(struct platform_device *pdev, | ||
276 | struct rsnd_priv *priv) | ||
277 | { | ||
278 | if (rsnd_is_gen1(priv)) | ||
279 | rsnd_gen1_remove(pdev, priv); | ||
280 | } | ||
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h new file mode 100644 index 000000000000..9cc6986a8cfb --- /dev/null +++ b/sound/soc/sh/rcar/rsnd.h | |||
@@ -0,0 +1,302 @@ | |||
1 | /* | ||
2 | * Renesas R-Car | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #ifndef RSND_H | ||
12 | #define RSND_H | ||
13 | |||
14 | #include <linux/clk.h> | ||
15 | #include <linux/device.h> | ||
16 | #include <linux/dma-mapping.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/list.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/sh_dma.h> | ||
21 | #include <linux/workqueue.h> | ||
22 | #include <sound/rcar_snd.h> | ||
23 | #include <sound/soc.h> | ||
24 | #include <sound/pcm_params.h> | ||
25 | |||
26 | /* | ||
27 | * pseudo register | ||
28 | * | ||
29 | * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different. | ||
30 | * This driver uses pseudo register in order to hide it. | ||
31 | * see gen1/gen2 for detail | ||
32 | */ | ||
33 | enum rsnd_reg { | ||
34 | /* SRU/SCU */ | ||
35 | RSND_REG_SRC_ROUTE_SEL, | ||
36 | RSND_REG_SRC_TMG_SEL0, | ||
37 | RSND_REG_SRC_TMG_SEL1, | ||
38 | RSND_REG_SRC_TMG_SEL2, | ||
39 | RSND_REG_SRC_CTRL, | ||
40 | RSND_REG_SSI_MODE0, | ||
41 | RSND_REG_SSI_MODE1, | ||
42 | RSND_REG_BUSIF_MODE, | ||
43 | RSND_REG_BUSIF_ADINR, | ||
44 | |||
45 | /* ADG */ | ||
46 | RSND_REG_BRRA, | ||
47 | RSND_REG_BRRB, | ||
48 | RSND_REG_SSICKR, | ||
49 | RSND_REG_AUDIO_CLK_SEL0, | ||
50 | RSND_REG_AUDIO_CLK_SEL1, | ||
51 | RSND_REG_AUDIO_CLK_SEL2, | ||
52 | RSND_REG_AUDIO_CLK_SEL3, | ||
53 | RSND_REG_AUDIO_CLK_SEL4, | ||
54 | RSND_REG_AUDIO_CLK_SEL5, | ||
55 | |||
56 | /* SSI */ | ||
57 | RSND_REG_SSICR, | ||
58 | RSND_REG_SSISR, | ||
59 | RSND_REG_SSITDR, | ||
60 | RSND_REG_SSIRDR, | ||
61 | RSND_REG_SSIWSR, | ||
62 | |||
63 | RSND_REG_MAX, | ||
64 | }; | ||
65 | |||
66 | struct rsnd_priv; | ||
67 | struct rsnd_mod; | ||
68 | struct rsnd_dai; | ||
69 | struct rsnd_dai_stream; | ||
70 | |||
71 | /* | ||
72 | * R-Car basic functions | ||
73 | */ | ||
74 | #define rsnd_mod_read(m, r) \ | ||
75 | rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r) | ||
76 | #define rsnd_mod_write(m, r, d) \ | ||
77 | rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d) | ||
78 | #define rsnd_mod_bset(m, r, s, d) \ | ||
79 | rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d) | ||
80 | |||
81 | #define rsnd_priv_read(p, r) rsnd_read(p, NULL, RSND_REG_##r) | ||
82 | #define rsnd_priv_write(p, r, d) rsnd_write(p, NULL, RSND_REG_##r, d) | ||
83 | #define rsnd_priv_bset(p, r, s, d) rsnd_bset(p, NULL, RSND_REG_##r, s, d) | ||
84 | |||
85 | u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg); | ||
86 | void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod, | ||
87 | enum rsnd_reg reg, u32 data); | ||
88 | void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg, | ||
89 | u32 mask, u32 data); | ||
90 | |||
91 | /* | ||
92 | * R-Car DMA | ||
93 | */ | ||
94 | struct rsnd_dma { | ||
95 | struct rsnd_priv *priv; | ||
96 | struct sh_dmae_slave slave; | ||
97 | struct work_struct work; | ||
98 | struct dma_chan *chan; | ||
99 | enum dma_data_direction dir; | ||
100 | int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len); | ||
101 | int (*complete)(struct rsnd_dma *dma); | ||
102 | |||
103 | int submit_loop; | ||
104 | }; | ||
105 | |||
106 | void rsnd_dma_start(struct rsnd_dma *dma); | ||
107 | void rsnd_dma_stop(struct rsnd_dma *dma); | ||
108 | int rsnd_dma_available(struct rsnd_dma *dma); | ||
109 | int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma, | ||
110 | int is_play, int id, | ||
111 | int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len), | ||
112 | int (*complete)(struct rsnd_dma *dma)); | ||
113 | void rsnd_dma_quit(struct rsnd_priv *priv, | ||
114 | struct rsnd_dma *dma); | ||
115 | |||
116 | |||
117 | /* | ||
118 | * R-Car sound mod | ||
119 | */ | ||
120 | |||
121 | struct rsnd_mod_ops { | ||
122 | char *name; | ||
123 | int (*init)(struct rsnd_mod *mod, | ||
124 | struct rsnd_dai *rdai, | ||
125 | struct rsnd_dai_stream *io); | ||
126 | int (*quit)(struct rsnd_mod *mod, | ||
127 | struct rsnd_dai *rdai, | ||
128 | struct rsnd_dai_stream *io); | ||
129 | int (*start)(struct rsnd_mod *mod, | ||
130 | struct rsnd_dai *rdai, | ||
131 | struct rsnd_dai_stream *io); | ||
132 | int (*stop)(struct rsnd_mod *mod, | ||
133 | struct rsnd_dai *rdai, | ||
134 | struct rsnd_dai_stream *io); | ||
135 | }; | ||
136 | |||
137 | struct rsnd_mod { | ||
138 | int id; | ||
139 | struct rsnd_priv *priv; | ||
140 | struct rsnd_mod_ops *ops; | ||
141 | struct list_head list; /* connect to rsnd_dai playback/capture */ | ||
142 | struct rsnd_dma dma; | ||
143 | }; | ||
144 | |||
145 | #define rsnd_mod_to_priv(mod) ((mod)->priv) | ||
146 | #define rsnd_mod_to_dma(mod) (&(mod)->dma) | ||
147 | #define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma) | ||
148 | #define rsnd_mod_id(mod) ((mod)->id) | ||
149 | #define for_each_rsnd_mod(pos, n, io) \ | ||
150 | list_for_each_entry_safe(pos, n, &(io)->head, list) | ||
151 | #define rsnd_mod_call(mod, func, rdai, io) \ | ||
152 | (!(mod) ? -ENODEV : \ | ||
153 | !((mod)->ops->func) ? 0 : \ | ||
154 | (mod)->ops->func(mod, rdai, io)) | ||
155 | |||
156 | void rsnd_mod_init(struct rsnd_priv *priv, | ||
157 | struct rsnd_mod *mod, | ||
158 | struct rsnd_mod_ops *ops, | ||
159 | int id); | ||
160 | char *rsnd_mod_name(struct rsnd_mod *mod); | ||
161 | |||
162 | /* | ||
163 | * R-Car sound DAI | ||
164 | */ | ||
165 | #define RSND_DAI_NAME_SIZE 16 | ||
166 | struct rsnd_dai_stream { | ||
167 | struct list_head head; /* head of rsnd_mod list */ | ||
168 | struct snd_pcm_substream *substream; | ||
169 | int byte_pos; | ||
170 | int period_pos; | ||
171 | int byte_per_period; | ||
172 | int next_period_byte; | ||
173 | }; | ||
174 | |||
175 | struct rsnd_dai { | ||
176 | char name[RSND_DAI_NAME_SIZE]; | ||
177 | struct rsnd_dai_platform_info *info; /* rcar_snd.h */ | ||
178 | struct rsnd_dai_stream playback; | ||
179 | struct rsnd_dai_stream capture; | ||
180 | |||
181 | int clk_master:1; | ||
182 | int bit_clk_inv:1; | ||
183 | int frm_clk_inv:1; | ||
184 | int sys_delay:1; | ||
185 | int data_alignment:1; | ||
186 | }; | ||
187 | |||
188 | #define rsnd_dai_nr(priv) ((priv)->dai_nr) | ||
189 | #define for_each_rsnd_dai(rdai, priv, i) \ | ||
190 | for (i = 0, (rdai) = rsnd_dai_get(priv, i); \ | ||
191 | i < rsnd_dai_nr(priv); \ | ||
192 | i++, (rdai) = rsnd_dai_get(priv, i)) | ||
193 | |||
194 | struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id); | ||
195 | int rsnd_dai_disconnect(struct rsnd_mod *mod); | ||
196 | int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod, | ||
197 | struct rsnd_dai_stream *io); | ||
198 | int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io); | ||
199 | int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai); | ||
200 | #define rsnd_dai_get_platform_info(rdai) ((rdai)->info) | ||
201 | #define rsnd_io_to_runtime(io) ((io)->substream->runtime) | ||
202 | |||
203 | void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt); | ||
204 | int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional); | ||
205 | |||
206 | /* | ||
207 | * R-Car Gen1/Gen2 | ||
208 | */ | ||
209 | int rsnd_gen_probe(struct platform_device *pdev, | ||
210 | struct rcar_snd_info *info, | ||
211 | struct rsnd_priv *priv); | ||
212 | void rsnd_gen_remove(struct platform_device *pdev, | ||
213 | struct rsnd_priv *priv); | ||
214 | int rsnd_gen_path_init(struct rsnd_priv *priv, | ||
215 | struct rsnd_dai *rdai, | ||
216 | struct rsnd_dai_stream *io); | ||
217 | int rsnd_gen_path_exit(struct rsnd_priv *priv, | ||
218 | struct rsnd_dai *rdai, | ||
219 | struct rsnd_dai_stream *io); | ||
220 | void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv, | ||
221 | struct rsnd_mod *mod, | ||
222 | enum rsnd_reg reg); | ||
223 | #define rsnd_is_gen1(s) ((s)->info->flags & RSND_GEN1) | ||
224 | #define rsnd_is_gen2(s) ((s)->info->flags & RSND_GEN2) | ||
225 | |||
226 | /* | ||
227 | * R-Car ADG | ||
228 | */ | ||
229 | int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod); | ||
230 | int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate); | ||
231 | int rsnd_adg_probe(struct platform_device *pdev, | ||
232 | struct rcar_snd_info *info, | ||
233 | struct rsnd_priv *priv); | ||
234 | void rsnd_adg_remove(struct platform_device *pdev, | ||
235 | struct rsnd_priv *priv); | ||
236 | |||
237 | /* | ||
238 | * R-Car sound priv | ||
239 | */ | ||
240 | struct rsnd_priv { | ||
241 | |||
242 | struct device *dev; | ||
243 | struct rcar_snd_info *info; | ||
244 | spinlock_t lock; | ||
245 | |||
246 | /* | ||
247 | * below value will be filled on rsnd_gen_probe() | ||
248 | */ | ||
249 | void *gen; | ||
250 | |||
251 | /* | ||
252 | * below value will be filled on rsnd_scu_probe() | ||
253 | */ | ||
254 | void *scu; | ||
255 | int scu_nr; | ||
256 | |||
257 | /* | ||
258 | * below value will be filled on rsnd_adg_probe() | ||
259 | */ | ||
260 | void *adg; | ||
261 | |||
262 | /* | ||
263 | * below value will be filled on rsnd_ssi_probe() | ||
264 | */ | ||
265 | void *ssiu; | ||
266 | |||
267 | /* | ||
268 | * below value will be filled on rsnd_dai_probe() | ||
269 | */ | ||
270 | struct snd_soc_dai_driver *daidrv; | ||
271 | struct rsnd_dai *rdai; | ||
272 | int dai_nr; | ||
273 | }; | ||
274 | |||
275 | #define rsnd_priv_to_dev(priv) ((priv)->dev) | ||
276 | #define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags) | ||
277 | #define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags) | ||
278 | |||
279 | /* | ||
280 | * R-Car SCU | ||
281 | */ | ||
282 | int rsnd_scu_probe(struct platform_device *pdev, | ||
283 | struct rcar_snd_info *info, | ||
284 | struct rsnd_priv *priv); | ||
285 | void rsnd_scu_remove(struct platform_device *pdev, | ||
286 | struct rsnd_priv *priv); | ||
287 | struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id); | ||
288 | #define rsnd_scu_nr(priv) ((priv)->scu_nr) | ||
289 | |||
290 | /* | ||
291 | * R-Car SSI | ||
292 | */ | ||
293 | int rsnd_ssi_probe(struct platform_device *pdev, | ||
294 | struct rcar_snd_info *info, | ||
295 | struct rsnd_priv *priv); | ||
296 | void rsnd_ssi_remove(struct platform_device *pdev, | ||
297 | struct rsnd_priv *priv); | ||
298 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id); | ||
299 | struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, | ||
300 | int dai_id, int is_play); | ||
301 | |||
302 | #endif | ||
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c new file mode 100644 index 000000000000..184d9008cecd --- /dev/null +++ b/sound/soc/sh/rcar/scu.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * Renesas R-Car SCU support | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | #include "rsnd.h" | ||
12 | |||
13 | struct rsnd_scu { | ||
14 | struct rsnd_scu_platform_info *info; /* rcar_snd.h */ | ||
15 | struct rsnd_mod mod; | ||
16 | }; | ||
17 | |||
18 | #define rsnd_scu_mode_flags(p) ((p)->info->flags) | ||
19 | |||
20 | /* | ||
21 | * ADINR | ||
22 | */ | ||
23 | #define OTBL_24 (0 << 16) | ||
24 | #define OTBL_22 (2 << 16) | ||
25 | #define OTBL_20 (4 << 16) | ||
26 | #define OTBL_18 (6 << 16) | ||
27 | #define OTBL_16 (8 << 16) | ||
28 | |||
29 | |||
30 | #define rsnd_mod_to_scu(_mod) \ | ||
31 | container_of((_mod), struct rsnd_scu, mod) | ||
32 | |||
33 | #define for_each_rsnd_scu(pos, priv, i) \ | ||
34 | for ((i) = 0; \ | ||
35 | ((i) < rsnd_scu_nr(priv)) && \ | ||
36 | ((pos) = (struct rsnd_scu *)(priv)->scu + i); \ | ||
37 | i++) | ||
38 | |||
39 | static int rsnd_scu_set_route(struct rsnd_priv *priv, | ||
40 | struct rsnd_mod *mod, | ||
41 | struct rsnd_dai *rdai, | ||
42 | struct rsnd_dai_stream *io) | ||
43 | { | ||
44 | struct scu_route_config { | ||
45 | u32 mask; | ||
46 | int shift; | ||
47 | } routes[] = { | ||
48 | { 0xF, 0, }, /* 0 */ | ||
49 | { 0xF, 4, }, /* 1 */ | ||
50 | { 0xF, 8, }, /* 2 */ | ||
51 | { 0x7, 12, }, /* 3 */ | ||
52 | { 0x7, 16, }, /* 4 */ | ||
53 | { 0x7, 20, }, /* 5 */ | ||
54 | { 0x7, 24, }, /* 6 */ | ||
55 | { 0x3, 28, }, /* 7 */ | ||
56 | { 0x3, 30, }, /* 8 */ | ||
57 | }; | ||
58 | |||
59 | u32 mask; | ||
60 | u32 val; | ||
61 | int shift; | ||
62 | int id; | ||
63 | |||
64 | /* | ||
65 | * Gen1 only | ||
66 | */ | ||
67 | if (!rsnd_is_gen1(priv)) | ||
68 | return 0; | ||
69 | |||
70 | id = rsnd_mod_id(mod); | ||
71 | if (id < 0 || id > ARRAY_SIZE(routes)) | ||
72 | return -EIO; | ||
73 | |||
74 | /* | ||
75 | * SRC_ROUTE_SELECT | ||
76 | */ | ||
77 | val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2; | ||
78 | val = val << routes[id].shift; | ||
79 | mask = routes[id].mask << routes[id].shift; | ||
80 | |||
81 | rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val); | ||
82 | |||
83 | /* | ||
84 | * SRC_TIMING_SELECT | ||
85 | */ | ||
86 | shift = (id % 4) * 8; | ||
87 | mask = 0x1F << shift; | ||
88 | if (8 == id) /* SRU8 is very special */ | ||
89 | val = id << shift; | ||
90 | else | ||
91 | val = (id + 1) << shift; | ||
92 | |||
93 | switch (id / 4) { | ||
94 | case 0: | ||
95 | rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val); | ||
96 | break; | ||
97 | case 1: | ||
98 | rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val); | ||
99 | break; | ||
100 | case 2: | ||
101 | rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val); | ||
102 | break; | ||
103 | } | ||
104 | |||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int rsnd_scu_set_mode(struct rsnd_priv *priv, | ||
109 | struct rsnd_mod *mod, | ||
110 | struct rsnd_dai *rdai, | ||
111 | struct rsnd_dai_stream *io) | ||
112 | { | ||
113 | int id = rsnd_mod_id(mod); | ||
114 | u32 val; | ||
115 | |||
116 | if (rsnd_is_gen1(priv)) { | ||
117 | val = (1 << id); | ||
118 | rsnd_mod_bset(mod, SRC_CTRL, val, val); | ||
119 | } | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | static int rsnd_scu_set_hpbif(struct rsnd_priv *priv, | ||
125 | struct rsnd_mod *mod, | ||
126 | struct rsnd_dai *rdai, | ||
127 | struct rsnd_dai_stream *io) | ||
128 | { | ||
129 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
130 | u32 adinr = runtime->channels; | ||
131 | |||
132 | switch (runtime->sample_bits) { | ||
133 | case 16: | ||
134 | adinr |= OTBL_16; | ||
135 | break; | ||
136 | case 32: | ||
137 | adinr |= OTBL_24; | ||
138 | break; | ||
139 | default: | ||
140 | return -EIO; | ||
141 | } | ||
142 | |||
143 | rsnd_mod_write(mod, BUSIF_MODE, 1); | ||
144 | rsnd_mod_write(mod, BUSIF_ADINR, adinr); | ||
145 | |||
146 | return 0; | ||
147 | } | ||
148 | |||
149 | static int rsnd_scu_start(struct rsnd_mod *mod, | ||
150 | struct rsnd_dai *rdai, | ||
151 | struct rsnd_dai_stream *io) | ||
152 | { | ||
153 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
154 | struct rsnd_scu *scu = rsnd_mod_to_scu(mod); | ||
155 | struct device *dev = rsnd_priv_to_dev(priv); | ||
156 | u32 flags = rsnd_scu_mode_flags(scu); | ||
157 | int ret; | ||
158 | |||
159 | /* | ||
160 | * SCU will be used if it has RSND_SCU_USB_HPBIF flags | ||
161 | */ | ||
162 | if (!(flags & RSND_SCU_USB_HPBIF)) { | ||
163 | /* it use PIO transter */ | ||
164 | dev_dbg(dev, "%s%d is not used\n", | ||
165 | rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
166 | |||
167 | return 0; | ||
168 | } | ||
169 | |||
170 | /* it use DMA transter */ | ||
171 | ret = rsnd_scu_set_route(priv, mod, rdai, io); | ||
172 | if (ret < 0) | ||
173 | return ret; | ||
174 | |||
175 | ret = rsnd_scu_set_mode(priv, mod, rdai, io); | ||
176 | if (ret < 0) | ||
177 | return ret; | ||
178 | |||
179 | ret = rsnd_scu_set_hpbif(priv, mod, rdai, io); | ||
180 | if (ret < 0) | ||
181 | return ret; | ||
182 | |||
183 | dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
184 | |||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | static struct rsnd_mod_ops rsnd_scu_ops = { | ||
189 | .name = "scu", | ||
190 | .start = rsnd_scu_start, | ||
191 | }; | ||
192 | |||
193 | struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id) | ||
194 | { | ||
195 | BUG_ON(id < 0 || id >= rsnd_scu_nr(priv)); | ||
196 | |||
197 | return &((struct rsnd_scu *)(priv->scu) + id)->mod; | ||
198 | } | ||
199 | |||
200 | int rsnd_scu_probe(struct platform_device *pdev, | ||
201 | struct rcar_snd_info *info, | ||
202 | struct rsnd_priv *priv) | ||
203 | { | ||
204 | struct device *dev = rsnd_priv_to_dev(priv); | ||
205 | struct rsnd_scu *scu; | ||
206 | int i, nr; | ||
207 | |||
208 | /* | ||
209 | * init SCU | ||
210 | */ | ||
211 | nr = info->scu_info_nr; | ||
212 | scu = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL); | ||
213 | if (!scu) { | ||
214 | dev_err(dev, "SCU allocate failed\n"); | ||
215 | return -ENOMEM; | ||
216 | } | ||
217 | |||
218 | priv->scu_nr = nr; | ||
219 | priv->scu = scu; | ||
220 | |||
221 | for_each_rsnd_scu(scu, priv, i) { | ||
222 | rsnd_mod_init(priv, &scu->mod, | ||
223 | &rsnd_scu_ops, i); | ||
224 | scu->info = &info->scu_info[i]; | ||
225 | |||
226 | dev_dbg(dev, "SCU%d probed\n", i); | ||
227 | } | ||
228 | dev_dbg(dev, "scu probed\n"); | ||
229 | |||
230 | return 0; | ||
231 | } | ||
232 | |||
233 | void rsnd_scu_remove(struct platform_device *pdev, | ||
234 | struct rsnd_priv *priv) | ||
235 | { | ||
236 | } | ||
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c new file mode 100644 index 000000000000..fae26d3f79d2 --- /dev/null +++ b/sound/soc/sh/rcar/ssi.c | |||
@@ -0,0 +1,728 @@ | |||
1 | /* | ||
2 | * Renesas R-Car SSIU/SSI support | ||
3 | * | ||
4 | * Copyright (C) 2013 Renesas Solutions Corp. | ||
5 | * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> | ||
6 | * | ||
7 | * Based on fsi.c | ||
8 | * Kuninori Morimoto <morimoto.kuninori@renesas.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | #include <linux/delay.h> | ||
15 | #include "rsnd.h" | ||
16 | #define RSND_SSI_NAME_SIZE 16 | ||
17 | |||
18 | /* | ||
19 | * SSICR | ||
20 | */ | ||
21 | #define FORCE (1 << 31) /* Fixed */ | ||
22 | #define DMEN (1 << 28) /* DMA Enable */ | ||
23 | #define UIEN (1 << 27) /* Underflow Interrupt Enable */ | ||
24 | #define OIEN (1 << 26) /* Overflow Interrupt Enable */ | ||
25 | #define IIEN (1 << 25) /* Idle Mode Interrupt Enable */ | ||
26 | #define DIEN (1 << 24) /* Data Interrupt Enable */ | ||
27 | |||
28 | #define DWL_8 (0 << 19) /* Data Word Length */ | ||
29 | #define DWL_16 (1 << 19) /* Data Word Length */ | ||
30 | #define DWL_18 (2 << 19) /* Data Word Length */ | ||
31 | #define DWL_20 (3 << 19) /* Data Word Length */ | ||
32 | #define DWL_22 (4 << 19) /* Data Word Length */ | ||
33 | #define DWL_24 (5 << 19) /* Data Word Length */ | ||
34 | #define DWL_32 (6 << 19) /* Data Word Length */ | ||
35 | |||
36 | #define SWL_32 (3 << 16) /* R/W System Word Length */ | ||
37 | #define SCKD (1 << 15) /* Serial Bit Clock Direction */ | ||
38 | #define SWSD (1 << 14) /* Serial WS Direction */ | ||
39 | #define SCKP (1 << 13) /* Serial Bit Clock Polarity */ | ||
40 | #define SWSP (1 << 12) /* Serial WS Polarity */ | ||
41 | #define SDTA (1 << 10) /* Serial Data Alignment */ | ||
42 | #define DEL (1 << 8) /* Serial Data Delay */ | ||
43 | #define CKDV(v) (v << 4) /* Serial Clock Division Ratio */ | ||
44 | #define TRMD (1 << 1) /* Transmit/Receive Mode Select */ | ||
45 | #define EN (1 << 0) /* SSI Module Enable */ | ||
46 | |||
47 | /* | ||
48 | * SSISR | ||
49 | */ | ||
50 | #define UIRQ (1 << 27) /* Underflow Error Interrupt Status */ | ||
51 | #define OIRQ (1 << 26) /* Overflow Error Interrupt Status */ | ||
52 | #define IIRQ (1 << 25) /* Idle Mode Interrupt Status */ | ||
53 | #define DIRQ (1 << 24) /* Data Interrupt Status Flag */ | ||
54 | |||
55 | /* | ||
56 | * SSIWSR | ||
57 | */ | ||
58 | #define CONT (1 << 8) /* WS Continue Function */ | ||
59 | |||
60 | struct rsnd_ssi { | ||
61 | struct clk *clk; | ||
62 | struct rsnd_ssi_platform_info *info; /* rcar_snd.h */ | ||
63 | struct rsnd_ssi *parent; | ||
64 | struct rsnd_mod mod; | ||
65 | |||
66 | struct rsnd_dai *rdai; | ||
67 | struct rsnd_dai_stream *io; | ||
68 | u32 cr_own; | ||
69 | u32 cr_clk; | ||
70 | u32 cr_etc; | ||
71 | int err; | ||
72 | int dma_offset; | ||
73 | unsigned int usrcnt; | ||
74 | unsigned int rate; | ||
75 | }; | ||
76 | |||
77 | struct rsnd_ssiu { | ||
78 | u32 ssi_mode0; | ||
79 | u32 ssi_mode1; | ||
80 | |||
81 | int ssi_nr; | ||
82 | struct rsnd_ssi *ssi; | ||
83 | }; | ||
84 | |||
85 | #define for_each_rsnd_ssi(pos, priv, i) \ | ||
86 | for (i = 0; \ | ||
87 | (i < rsnd_ssi_nr(priv)) && \ | ||
88 | ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \ | ||
89 | i++) | ||
90 | |||
91 | #define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr) | ||
92 | #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod) | ||
93 | #define rsnd_dma_to_ssi(dma) rsnd_mod_to_ssi(rsnd_dma_to_mod(dma)) | ||
94 | #define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0) | ||
95 | #define rsnd_ssi_dma_available(ssi) \ | ||
96 | rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod)) | ||
97 | #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent) | ||
98 | #define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master) | ||
99 | #define rsnd_ssi_mode_flags(p) ((p)->info->flags) | ||
100 | #define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id) | ||
101 | #define rsnd_ssi_to_ssiu(ssi)\ | ||
102 | (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1) | ||
103 | |||
104 | static void rsnd_ssi_mode_init(struct rsnd_priv *priv, | ||
105 | struct rsnd_ssiu *ssiu) | ||
106 | { | ||
107 | struct device *dev = rsnd_priv_to_dev(priv); | ||
108 | struct rsnd_ssi *ssi; | ||
109 | u32 flags; | ||
110 | u32 val; | ||
111 | int i; | ||
112 | |||
113 | /* | ||
114 | * SSI_MODE0 | ||
115 | */ | ||
116 | ssiu->ssi_mode0 = 0; | ||
117 | for_each_rsnd_ssi(ssi, priv, i) { | ||
118 | flags = rsnd_ssi_mode_flags(ssi); | ||
119 | |||
120 | /* see also BUSIF_MODE */ | ||
121 | if (!(flags & RSND_SSI_DEPENDENT)) { | ||
122 | ssiu->ssi_mode0 |= (1 << i); | ||
123 | dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i); | ||
124 | } else { | ||
125 | dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | /* | ||
130 | * SSI_MODE1 | ||
131 | */ | ||
132 | #define ssi_parent_set(p, sync, adg, ext) \ | ||
133 | do { \ | ||
134 | ssi->parent = ssiu->ssi + p; \ | ||
135 | if (flags & RSND_SSI_CLK_FROM_ADG) \ | ||
136 | val = adg; \ | ||
137 | else \ | ||
138 | val = ext; \ | ||
139 | if (flags & RSND_SSI_SYNC) \ | ||
140 | val |= sync; \ | ||
141 | } while (0) | ||
142 | |||
143 | ssiu->ssi_mode1 = 0; | ||
144 | for_each_rsnd_ssi(ssi, priv, i) { | ||
145 | flags = rsnd_ssi_mode_flags(ssi); | ||
146 | |||
147 | if (!(flags & RSND_SSI_CLK_PIN_SHARE)) | ||
148 | continue; | ||
149 | |||
150 | val = 0; | ||
151 | switch (i) { | ||
152 | case 1: | ||
153 | ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0)); | ||
154 | break; | ||
155 | case 2: | ||
156 | ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2)); | ||
157 | break; | ||
158 | case 4: | ||
159 | ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16)); | ||
160 | break; | ||
161 | case 8: | ||
162 | ssi_parent_set(7, 0, 0, 0); | ||
163 | break; | ||
164 | } | ||
165 | |||
166 | ssiu->ssi_mode1 |= val; | ||
167 | } | ||
168 | } | ||
169 | |||
170 | static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi) | ||
171 | { | ||
172 | struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi); | ||
173 | |||
174 | rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0); | ||
175 | rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1); | ||
176 | } | ||
177 | |||
178 | static void rsnd_ssi_status_check(struct rsnd_mod *mod, | ||
179 | u32 bit) | ||
180 | { | ||
181 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
182 | struct device *dev = rsnd_priv_to_dev(priv); | ||
183 | u32 status; | ||
184 | int i; | ||
185 | |||
186 | for (i = 0; i < 1024; i++) { | ||
187 | status = rsnd_mod_read(mod, SSISR); | ||
188 | if (status & bit) | ||
189 | return; | ||
190 | |||
191 | udelay(50); | ||
192 | } | ||
193 | |||
194 | dev_warn(dev, "status check failed\n"); | ||
195 | } | ||
196 | |||
197 | static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi, | ||
198 | unsigned int rate) | ||
199 | { | ||
200 | struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); | ||
201 | struct device *dev = rsnd_priv_to_dev(priv); | ||
202 | int i, j, ret; | ||
203 | int adg_clk_div_table[] = { | ||
204 | 1, 6, /* see adg.c */ | ||
205 | }; | ||
206 | int ssi_clk_mul_table[] = { | ||
207 | 1, 2, 4, 8, 16, 6, 12, | ||
208 | }; | ||
209 | unsigned int main_rate; | ||
210 | |||
211 | /* | ||
212 | * Find best clock, and try to start ADG | ||
213 | */ | ||
214 | for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) { | ||
215 | for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) { | ||
216 | |||
217 | /* | ||
218 | * this driver is assuming that | ||
219 | * system word is 64fs (= 2 x 32bit) | ||
220 | * see rsnd_ssi_start() | ||
221 | */ | ||
222 | main_rate = rate / adg_clk_div_table[i] | ||
223 | * 32 * 2 * ssi_clk_mul_table[j]; | ||
224 | |||
225 | ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate); | ||
226 | if (0 == ret) { | ||
227 | ssi->rate = rate; | ||
228 | ssi->cr_clk = FORCE | SWL_32 | | ||
229 | SCKD | SWSD | CKDV(j); | ||
230 | |||
231 | dev_dbg(dev, "ssi%d outputs %u Hz\n", | ||
232 | rsnd_mod_id(&ssi->mod), rate); | ||
233 | |||
234 | return 0; | ||
235 | } | ||
236 | } | ||
237 | } | ||
238 | |||
239 | dev_err(dev, "unsupported clock rate\n"); | ||
240 | return -EIO; | ||
241 | } | ||
242 | |||
243 | static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi) | ||
244 | { | ||
245 | ssi->rate = 0; | ||
246 | ssi->cr_clk = 0; | ||
247 | rsnd_adg_ssi_clk_stop(&ssi->mod); | ||
248 | } | ||
249 | |||
250 | static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi, | ||
251 | struct rsnd_dai *rdai, | ||
252 | struct rsnd_dai_stream *io) | ||
253 | { | ||
254 | struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); | ||
255 | struct device *dev = rsnd_priv_to_dev(priv); | ||
256 | u32 cr; | ||
257 | |||
258 | if (0 == ssi->usrcnt) { | ||
259 | clk_enable(ssi->clk); | ||
260 | |||
261 | if (rsnd_rdai_is_clk_master(rdai)) { | ||
262 | struct snd_pcm_runtime *runtime; | ||
263 | |||
264 | runtime = rsnd_io_to_runtime(io); | ||
265 | |||
266 | if (rsnd_ssi_clk_from_parent(ssi)) | ||
267 | rsnd_ssi_hw_start(ssi->parent, rdai, io); | ||
268 | else | ||
269 | rsnd_ssi_master_clk_start(ssi, runtime->rate); | ||
270 | } | ||
271 | } | ||
272 | |||
273 | cr = ssi->cr_own | | ||
274 | ssi->cr_clk | | ||
275 | ssi->cr_etc | | ||
276 | EN; | ||
277 | |||
278 | rsnd_mod_write(&ssi->mod, SSICR, cr); | ||
279 | |||
280 | ssi->usrcnt++; | ||
281 | |||
282 | dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod)); | ||
283 | } | ||
284 | |||
285 | static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi, | ||
286 | struct rsnd_dai *rdai) | ||
287 | { | ||
288 | struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod); | ||
289 | struct device *dev = rsnd_priv_to_dev(priv); | ||
290 | u32 cr; | ||
291 | |||
292 | if (0 == ssi->usrcnt) /* stop might be called without start */ | ||
293 | return; | ||
294 | |||
295 | ssi->usrcnt--; | ||
296 | |||
297 | if (0 == ssi->usrcnt) { | ||
298 | /* | ||
299 | * disable all IRQ, | ||
300 | * and, wait all data was sent | ||
301 | */ | ||
302 | cr = ssi->cr_own | | ||
303 | ssi->cr_clk; | ||
304 | |||
305 | rsnd_mod_write(&ssi->mod, SSICR, cr | EN); | ||
306 | rsnd_ssi_status_check(&ssi->mod, DIRQ); | ||
307 | |||
308 | /* | ||
309 | * disable SSI, | ||
310 | * and, wait idle state | ||
311 | */ | ||
312 | rsnd_mod_write(&ssi->mod, SSICR, cr); /* disabled all */ | ||
313 | rsnd_ssi_status_check(&ssi->mod, IIRQ); | ||
314 | |||
315 | if (rsnd_rdai_is_clk_master(rdai)) { | ||
316 | if (rsnd_ssi_clk_from_parent(ssi)) | ||
317 | rsnd_ssi_hw_stop(ssi->parent, rdai); | ||
318 | else | ||
319 | rsnd_ssi_master_clk_stop(ssi); | ||
320 | } | ||
321 | |||
322 | clk_disable(ssi->clk); | ||
323 | } | ||
324 | |||
325 | dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod)); | ||
326 | } | ||
327 | |||
328 | /* | ||
329 | * SSI mod common functions | ||
330 | */ | ||
331 | static int rsnd_ssi_init(struct rsnd_mod *mod, | ||
332 | struct rsnd_dai *rdai, | ||
333 | struct rsnd_dai_stream *io) | ||
334 | { | ||
335 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
336 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
337 | struct device *dev = rsnd_priv_to_dev(priv); | ||
338 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
339 | u32 cr; | ||
340 | |||
341 | cr = FORCE; | ||
342 | |||
343 | /* | ||
344 | * always use 32bit system word for easy clock calculation. | ||
345 | * see also rsnd_ssi_master_clk_enable() | ||
346 | */ | ||
347 | cr |= SWL_32; | ||
348 | |||
349 | /* | ||
350 | * init clock settings for SSICR | ||
351 | */ | ||
352 | switch (runtime->sample_bits) { | ||
353 | case 16: | ||
354 | cr |= DWL_16; | ||
355 | break; | ||
356 | case 32: | ||
357 | cr |= DWL_24; | ||
358 | break; | ||
359 | default: | ||
360 | return -EIO; | ||
361 | } | ||
362 | |||
363 | if (rdai->bit_clk_inv) | ||
364 | cr |= SCKP; | ||
365 | if (rdai->frm_clk_inv) | ||
366 | cr |= SWSP; | ||
367 | if (rdai->data_alignment) | ||
368 | cr |= SDTA; | ||
369 | if (rdai->sys_delay) | ||
370 | cr |= DEL; | ||
371 | if (rsnd_dai_is_play(rdai, io)) | ||
372 | cr |= TRMD; | ||
373 | |||
374 | /* | ||
375 | * set ssi parameter | ||
376 | */ | ||
377 | ssi->rdai = rdai; | ||
378 | ssi->io = io; | ||
379 | ssi->cr_own = cr; | ||
380 | ssi->err = -1; /* ignore 1st error */ | ||
381 | |||
382 | rsnd_ssi_mode_set(ssi); | ||
383 | |||
384 | dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
385 | |||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | static int rsnd_ssi_quit(struct rsnd_mod *mod, | ||
390 | struct rsnd_dai *rdai, | ||
391 | struct rsnd_dai_stream *io) | ||
392 | { | ||
393 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
394 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
395 | struct device *dev = rsnd_priv_to_dev(priv); | ||
396 | |||
397 | dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
398 | |||
399 | if (ssi->err > 0) | ||
400 | dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err); | ||
401 | |||
402 | ssi->rdai = NULL; | ||
403 | ssi->io = NULL; | ||
404 | ssi->cr_own = 0; | ||
405 | ssi->err = 0; | ||
406 | |||
407 | return 0; | ||
408 | } | ||
409 | |||
410 | static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status) | ||
411 | { | ||
412 | /* under/over flow error */ | ||
413 | if (status & (UIRQ | OIRQ)) { | ||
414 | ssi->err++; | ||
415 | |||
416 | /* clear error status */ | ||
417 | rsnd_mod_write(&ssi->mod, SSISR, 0); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | /* | ||
422 | * SSI PIO | ||
423 | */ | ||
424 | static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data) | ||
425 | { | ||
426 | struct rsnd_ssi *ssi = data; | ||
427 | struct rsnd_dai_stream *io = ssi->io; | ||
428 | u32 status = rsnd_mod_read(&ssi->mod, SSISR); | ||
429 | irqreturn_t ret = IRQ_NONE; | ||
430 | |||
431 | if (io && (status & DIRQ)) { | ||
432 | struct rsnd_dai *rdai = ssi->rdai; | ||
433 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
434 | u32 *buf = (u32 *)(runtime->dma_area + | ||
435 | rsnd_dai_pointer_offset(io, 0)); | ||
436 | |||
437 | rsnd_ssi_record_error(ssi, status); | ||
438 | |||
439 | /* | ||
440 | * 8/16/32 data can be assesse to TDR/RDR register | ||
441 | * directly as 32bit data | ||
442 | * see rsnd_ssi_init() | ||
443 | */ | ||
444 | if (rsnd_dai_is_play(rdai, io)) | ||
445 | rsnd_mod_write(&ssi->mod, SSITDR, *buf); | ||
446 | else | ||
447 | *buf = rsnd_mod_read(&ssi->mod, SSIRDR); | ||
448 | |||
449 | rsnd_dai_pointer_update(io, sizeof(*buf)); | ||
450 | |||
451 | ret = IRQ_HANDLED; | ||
452 | } | ||
453 | |||
454 | return ret; | ||
455 | } | ||
456 | |||
457 | static int rsnd_ssi_pio_start(struct rsnd_mod *mod, | ||
458 | struct rsnd_dai *rdai, | ||
459 | struct rsnd_dai_stream *io) | ||
460 | { | ||
461 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
462 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
463 | struct device *dev = rsnd_priv_to_dev(priv); | ||
464 | |||
465 | /* enable PIO IRQ */ | ||
466 | ssi->cr_etc = UIEN | OIEN | DIEN; | ||
467 | |||
468 | rsnd_ssi_hw_start(ssi, rdai, io); | ||
469 | |||
470 | dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
471 | |||
472 | return 0; | ||
473 | } | ||
474 | |||
475 | static int rsnd_ssi_pio_stop(struct rsnd_mod *mod, | ||
476 | struct rsnd_dai *rdai, | ||
477 | struct rsnd_dai_stream *io) | ||
478 | { | ||
479 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
480 | struct device *dev = rsnd_priv_to_dev(priv); | ||
481 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
482 | |||
483 | dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod)); | ||
484 | |||
485 | ssi->cr_etc = 0; | ||
486 | |||
487 | rsnd_ssi_hw_stop(ssi, rdai); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static struct rsnd_mod_ops rsnd_ssi_pio_ops = { | ||
493 | .name = "ssi (pio)", | ||
494 | .init = rsnd_ssi_init, | ||
495 | .quit = rsnd_ssi_quit, | ||
496 | .start = rsnd_ssi_pio_start, | ||
497 | .stop = rsnd_ssi_pio_stop, | ||
498 | }; | ||
499 | |||
500 | static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len) | ||
501 | { | ||
502 | struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); | ||
503 | struct rsnd_dai_stream *io = ssi->io; | ||
504 | struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io); | ||
505 | |||
506 | *len = io->byte_per_period; | ||
507 | *buf = runtime->dma_addr + | ||
508 | rsnd_dai_pointer_offset(io, ssi->dma_offset + *len); | ||
509 | ssi->dma_offset = *len; /* it cares A/B plane */ | ||
510 | |||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | static int rsnd_ssi_dma_complete(struct rsnd_dma *dma) | ||
515 | { | ||
516 | struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma); | ||
517 | struct rsnd_dai_stream *io = ssi->io; | ||
518 | u32 status = rsnd_mod_read(&ssi->mod, SSISR); | ||
519 | |||
520 | rsnd_ssi_record_error(ssi, status); | ||
521 | |||
522 | rsnd_dai_pointer_update(ssi->io, io->byte_per_period); | ||
523 | |||
524 | return 0; | ||
525 | } | ||
526 | |||
527 | static int rsnd_ssi_dma_start(struct rsnd_mod *mod, | ||
528 | struct rsnd_dai *rdai, | ||
529 | struct rsnd_dai_stream *io) | ||
530 | { | ||
531 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
532 | struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); | ||
533 | |||
534 | /* enable DMA transfer */ | ||
535 | ssi->cr_etc = DMEN; | ||
536 | ssi->dma_offset = 0; | ||
537 | |||
538 | rsnd_dma_start(dma); | ||
539 | |||
540 | rsnd_ssi_hw_start(ssi, ssi->rdai, io); | ||
541 | |||
542 | /* enable WS continue */ | ||
543 | if (rsnd_rdai_is_clk_master(rdai)) | ||
544 | rsnd_mod_write(&ssi->mod, SSIWSR, CONT); | ||
545 | |||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static int rsnd_ssi_dma_stop(struct rsnd_mod *mod, | ||
550 | struct rsnd_dai *rdai, | ||
551 | struct rsnd_dai_stream *io) | ||
552 | { | ||
553 | struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod); | ||
554 | struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod); | ||
555 | |||
556 | ssi->cr_etc = 0; | ||
557 | |||
558 | rsnd_ssi_hw_stop(ssi, rdai); | ||
559 | |||
560 | rsnd_dma_stop(dma); | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | static struct rsnd_mod_ops rsnd_ssi_dma_ops = { | ||
566 | .name = "ssi (dma)", | ||
567 | .init = rsnd_ssi_init, | ||
568 | .quit = rsnd_ssi_quit, | ||
569 | .start = rsnd_ssi_dma_start, | ||
570 | .stop = rsnd_ssi_dma_stop, | ||
571 | }; | ||
572 | |||
573 | /* | ||
574 | * Non SSI | ||
575 | */ | ||
576 | static int rsnd_ssi_non(struct rsnd_mod *mod, | ||
577 | struct rsnd_dai *rdai, | ||
578 | struct rsnd_dai_stream *io) | ||
579 | { | ||
580 | struct rsnd_priv *priv = rsnd_mod_to_priv(mod); | ||
581 | struct device *dev = rsnd_priv_to_dev(priv); | ||
582 | |||
583 | dev_dbg(dev, "%s\n", __func__); | ||
584 | |||
585 | return 0; | ||
586 | } | ||
587 | |||
588 | static struct rsnd_mod_ops rsnd_ssi_non_ops = { | ||
589 | .name = "ssi (non)", | ||
590 | .init = rsnd_ssi_non, | ||
591 | .quit = rsnd_ssi_non, | ||
592 | .start = rsnd_ssi_non, | ||
593 | .stop = rsnd_ssi_non, | ||
594 | }; | ||
595 | |||
596 | /* | ||
597 | * ssi mod function | ||
598 | */ | ||
599 | struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv, | ||
600 | int dai_id, int is_play) | ||
601 | { | ||
602 | struct rsnd_ssi *ssi; | ||
603 | int i, has_play; | ||
604 | |||
605 | is_play = !!is_play; | ||
606 | |||
607 | for_each_rsnd_ssi(ssi, priv, i) { | ||
608 | if (rsnd_ssi_dai_id(ssi) != dai_id) | ||
609 | continue; | ||
610 | |||
611 | has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY); | ||
612 | |||
613 | if (is_play == has_play) | ||
614 | return &ssi->mod; | ||
615 | } | ||
616 | |||
617 | return NULL; | ||
618 | } | ||
619 | |||
620 | struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id) | ||
621 | { | ||
622 | BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv)); | ||
623 | |||
624 | return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod; | ||
625 | } | ||
626 | |||
627 | int rsnd_ssi_probe(struct platform_device *pdev, | ||
628 | struct rcar_snd_info *info, | ||
629 | struct rsnd_priv *priv) | ||
630 | { | ||
631 | struct rsnd_ssi_platform_info *pinfo; | ||
632 | struct device *dev = rsnd_priv_to_dev(priv); | ||
633 | struct rsnd_mod_ops *ops; | ||
634 | struct clk *clk; | ||
635 | struct rsnd_ssiu *ssiu; | ||
636 | struct rsnd_ssi *ssi; | ||
637 | char name[RSND_SSI_NAME_SIZE]; | ||
638 | int i, nr, ret; | ||
639 | |||
640 | /* | ||
641 | * init SSI | ||
642 | */ | ||
643 | nr = info->ssi_info_nr; | ||
644 | ssiu = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr), | ||
645 | GFP_KERNEL); | ||
646 | if (!ssiu) { | ||
647 | dev_err(dev, "SSI allocate failed\n"); | ||
648 | return -ENOMEM; | ||
649 | } | ||
650 | |||
651 | priv->ssiu = ssiu; | ||
652 | ssiu->ssi = (struct rsnd_ssi *)(ssiu + 1); | ||
653 | ssiu->ssi_nr = nr; | ||
654 | |||
655 | for_each_rsnd_ssi(ssi, priv, i) { | ||
656 | pinfo = &info->ssi_info[i]; | ||
657 | |||
658 | snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i); | ||
659 | |||
660 | clk = clk_get(dev, name); | ||
661 | if (IS_ERR(clk)) | ||
662 | return PTR_ERR(clk); | ||
663 | |||
664 | ssi->info = pinfo; | ||
665 | ssi->clk = clk; | ||
666 | |||
667 | ops = &rsnd_ssi_non_ops; | ||
668 | |||
669 | /* | ||
670 | * SSI DMA case | ||
671 | */ | ||
672 | if (pinfo->dma_id > 0) { | ||
673 | ret = rsnd_dma_init( | ||
674 | priv, rsnd_mod_to_dma(&ssi->mod), | ||
675 | (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY), | ||
676 | pinfo->dma_id, | ||
677 | rsnd_ssi_dma_inquiry, | ||
678 | rsnd_ssi_dma_complete); | ||
679 | if (ret < 0) | ||
680 | dev_info(dev, "SSI DMA failed. try PIO transter\n"); | ||
681 | else | ||
682 | ops = &rsnd_ssi_dma_ops; | ||
683 | |||
684 | dev_dbg(dev, "SSI%d use DMA transfer\n", i); | ||
685 | } | ||
686 | |||
687 | /* | ||
688 | * SSI PIO case | ||
689 | */ | ||
690 | if (!rsnd_ssi_dma_available(ssi) && | ||
691 | rsnd_ssi_pio_available(ssi)) { | ||
692 | ret = devm_request_irq(dev, pinfo->pio_irq, | ||
693 | &rsnd_ssi_pio_interrupt, | ||
694 | IRQF_SHARED, | ||
695 | dev_name(dev), ssi); | ||
696 | if (ret) { | ||
697 | dev_err(dev, "SSI request interrupt failed\n"); | ||
698 | return ret; | ||
699 | } | ||
700 | |||
701 | ops = &rsnd_ssi_pio_ops; | ||
702 | |||
703 | dev_dbg(dev, "SSI%d use PIO transfer\n", i); | ||
704 | } | ||
705 | |||
706 | rsnd_mod_init(priv, &ssi->mod, ops, i); | ||
707 | } | ||
708 | |||
709 | rsnd_ssi_mode_init(priv, ssiu); | ||
710 | |||
711 | dev_dbg(dev, "ssi probed\n"); | ||
712 | |||
713 | return 0; | ||
714 | } | ||
715 | |||
716 | void rsnd_ssi_remove(struct platform_device *pdev, | ||
717 | struct rsnd_priv *priv) | ||
718 | { | ||
719 | struct rsnd_ssi *ssi; | ||
720 | int i; | ||
721 | |||
722 | for_each_rsnd_ssi(ssi, priv, i) { | ||
723 | clk_put(ssi->clk); | ||
724 | if (rsnd_ssi_dma_available(ssi)) | ||
725 | rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod)); | ||
726 | } | ||
727 | |||
728 | } | ||