diff options
author | Lars-Peter Clausen <lars@metafoo.de> | 2013-05-28 13:22:17 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2013-05-30 07:33:41 -0400 |
commit | cc37961b21eb3d57d421ca34ffec9bbe0a6096c0 (patch) | |
tree | 3a10e59aae5e9788b8920e9acaafbfe7cb3f8223 /sound/soc/blackfin | |
parent | 34f4095564ff334adae5ab4a9904f8d66d03e994 (diff) |
ASoC: blackfin: Remove bf5xx-tdm driver
Now that the bf5xx-i2s driver supports TDM mode and all users of the bf5xx-tdm
driver have been switch over to using the bf5xx-i2s driver there is no need to
keep the b5fxx-tdm driver around.
Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Diffstat (limited to 'sound/soc/blackfin')
-rw-r--r-- | sound/soc/blackfin/Kconfig | 13 | ||||
-rw-r--r-- | sound/soc/blackfin/Makefile | 4 | ||||
-rw-r--r-- | sound/soc/blackfin/bf5xx-tdm-pcm.c | 343 | ||||
-rw-r--r-- | sound/soc/blackfin/bf5xx-tdm.c | 328 | ||||
-rw-r--r-- | sound/soc/blackfin/bf5xx-tdm.h | 23 |
5 files changed, 0 insertions, 711 deletions
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig index 4a67865bc4fc..54f74f8cbb75 100644 --- a/sound/soc/blackfin/Kconfig +++ b/sound/soc/blackfin/Kconfig | |||
@@ -89,16 +89,6 @@ config SND_BFIN_AD73311_SE | |||
89 | Enter the GPIO used to control AD73311's SE pin. Acceptable | 89 | Enter the GPIO used to control AD73311's SE pin. Acceptable |
90 | values are 0 to 7 | 90 | values are 0 to 7 |
91 | 91 | ||
92 | config SND_BF5XX_TDM | ||
93 | tristate "SoC I2S(TDM mode) Audio for the ADI BF5xx chip" | ||
94 | depends on (BLACKFIN && SND_SOC) | ||
95 | select SND_BF5XX_SOC_SPORT | ||
96 | help | ||
97 | Say Y or M if you want to add support for codecs attached to | ||
98 | the Blackfin SPORT (synchronous serial ports) interface in TDM | ||
99 | mode. | ||
100 | You will also need to select the audio interfaces to support below. | ||
101 | |||
102 | config SND_BF5XX_AC97 | 92 | config SND_BF5XX_AC97 |
103 | tristate "SoC AC97 Audio for the ADI BF5xx chip" | 93 | tristate "SoC AC97 Audio for the ADI BF5xx chip" |
104 | depends on BLACKFIN | 94 | depends on BLACKFIN |
@@ -174,9 +164,6 @@ config SND_BF5XX_SOC_I2S | |||
174 | config SND_BF6XX_SOC_I2S | 164 | config SND_BF6XX_SOC_I2S |
175 | tristate | 165 | tristate |
176 | 166 | ||
177 | config SND_BF5XX_SOC_TDM | ||
178 | tristate | ||
179 | |||
180 | config SND_BF5XX_SOC_AC97 | 167 | config SND_BF5XX_SOC_AC97 |
181 | tristate | 168 | tristate |
182 | 169 | ||
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile index 6fea1f4cbee2..ad0a6e99bc5d 100644 --- a/sound/soc/blackfin/Makefile +++ b/sound/soc/blackfin/Makefile | |||
@@ -1,23 +1,19 @@ | |||
1 | # Blackfin Platform Support | 1 | # Blackfin Platform Support |
2 | snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o | 2 | snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o |
3 | snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o | 3 | snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o |
4 | snd-bf5xx-tdm-objs := bf5xx-tdm-pcm.o | ||
5 | snd-soc-bf5xx-sport-objs := bf5xx-sport.o | 4 | snd-soc-bf5xx-sport-objs := bf5xx-sport.o |
6 | snd-soc-bf6xx-sport-objs := bf6xx-sport.o | 5 | snd-soc-bf6xx-sport-objs := bf6xx-sport.o |
7 | snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o | 6 | snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o |
8 | snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o | 7 | snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o |
9 | snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o | 8 | snd-soc-bf6xx-i2s-objs := bf6xx-i2s.o |
10 | snd-soc-bf5xx-tdm-objs := bf5xx-tdm.o | ||
11 | 9 | ||
12 | obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o | 10 | obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o |
13 | obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o | 11 | obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o |
14 | obj-$(CONFIG_SND_BF5XX_TDM) += snd-bf5xx-tdm.o | ||
15 | obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o | 12 | obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o |
16 | obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o | 13 | obj-$(CONFIG_SND_BF6XX_SOC_SPORT) += snd-soc-bf6xx-sport.o |
17 | obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o | 14 | obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o |
18 | obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o | 15 | obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o |
19 | obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o | 16 | obj-$(CONFIG_SND_BF6XX_SOC_I2S) += snd-soc-bf6xx-i2s.o |
20 | obj-$(CONFIG_SND_BF5XX_SOC_TDM) += snd-soc-bf5xx-tdm.o | ||
21 | 17 | ||
22 | # Blackfin Machine Support | 18 | # Blackfin Machine Support |
23 | snd-ad1836-objs := bf5xx-ad1836.o | 19 | snd-ad1836-objs := bf5xx-ad1836.o |
diff --git a/sound/soc/blackfin/bf5xx-tdm-pcm.c b/sound/soc/blackfin/bf5xx-tdm-pcm.c deleted file mode 100644 index a6b5457036ef..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm-pcm.c +++ /dev/null | |||
@@ -1,343 +0,0 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-tdm-pcm.c | ||
3 | * Author: Barry Song <Barry.Song@analog.com> | ||
4 | * | ||
5 | * Created: Tue June 06 2009 | ||
6 | * Description: DMA driver for tdm codec | ||
7 | * | ||
8 | * Modified: | ||
9 | * Copyright 2009 Analog Devices Inc. | ||
10 | * | ||
11 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
12 | * | ||
13 | * This program is free software; you can redistribute it and/or modify | ||
14 | * it under the terms of the GNU General Public License as published by | ||
15 | * the Free Software Foundation; either version 2 of the License, or | ||
16 | * (at your option) any later version. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | * | ||
23 | * You should have received a copy of the GNU General Public License | ||
24 | * along with this program; if not, see the file COPYING, or write | ||
25 | * to the Free Software Foundation, Inc., | ||
26 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
27 | */ | ||
28 | |||
29 | #include <linux/module.h> | ||
30 | #include <linux/init.h> | ||
31 | #include <linux/platform_device.h> | ||
32 | #include <linux/dma-mapping.h> | ||
33 | #include <linux/gfp.h> | ||
34 | |||
35 | #include <sound/core.h> | ||
36 | #include <sound/pcm.h> | ||
37 | #include <sound/pcm_params.h> | ||
38 | #include <sound/soc.h> | ||
39 | |||
40 | #include <asm/dma.h> | ||
41 | |||
42 | #include "bf5xx-tdm.h" | ||
43 | #include "bf5xx-sport.h" | ||
44 | |||
45 | #define PCM_BUFFER_MAX 0x8000 | ||
46 | #define FRAGMENT_SIZE_MIN (4*1024) | ||
47 | #define FRAGMENTS_MIN 2 | ||
48 | #define FRAGMENTS_MAX 32 | ||
49 | |||
50 | static void bf5xx_dma_irq(void *data) | ||
51 | { | ||
52 | struct snd_pcm_substream *pcm = data; | ||
53 | snd_pcm_period_elapsed(pcm); | ||
54 | } | ||
55 | |||
56 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | ||
57 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | | ||
58 | SNDRV_PCM_INFO_RESUME), | ||
59 | .formats = SNDRV_PCM_FMTBIT_S32_LE, | ||
60 | .rates = SNDRV_PCM_RATE_48000, | ||
61 | .channels_min = 2, | ||
62 | .channels_max = 8, | ||
63 | .buffer_bytes_max = PCM_BUFFER_MAX, | ||
64 | .period_bytes_min = FRAGMENT_SIZE_MIN, | ||
65 | .period_bytes_max = PCM_BUFFER_MAX/2, | ||
66 | .periods_min = FRAGMENTS_MIN, | ||
67 | .periods_max = FRAGMENTS_MAX, | ||
68 | }; | ||
69 | |||
70 | static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | ||
71 | struct snd_pcm_hw_params *params) | ||
72 | { | ||
73 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; | ||
74 | snd_pcm_lib_malloc_pages(substream, size * 4); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | ||
80 | { | ||
81 | snd_pcm_lib_free_pages(substream); | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | ||
87 | { | ||
88 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
89 | struct sport_device *sport = runtime->private_data; | ||
90 | int fragsize_bytes = frames_to_bytes(runtime, runtime->period_size); | ||
91 | |||
92 | fragsize_bytes /= runtime->channels; | ||
93 | /* inflate the fragsize to match the dma width of SPORT */ | ||
94 | fragsize_bytes *= 8; | ||
95 | |||
96 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
97 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); | ||
98 | sport_config_tx_dma(sport, runtime->dma_area, | ||
99 | runtime->periods, fragsize_bytes); | ||
100 | } else { | ||
101 | sport_set_rx_callback(sport, bf5xx_dma_irq, substream); | ||
102 | sport_config_rx_dma(sport, runtime->dma_area, | ||
103 | runtime->periods, fragsize_bytes); | ||
104 | } | ||
105 | |||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | ||
110 | { | ||
111 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
112 | struct sport_device *sport = runtime->private_data; | ||
113 | int ret = 0; | ||
114 | |||
115 | switch (cmd) { | ||
116 | case SNDRV_PCM_TRIGGER_START: | ||
117 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
118 | sport_tx_start(sport); | ||
119 | else | ||
120 | sport_rx_start(sport); | ||
121 | break; | ||
122 | case SNDRV_PCM_TRIGGER_STOP: | ||
123 | case SNDRV_PCM_TRIGGER_SUSPEND: | ||
124 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
125 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
126 | sport_tx_stop(sport); | ||
127 | else | ||
128 | sport_rx_stop(sport); | ||
129 | break; | ||
130 | default: | ||
131 | ret = -EINVAL; | ||
132 | } | ||
133 | |||
134 | return ret; | ||
135 | } | ||
136 | |||
137 | static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | ||
138 | { | ||
139 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
140 | struct sport_device *sport = runtime->private_data; | ||
141 | unsigned int diff; | ||
142 | snd_pcm_uframes_t frames; | ||
143 | |||
144 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
145 | diff = sport_curr_offset_tx(sport); | ||
146 | frames = diff / (8*4); /* 32 bytes per frame */ | ||
147 | } else { | ||
148 | diff = sport_curr_offset_rx(sport); | ||
149 | frames = diff / (8*4); | ||
150 | } | ||
151 | return frames; | ||
152 | } | ||
153 | |||
154 | static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | ||
155 | { | ||
156 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | ||
157 | struct snd_soc_dai *cpu_dai = rtd->cpu_dai; | ||
158 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(cpu_dai); | ||
159 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
160 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
161 | |||
162 | int ret = 0; | ||
163 | |||
164 | snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware); | ||
165 | |||
166 | ret = snd_pcm_hw_constraint_integer(runtime, | ||
167 | SNDRV_PCM_HW_PARAM_PERIODS); | ||
168 | if (ret < 0) | ||
169 | goto out; | ||
170 | |||
171 | if (sport_handle != NULL) { | ||
172 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | ||
173 | sport_handle->tx_buf = buf->area; | ||
174 | else | ||
175 | sport_handle->rx_buf = buf->area; | ||
176 | |||
177 | runtime->private_data = sport_handle; | ||
178 | } else { | ||
179 | pr_err("sport_handle is NULL\n"); | ||
180 | ret = -ENODEV; | ||
181 | } | ||
182 | out: | ||
183 | return ret; | ||
184 | } | ||
185 | |||
186 | static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, | ||
187 | snd_pcm_uframes_t pos, void *buf, snd_pcm_uframes_t count) | ||
188 | { | ||
189 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
190 | struct sport_device *sport = runtime->private_data; | ||
191 | struct bf5xx_tdm_port *tdm_port = sport->private_data; | ||
192 | unsigned int *src; | ||
193 | unsigned int *dst; | ||
194 | int i; | ||
195 | |||
196 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
197 | src = buf; | ||
198 | dst = (unsigned int *)substream->runtime->dma_area; | ||
199 | |||
200 | dst += pos * 8; | ||
201 | while (count--) { | ||
202 | for (i = 0; i < substream->runtime->channels; i++) | ||
203 | *(dst + tdm_port->tx_map[i]) = *src++; | ||
204 | dst += 8; | ||
205 | } | ||
206 | } else { | ||
207 | src = (unsigned int *)substream->runtime->dma_area; | ||
208 | dst = buf; | ||
209 | |||
210 | src += pos * 8; | ||
211 | while (count--) { | ||
212 | for (i = 0; i < substream->runtime->channels; i++) | ||
213 | *dst++ = *(src + tdm_port->rx_map[i]); | ||
214 | src += 8; | ||
215 | } | ||
216 | } | ||
217 | |||
218 | return 0; | ||
219 | } | ||
220 | |||
221 | static int bf5xx_pcm_silence(struct snd_pcm_substream *substream, | ||
222 | int channel, snd_pcm_uframes_t pos, snd_pcm_uframes_t count) | ||
223 | { | ||
224 | unsigned char *buf = substream->runtime->dma_area; | ||
225 | buf += pos * 8 * 4; | ||
226 | memset(buf, '\0', count * 8 * 4); | ||
227 | |||
228 | return 0; | ||
229 | } | ||
230 | |||
231 | static struct snd_pcm_ops bf5xx_pcm_tdm_ops = { | ||
232 | .open = bf5xx_pcm_open, | ||
233 | .ioctl = snd_pcm_lib_ioctl, | ||
234 | .hw_params = bf5xx_pcm_hw_params, | ||
235 | .hw_free = bf5xx_pcm_hw_free, | ||
236 | .prepare = bf5xx_pcm_prepare, | ||
237 | .trigger = bf5xx_pcm_trigger, | ||
238 | .pointer = bf5xx_pcm_pointer, | ||
239 | .copy = bf5xx_pcm_copy, | ||
240 | .silence = bf5xx_pcm_silence, | ||
241 | }; | ||
242 | |||
243 | static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | ||
244 | { | ||
245 | struct snd_pcm_substream *substream = pcm->streams[stream].substream; | ||
246 | struct snd_dma_buffer *buf = &substream->dma_buffer; | ||
247 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max; | ||
248 | |||
249 | buf->dev.type = SNDRV_DMA_TYPE_DEV; | ||
250 | buf->dev.dev = pcm->card->dev; | ||
251 | buf->private_data = NULL; | ||
252 | buf->area = dma_alloc_coherent(pcm->card->dev, size * 4, | ||
253 | &buf->addr, GFP_KERNEL); | ||
254 | if (!buf->area) { | ||
255 | pr_err("Failed to allocate dma memory - Please increase uncached DMA memory region\n"); | ||
256 | return -ENOMEM; | ||
257 | } | ||
258 | buf->bytes = size; | ||
259 | |||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | ||
264 | { | ||
265 | struct snd_pcm_substream *substream; | ||
266 | struct snd_dma_buffer *buf; | ||
267 | int stream; | ||
268 | |||
269 | for (stream = 0; stream < 2; stream++) { | ||
270 | substream = pcm->streams[stream].substream; | ||
271 | if (!substream) | ||
272 | continue; | ||
273 | |||
274 | buf = &substream->dma_buffer; | ||
275 | if (!buf->area) | ||
276 | continue; | ||
277 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); | ||
278 | buf->area = NULL; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | static u64 bf5xx_pcm_dmamask = DMA_BIT_MASK(32); | ||
283 | |||
284 | static int bf5xx_pcm_tdm_new(struct snd_soc_pcm_runtime *rtd) | ||
285 | { | ||
286 | struct snd_card *card = rtd->card->snd_card; | ||
287 | struct snd_pcm *pcm = rtd->pcm; | ||
288 | int ret = 0; | ||
289 | |||
290 | if (!card->dev->dma_mask) | ||
291 | card->dev->dma_mask = &bf5xx_pcm_dmamask; | ||
292 | if (!card->dev->coherent_dma_mask) | ||
293 | card->dev->coherent_dma_mask = DMA_BIT_MASK(32); | ||
294 | |||
295 | if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { | ||
296 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
297 | SNDRV_PCM_STREAM_PLAYBACK); | ||
298 | if (ret) | ||
299 | goto out; | ||
300 | } | ||
301 | |||
302 | if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { | ||
303 | ret = bf5xx_pcm_preallocate_dma_buffer(pcm, | ||
304 | SNDRV_PCM_STREAM_CAPTURE); | ||
305 | if (ret) | ||
306 | goto out; | ||
307 | } | ||
308 | out: | ||
309 | return ret; | ||
310 | } | ||
311 | |||
312 | static struct snd_soc_platform_driver bf5xx_tdm_soc_platform = { | ||
313 | .ops = &bf5xx_pcm_tdm_ops, | ||
314 | .pcm_new = bf5xx_pcm_tdm_new, | ||
315 | .pcm_free = bf5xx_pcm_free_dma_buffers, | ||
316 | }; | ||
317 | |||
318 | static int bf5xx_soc_platform_probe(struct platform_device *pdev) | ||
319 | { | ||
320 | return snd_soc_register_platform(&pdev->dev, &bf5xx_tdm_soc_platform); | ||
321 | } | ||
322 | |||
323 | static int bf5xx_soc_platform_remove(struct platform_device *pdev) | ||
324 | { | ||
325 | snd_soc_unregister_platform(&pdev->dev); | ||
326 | return 0; | ||
327 | } | ||
328 | |||
329 | static struct platform_driver bfin_tdm_driver = { | ||
330 | .driver = { | ||
331 | .name = "bfin-tdm-pcm-audio", | ||
332 | .owner = THIS_MODULE, | ||
333 | }, | ||
334 | |||
335 | .probe = bf5xx_soc_platform_probe, | ||
336 | .remove = bf5xx_soc_platform_remove, | ||
337 | }; | ||
338 | |||
339 | module_platform_driver(bfin_tdm_driver); | ||
340 | |||
341 | MODULE_AUTHOR("Barry Song"); | ||
342 | MODULE_DESCRIPTION("ADI Blackfin TDM PCM DMA module"); | ||
343 | MODULE_LICENSE("GPL"); | ||
diff --git a/sound/soc/blackfin/bf5xx-tdm.c b/sound/soc/blackfin/bf5xx-tdm.c deleted file mode 100644 index aa0851650b56..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm.c +++ /dev/null | |||
@@ -1,328 +0,0 @@ | |||
1 | /* | ||
2 | * File: sound/soc/blackfin/bf5xx-tdm.c | ||
3 | * Author: Barry Song <Barry.Song@analog.com> | ||
4 | * | ||
5 | * Created: Thurs June 04 2009 | ||
6 | * Description: Blackfin I2S(TDM) CPU DAI driver | ||
7 | * Even though TDM mode can be as part of I2S DAI, but there | ||
8 | * are so much difference in configuration and data flow, | ||
9 | * it's very ugly to integrate I2S and TDM into a module | ||
10 | * | ||
11 | * Modified: | ||
12 | * Copyright 2009 Analog Devices Inc. | ||
13 | * | ||
14 | * Bugs: Enter bugs at http://blackfin.uclinux.org/ | ||
15 | * | ||
16 | * This program is free software; you can redistribute it and/or modify | ||
17 | * it under the terms of the GNU General Public License as published by | ||
18 | * the Free Software Foundation; either version 2 of the License, or | ||
19 | * (at your option) any later version. | ||
20 | * | ||
21 | * This program is distributed in the hope that it will be useful, | ||
22 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
23 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
24 | * GNU General Public License for more details. | ||
25 | * | ||
26 | * You should have received a copy of the GNU General Public License | ||
27 | * along with this program; if not, see the file COPYING, or write | ||
28 | * to the Free Software Foundation, Inc., | ||
29 | * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | ||
30 | */ | ||
31 | |||
32 | #include <linux/init.h> | ||
33 | #include <linux/module.h> | ||
34 | #include <linux/device.h> | ||
35 | #include <sound/core.h> | ||
36 | #include <sound/pcm.h> | ||
37 | #include <sound/pcm_params.h> | ||
38 | #include <sound/initval.h> | ||
39 | #include <sound/soc.h> | ||
40 | |||
41 | #include <asm/irq.h> | ||
42 | #include <asm/portmux.h> | ||
43 | #include <linux/mutex.h> | ||
44 | #include <linux/gpio.h> | ||
45 | |||
46 | #include "bf5xx-sport.h" | ||
47 | #include "bf5xx-tdm.h" | ||
48 | |||
49 | static int bf5xx_tdm_set_dai_fmt(struct snd_soc_dai *cpu_dai, | ||
50 | unsigned int fmt) | ||
51 | { | ||
52 | int ret = 0; | ||
53 | |||
54 | /* interface format:support TDM,slave mode */ | ||
55 | switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { | ||
56 | case SND_SOC_DAIFMT_DSP_A: | ||
57 | break; | ||
58 | default: | ||
59 | printk(KERN_ERR "%s: Unknown DAI format type\n", __func__); | ||
60 | ret = -EINVAL; | ||
61 | break; | ||
62 | } | ||
63 | |||
64 | switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { | ||
65 | case SND_SOC_DAIFMT_CBM_CFM: | ||
66 | break; | ||
67 | case SND_SOC_DAIFMT_CBS_CFS: | ||
68 | case SND_SOC_DAIFMT_CBM_CFS: | ||
69 | case SND_SOC_DAIFMT_CBS_CFM: | ||
70 | ret = -EINVAL; | ||
71 | break; | ||
72 | default: | ||
73 | printk(KERN_ERR "%s: Unknown DAI master type\n", __func__); | ||
74 | ret = -EINVAL; | ||
75 | break; | ||
76 | } | ||
77 | |||
78 | return ret; | ||
79 | } | ||
80 | |||
81 | static int bf5xx_tdm_hw_params(struct snd_pcm_substream *substream, | ||
82 | struct snd_pcm_hw_params *params, | ||
83 | struct snd_soc_dai *dai) | ||
84 | { | ||
85 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
86 | struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; | ||
87 | int ret = 0; | ||
88 | |||
89 | bf5xx_tdm->tcr2 &= ~0x1f; | ||
90 | bf5xx_tdm->rcr2 &= ~0x1f; | ||
91 | switch (params_format(params)) { | ||
92 | case SNDRV_PCM_FORMAT_S32_LE: | ||
93 | bf5xx_tdm->tcr2 |= 31; | ||
94 | bf5xx_tdm->rcr2 |= 31; | ||
95 | sport_handle->wdsize = 4; | ||
96 | break; | ||
97 | /* at present, we only support 32bit transfer */ | ||
98 | default: | ||
99 | pr_err("not supported PCM format yet\n"); | ||
100 | return -EINVAL; | ||
101 | break; | ||
102 | } | ||
103 | |||
104 | if (!bf5xx_tdm->configured) { | ||
105 | /* | ||
106 | * TX and RX are not independent,they are enabled at the | ||
107 | * same time, even if only one side is running. So, we | ||
108 | * need to configure both of them at the time when the first | ||
109 | * stream is opened. | ||
110 | * | ||
111 | * CPU DAI:slave mode. | ||
112 | */ | ||
113 | ret = sport_config_rx(sport_handle, bf5xx_tdm->rcr1, | ||
114 | bf5xx_tdm->rcr2, 0, 0); | ||
115 | if (ret) { | ||
116 | pr_err("SPORT is busy!\n"); | ||
117 | return -EBUSY; | ||
118 | } | ||
119 | |||
120 | ret = sport_config_tx(sport_handle, bf5xx_tdm->tcr1, | ||
121 | bf5xx_tdm->tcr2, 0, 0); | ||
122 | if (ret) { | ||
123 | pr_err("SPORT is busy!\n"); | ||
124 | return -EBUSY; | ||
125 | } | ||
126 | |||
127 | bf5xx_tdm->configured = 1; | ||
128 | } | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static void bf5xx_tdm_shutdown(struct snd_pcm_substream *substream, | ||
134 | struct snd_soc_dai *dai) | ||
135 | { | ||
136 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
137 | struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; | ||
138 | |||
139 | /* No active stream, SPORT is allowed to be configured again. */ | ||
140 | if (!dai->active) | ||
141 | bf5xx_tdm->configured = 0; | ||
142 | } | ||
143 | |||
144 | static int bf5xx_tdm_set_channel_map(struct snd_soc_dai *dai, | ||
145 | unsigned int tx_num, unsigned int *tx_slot, | ||
146 | unsigned int rx_num, unsigned int *rx_slot) | ||
147 | { | ||
148 | struct sport_device *sport_handle = snd_soc_dai_get_drvdata(dai); | ||
149 | struct bf5xx_tdm_port *bf5xx_tdm = sport_handle->private_data; | ||
150 | int i; | ||
151 | unsigned int slot; | ||
152 | unsigned int tx_mapped = 0, rx_mapped = 0; | ||
153 | |||
154 | if ((tx_num > BFIN_TDM_DAI_MAX_SLOTS) || | ||
155 | (rx_num > BFIN_TDM_DAI_MAX_SLOTS)) | ||
156 | return -EINVAL; | ||
157 | |||
158 | for (i = 0; i < tx_num; i++) { | ||
159 | slot = tx_slot[i]; | ||
160 | if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && | ||
161 | (!(tx_mapped & (1 << slot)))) { | ||
162 | bf5xx_tdm->tx_map[i] = slot; | ||
163 | tx_mapped |= 1 << slot; | ||
164 | } else | ||
165 | return -EINVAL; | ||
166 | } | ||
167 | for (i = 0; i < rx_num; i++) { | ||
168 | slot = rx_slot[i]; | ||
169 | if ((slot < BFIN_TDM_DAI_MAX_SLOTS) && | ||
170 | (!(rx_mapped & (1 << slot)))) { | ||
171 | bf5xx_tdm->rx_map[i] = slot; | ||
172 | rx_mapped |= 1 << slot; | ||
173 | } else | ||
174 | return -EINVAL; | ||
175 | } | ||
176 | |||
177 | return 0; | ||
178 | } | ||
179 | |||
180 | #ifdef CONFIG_PM | ||
181 | static int bf5xx_tdm_suspend(struct snd_soc_dai *dai) | ||
182 | { | ||
183 | struct sport_device *sport = snd_soc_dai_get_drvdata(dai); | ||
184 | |||
185 | if (dai->playback_active) | ||
186 | sport_tx_stop(sport); | ||
187 | if (dai->capture_active) | ||
188 | sport_rx_stop(sport); | ||
189 | |||
190 | /* isolate sync/clock pins from codec while sports resume */ | ||
191 | peripheral_free_list(sport->pin_req); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int bf5xx_tdm_resume(struct snd_soc_dai *dai) | ||
197 | { | ||
198 | int ret; | ||
199 | struct sport_device *sport = snd_soc_dai_get_drvdata(dai); | ||
200 | |||
201 | ret = sport_set_multichannel(sport, 8, 0xFF, 0xFF, 1); | ||
202 | if (ret) { | ||
203 | pr_err("SPORT is busy!\n"); | ||
204 | ret = -EBUSY; | ||
205 | } | ||
206 | |||
207 | ret = sport_config_rx(sport, 0, 0x1F, 0, 0); | ||
208 | if (ret) { | ||
209 | pr_err("SPORT is busy!\n"); | ||
210 | ret = -EBUSY; | ||
211 | } | ||
212 | |||
213 | ret = sport_config_tx(sport, 0, 0x1F, 0, 0); | ||
214 | if (ret) { | ||
215 | pr_err("SPORT is busy!\n"); | ||
216 | ret = -EBUSY; | ||
217 | } | ||
218 | |||
219 | peripheral_request_list(sport->pin_req, "soc-audio"); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | #else | ||
225 | #define bf5xx_tdm_suspend NULL | ||
226 | #define bf5xx_tdm_resume NULL | ||
227 | #endif | ||
228 | |||
229 | static const struct snd_soc_dai_ops bf5xx_tdm_dai_ops = { | ||
230 | .hw_params = bf5xx_tdm_hw_params, | ||
231 | .set_fmt = bf5xx_tdm_set_dai_fmt, | ||
232 | .shutdown = bf5xx_tdm_shutdown, | ||
233 | .set_channel_map = bf5xx_tdm_set_channel_map, | ||
234 | }; | ||
235 | |||
236 | static struct snd_soc_dai_driver bf5xx_tdm_dai = { | ||
237 | .suspend = bf5xx_tdm_suspend, | ||
238 | .resume = bf5xx_tdm_resume, | ||
239 | .playback = { | ||
240 | .channels_min = 2, | ||
241 | .channels_max = 8, | ||
242 | .rates = SNDRV_PCM_RATE_48000, | ||
243 | .formats = SNDRV_PCM_FMTBIT_S32_LE,}, | ||
244 | .capture = { | ||
245 | .channels_min = 2, | ||
246 | .channels_max = 8, | ||
247 | .rates = SNDRV_PCM_RATE_48000, | ||
248 | .formats = SNDRV_PCM_FMTBIT_S32_LE,}, | ||
249 | .ops = &bf5xx_tdm_dai_ops, | ||
250 | }; | ||
251 | |||
252 | static const struct snd_soc_component_driver bf5xx_tdm_component = { | ||
253 | .name = "bf5xx-tdm", | ||
254 | }; | ||
255 | |||
256 | static int bfin_tdm_probe(struct platform_device *pdev) | ||
257 | { | ||
258 | struct sport_device *sport_handle; | ||
259 | int ret; | ||
260 | |||
261 | /* configure SPORT for TDM */ | ||
262 | sport_handle = sport_init(pdev, 4, 8 * sizeof(u32), | ||
263 | sizeof(struct bf5xx_tdm_port)); | ||
264 | if (!sport_handle) | ||
265 | return -ENODEV; | ||
266 | |||
267 | /* SPORT works in TDM mode */ | ||
268 | ret = sport_set_multichannel(sport_handle, 8, 0xFF, 0xFF, 1); | ||
269 | if (ret) { | ||
270 | pr_err("SPORT is busy!\n"); | ||
271 | ret = -EBUSY; | ||
272 | goto sport_config_err; | ||
273 | } | ||
274 | |||
275 | ret = sport_config_rx(sport_handle, 0, 0x1F, 0, 0); | ||
276 | if (ret) { | ||
277 | pr_err("SPORT is busy!\n"); | ||
278 | ret = -EBUSY; | ||
279 | goto sport_config_err; | ||
280 | } | ||
281 | |||
282 | ret = sport_config_tx(sport_handle, 0, 0x1F, 0, 0); | ||
283 | if (ret) { | ||
284 | pr_err("SPORT is busy!\n"); | ||
285 | ret = -EBUSY; | ||
286 | goto sport_config_err; | ||
287 | } | ||
288 | |||
289 | ret = snd_soc_register_component(&pdev->dev, &bf5xx_tdm_component, | ||
290 | &bf5xx_tdm_dai, 1); | ||
291 | if (ret) { | ||
292 | pr_err("Failed to register DAI: %d\n", ret); | ||
293 | goto sport_config_err; | ||
294 | } | ||
295 | |||
296 | return 0; | ||
297 | |||
298 | sport_config_err: | ||
299 | sport_done(sport_handle); | ||
300 | return ret; | ||
301 | } | ||
302 | |||
303 | static int bfin_tdm_remove(struct platform_device *pdev) | ||
304 | { | ||
305 | struct sport_device *sport_handle = platform_get_drvdata(pdev); | ||
306 | |||
307 | snd_soc_unregister_component(&pdev->dev); | ||
308 | sport_done(sport_handle); | ||
309 | |||
310 | return 0; | ||
311 | } | ||
312 | |||
313 | static struct platform_driver bfin_tdm_driver = { | ||
314 | .probe = bfin_tdm_probe, | ||
315 | .remove = bfin_tdm_remove, | ||
316 | .driver = { | ||
317 | .name = "bfin-tdm", | ||
318 | .owner = THIS_MODULE, | ||
319 | }, | ||
320 | }; | ||
321 | |||
322 | module_platform_driver(bfin_tdm_driver); | ||
323 | |||
324 | /* Module information */ | ||
325 | MODULE_AUTHOR("Barry Song"); | ||
326 | MODULE_DESCRIPTION("TDM driver for ADI Blackfin"); | ||
327 | MODULE_LICENSE("GPL"); | ||
328 | |||
diff --git a/sound/soc/blackfin/bf5xx-tdm.h b/sound/soc/blackfin/bf5xx-tdm.h deleted file mode 100644 index e986a3ea3315..000000000000 --- a/sound/soc/blackfin/bf5xx-tdm.h +++ /dev/null | |||
@@ -1,23 +0,0 @@ | |||
1 | /* | ||
2 | * sound/soc/blackfin/bf5xx-tdm.h | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #ifndef _BF5XX_TDM_H | ||
10 | #define _BF5XX_TDM_H | ||
11 | |||
12 | #define BFIN_TDM_DAI_MAX_SLOTS 8 | ||
13 | struct bf5xx_tdm_port { | ||
14 | u16 tcr1; | ||
15 | u16 rcr1; | ||
16 | u16 tcr2; | ||
17 | u16 rcr2; | ||
18 | unsigned int tx_map[BFIN_TDM_DAI_MAX_SLOTS]; | ||
19 | unsigned int rx_map[BFIN_TDM_DAI_MAX_SLOTS]; | ||
20 | int configured; | ||
21 | }; | ||
22 | |||
23 | #endif | ||