aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/blackfin
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/blackfin')
-rw-r--r--sound/soc/blackfin/Kconfig101
-rw-r--r--sound/soc/blackfin/Makefile21
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c457
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.h29
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c406
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.h36
-rw-r--r--sound/soc/blackfin/bf5xx-ad1980.c113
-rw-r--r--sound/soc/blackfin/bf5xx-ad73311.c240
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.c288
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h29
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c311
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.h14
-rw-r--r--sound/soc/blackfin/bf5xx-sport.c1032
-rw-r--r--sound/soc/blackfin/bf5xx-sport.h194
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c186
15 files changed, 3457 insertions, 0 deletions
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
new file mode 100644
index 000000000000..dc006206f622
--- /dev/null
+++ b/sound/soc/blackfin/Kconfig
@@ -0,0 +1,101 @@
1config SND_BF5XX_I2S
2 tristate "SoC I2S Audio for the ADI BF5xx chip"
3 depends on BLACKFIN && SND_SOC
4 help
5 Say Y or M if you want to add support for codecs attached to
6 the Blackfin SPORT (synchronous serial ports) interface in I2S
7 mode (supports single stereo In/Out).
8 You will also need to select the audio interfaces to support below.
9
10config SND_BF5XX_SOC_SSM2602
11 tristate "SoC SSM2602 Audio support for BF52x ezkit"
12 depends on SND_BF5XX_I2S
13 select SND_BF5XX_SOC_I2S
14 select SND_SOC_SSM2602
15 select I2C
16 select I2C_BLACKFIN_TWI
17 help
18 Say Y if you want to add support for SoC audio on BF527-EZKIT.
19
20config SND_BF5XX_SOC_AD73311
21 tristate "SoC AD73311 Audio support for Blackfin"
22 depends on SND_BF5XX_I2S
23 select SND_BF5XX_SOC_I2S
24 select SND_SOC_AD73311
25 help
26 Say Y if you want to add support for AD73311 codec on Blackfin.
27
28config SND_BFIN_AD73311_SE
29 int "PF pin for AD73311L Chip Select"
30 depends on SND_BF5XX_SOC_AD73311
31 default 4
32 help
33 Enter the GPIO used to control AD73311's SE pin. Acceptable
34 values are 0 to 7
35
36config SND_BF5XX_AC97
37 tristate "SoC AC97 Audio for the ADI BF5xx chip"
38 depends on BLACKFIN && SND_SOC
39 help
40 Say Y or M if you want to add support for codecs attached to
41 the Blackfin SPORT (synchronous serial ports) interface in slot 16
42 mode (pseudo AC97 interface).
43 You will also need to select the audio interfaces to support below.
44
45 Note:
46 AC97 codecs which do not implment the slot-16 mode will not function
47 properly with this driver. This driver is known to work with the
48 Analog Devices line of AC97 codecs.
49
50config SND_MMAP_SUPPORT
51 bool "Enable MMAP Support"
52 depends on SND_BF5XX_AC97
53 default y
54 help
55 Say y if you want AC97 driver to support mmap mode.
56 We introduce an intermediate buffer to simulate mmap.
57
58config SND_BF5XX_SOC_SPORT
59 tristate
60
61config SND_BF5XX_SOC_I2S
62 tristate
63 select SND_BF5XX_SOC_SPORT
64
65config SND_BF5XX_SOC_AC97
66 tristate
67 select AC97_BUS
68 select SND_SOC_AC97_BUS
69 select SND_BF5XX_SOC_SPORT
70
71config SND_BF5XX_SOC_AD1980
72 tristate "SoC AD1980/1 Audio support for BF5xx"
73 depends on SND_BF5XX_AC97
74 select SND_BF5XX_SOC_AC97
75 select SND_SOC_AD1980
76 help
77 Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
78
79config SND_BF5XX_SPORT_NUM
80 int "Set a SPORT for Sound chip"
81 depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
82 range 0 3 if BF54x
83 range 0 1 if (BF53x || BF561)
84 default 0
85 help
86 Set the correct SPORT for sound chip.
87
88config SND_BF5XX_HAVE_COLD_RESET
89 bool "BOARD has COLD Reset GPIO"
90 depends on SND_BF5XX_AC97
91 default y if BFIN548_EZKIT
92 default n if !BFIN548_EZKIT
93
94config SND_BF5XX_RESET_GPIO_NUM
95 int "Set a GPIO for cold reset"
96 depends on SND_BF5XX_HAVE_COLD_RESET
97 range 0 159
98 default 19 if BFIN548_EZKIT
99 default 5 if BFIN537_STAMP
100 help
101 Set the correct GPIO for RESET the sound chip.
diff --git a/sound/soc/blackfin/Makefile b/sound/soc/blackfin/Makefile
new file mode 100644
index 000000000000..97bb37a6359c
--- /dev/null
+++ b/sound/soc/blackfin/Makefile
@@ -0,0 +1,21 @@
1# Blackfin Platform Support
2snd-bf5xx-ac97-objs := bf5xx-ac97-pcm.o
3snd-bf5xx-i2s-objs := bf5xx-i2s-pcm.o
4snd-soc-bf5xx-sport-objs := bf5xx-sport.o
5snd-soc-bf5xx-ac97-objs := bf5xx-ac97.o
6snd-soc-bf5xx-i2s-objs := bf5xx-i2s.o
7
8obj-$(CONFIG_SND_BF5XX_AC97) += snd-bf5xx-ac97.o
9obj-$(CONFIG_SND_BF5XX_I2S) += snd-bf5xx-i2s.o
10obj-$(CONFIG_SND_BF5XX_SOC_SPORT) += snd-soc-bf5xx-sport.o
11obj-$(CONFIG_SND_BF5XX_SOC_AC97) += snd-soc-bf5xx-ac97.o
12obj-$(CONFIG_SND_BF5XX_SOC_I2S) += snd-soc-bf5xx-i2s.o
13
14# Blackfin Machine Support
15snd-ad1980-objs := bf5xx-ad1980.o
16snd-ssm2602-objs := bf5xx-ssm2602.o
17snd-ad73311-objs := bf5xx-ad73311.o
18
19obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
20obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.o
21obj-$(CONFIG_SND_BF5XX_SOC_AD73311) += snd-ad73311.o
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c
new file mode 100644
index 000000000000..25e50d2ea1ec
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -0,0 +1,457 @@
1/*
2 * File: sound/soc/blackfin/bf5xx-ac97-pcm.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Tue June 06 2008
6 * Description: DMA Driver for AC97 sound chip
7 *
8 * Modified:
9 * Copyright 2008 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/slab.h>
33#include <linux/dma-mapping.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-ac97-pcm.h"
43#include "bf5xx-ac97.h"
44#include "bf5xx-sport.h"
45
46#if defined(CONFIG_SND_MMAP_SUPPORT)
47static void bf5xx_mmap_copy(struct snd_pcm_substream *substream,
48 snd_pcm_uframes_t count)
49{
50 struct snd_pcm_runtime *runtime = substream->runtime;
51 struct sport_device *sport = runtime->private_data;
52 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
53 bf5xx_pcm_to_ac97(
54 (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos,
55 (__u32 *)runtime->dma_area + sport->tx_pos, count);
56 sport->tx_pos += runtime->period_size;
57 if (sport->tx_pos >= runtime->buffer_size)
58 sport->tx_pos %= runtime->buffer_size;
59 sport->tx_delay_pos = sport->tx_pos;
60 } else {
61 bf5xx_ac97_to_pcm(
62 (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
63 (__u32 *)runtime->dma_area + sport->rx_pos, count);
64 sport->rx_pos += runtime->period_size;
65 if (sport->rx_pos >= runtime->buffer_size)
66 sport->rx_pos %= runtime->buffer_size;
67 }
68}
69#endif
70
71static void bf5xx_dma_irq(void *data)
72{
73 struct snd_pcm_substream *pcm = data;
74#if defined(CONFIG_SND_MMAP_SUPPORT)
75 struct snd_pcm_runtime *runtime = pcm->runtime;
76 struct sport_device *sport = runtime->private_data;
77 bf5xx_mmap_copy(pcm, runtime->period_size);
78 if (pcm->stream == SNDRV_PCM_STREAM_PLAYBACK) {
79 if (sport->once == 0) {
80 snd_pcm_period_elapsed(pcm);
81 bf5xx_mmap_copy(pcm, runtime->period_size);
82 sport->once = 1;
83 }
84 }
85#endif
86 snd_pcm_period_elapsed(pcm);
87}
88
89/* The memory size for pure pcm data is 128*1024 = 0x20000 bytes.
90 * The total rx/tx buffer is for ac97 frame to hold all pcm data
91 * is 0x20000 * sizeof(struct ac97_frame) / 4.
92 */
93#ifdef CONFIG_SND_MMAP_SUPPORT
94static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
95 .info = SNDRV_PCM_INFO_INTERLEAVED |
96 SNDRV_PCM_INFO_MMAP |
97 SNDRV_PCM_INFO_MMAP_VALID |
98 SNDRV_PCM_INFO_BLOCK_TRANSFER,
99#else
100static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
101 .info = SNDRV_PCM_INFO_INTERLEAVED |
102 SNDRV_PCM_INFO_BLOCK_TRANSFER,
103#endif
104 .formats = SNDRV_PCM_FMTBIT_S16_LE,
105 .period_bytes_min = 32,
106 .period_bytes_max = 0x10000,
107 .periods_min = 1,
108 .periods_max = PAGE_SIZE/32,
109 .buffer_bytes_max = 0x20000, /* 128 kbytes */
110 .fifo_size = 16,
111};
112
113static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
114 struct snd_pcm_hw_params *params)
115{
116 size_t size = bf5xx_pcm_hardware.buffer_bytes_max
117 * sizeof(struct ac97_frame) / 4;
118
119 snd_pcm_lib_malloc_pages(substream, size);
120
121 return 0;
122}
123
124static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
125{
126 struct snd_pcm_runtime *runtime = substream->runtime;
127
128 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
129 memset(runtime->dma_area, 0, runtime->buffer_size);
130 snd_pcm_lib_free_pages(substream);
131 return 0;
132}
133
134static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
135{
136 struct snd_pcm_runtime *runtime = substream->runtime;
137 struct sport_device *sport = runtime->private_data;
138
139 /* An intermediate buffer is introduced for implementing mmap for
140 * SPORT working in TMD mode(include AC97).
141 */
142#if defined(CONFIG_SND_MMAP_SUPPORT)
143 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
144 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
145 sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
146 runtime->period_size * sizeof(struct ac97_frame));
147 } else {
148 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
149 sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
150 runtime->period_size * sizeof(struct ac97_frame));
151 }
152#else
153 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
154 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
155 sport_config_tx_dma(sport, runtime->dma_area, runtime->periods,
156 runtime->period_size * sizeof(struct ac97_frame));
157 } else {
158 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
159 sport_config_rx_dma(sport, runtime->dma_area, runtime->periods,
160 runtime->period_size * sizeof(struct ac97_frame));
161 }
162#endif
163 return 0;
164}
165
166static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
167{
168 struct snd_pcm_runtime *runtime = substream->runtime;
169 struct sport_device *sport = runtime->private_data;
170 int ret = 0;
171
172 pr_debug("%s enter\n", __func__);
173 switch (cmd) {
174 case SNDRV_PCM_TRIGGER_START:
175 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
176 bf5xx_mmap_copy(substream, runtime->period_size);
177 snd_pcm_period_elapsed(substream);
178 sport->tx_delay_pos = 0;
179 sport_tx_start(sport);
180 }
181 else
182 sport_rx_start(sport);
183 break;
184 case SNDRV_PCM_TRIGGER_STOP:
185 case SNDRV_PCM_TRIGGER_SUSPEND:
186 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
187 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
188#if defined(CONFIG_SND_MMAP_SUPPORT)
189 sport->tx_pos = 0;
190#endif
191 sport_tx_stop(sport);
192 } else {
193#if defined(CONFIG_SND_MMAP_SUPPORT)
194 sport->rx_pos = 0;
195#endif
196 sport_rx_stop(sport);
197 }
198 break;
199 default:
200 ret = -EINVAL;
201 }
202 return ret;
203}
204
205static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
206{
207 struct snd_pcm_runtime *runtime = substream->runtime;
208 struct sport_device *sport = runtime->private_data;
209 unsigned int curr;
210
211#if defined(CONFIG_SND_MMAP_SUPPORT)
212 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
213 curr = sport->tx_delay_pos;
214 else
215 curr = sport->rx_pos;
216#else
217
218 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
219 curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame);
220 else
221 curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame);
222
223#endif
224 return curr;
225}
226
227static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
228{
229 struct snd_pcm_runtime *runtime = substream->runtime;
230 int ret;
231
232 pr_debug("%s enter\n", __func__);
233 snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
234
235 ret = snd_pcm_hw_constraint_integer(runtime,
236 SNDRV_PCM_HW_PARAM_PERIODS);
237 if (ret < 0)
238 goto out;
239
240 if (sport_handle != NULL)
241 runtime->private_data = sport_handle;
242 else {
243 pr_err("sport_handle is NULL\n");
244 return -1;
245 }
246 return 0;
247
248 out:
249 return ret;
250}
251
252static int bf5xx_pcm_close(struct snd_pcm_substream *substream)
253{
254 struct snd_pcm_runtime *runtime = substream->runtime;
255 struct sport_device *sport = runtime->private_data;
256
257 pr_debug("%s enter\n", __func__);
258 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
259 sport->once = 0;
260 memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
261 } else
262 memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame));
263
264 return 0;
265}
266
267#ifdef CONFIG_SND_MMAP_SUPPORT
268static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
269 struct vm_area_struct *vma)
270{
271 struct snd_pcm_runtime *runtime = substream->runtime;
272 size_t size = vma->vm_end - vma->vm_start;
273 vma->vm_start = (unsigned long)runtime->dma_area;
274 vma->vm_end = vma->vm_start + size;
275 vma->vm_flags |= VM_SHARED;
276 return 0 ;
277}
278#else
279static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
280 snd_pcm_uframes_t pos,
281 void __user *buf, snd_pcm_uframes_t count)
282{
283 struct snd_pcm_runtime *runtime = substream->runtime;
284
285 pr_debug("%s copy pos:0x%lx count:0x%lx\n",
286 substream->stream ? "Capture" : "Playback", pos, count);
287
288 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
289 bf5xx_pcm_to_ac97(
290 (struct ac97_frame *)runtime->dma_area + pos,
291 buf, count);
292 else
293 bf5xx_ac97_to_pcm(
294 (struct ac97_frame *)runtime->dma_area + pos,
295 buf, count);
296 return 0;
297}
298#endif
299
300struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
301 .open = bf5xx_pcm_open,
302 .close = bf5xx_pcm_close,
303 .ioctl = snd_pcm_lib_ioctl,
304 .hw_params = bf5xx_pcm_hw_params,
305 .hw_free = bf5xx_pcm_hw_free,
306 .prepare = bf5xx_pcm_prepare,
307 .trigger = bf5xx_pcm_trigger,
308 .pointer = bf5xx_pcm_pointer,
309#ifdef CONFIG_SND_MMAP_SUPPORT
310 .mmap = bf5xx_pcm_mmap,
311#else
312 .copy = bf5xx_pcm_copy,
313#endif
314};
315
316static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
317{
318 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
319 struct snd_dma_buffer *buf = &substream->dma_buffer;
320 size_t size = bf5xx_pcm_hardware.buffer_bytes_max
321 * sizeof(struct ac97_frame) / 4;
322
323 buf->dev.type = SNDRV_DMA_TYPE_DEV;
324 buf->dev.dev = pcm->card->dev;
325 buf->private_data = NULL;
326 buf->area = dma_alloc_coherent(pcm->card->dev, size,
327 &buf->addr, GFP_KERNEL);
328 if (!buf->area) {
329 pr_err("Failed to allocate dma memory\n");
330 pr_err("Please increase uncached DMA memory region\n");
331 return -ENOMEM;
332 }
333 buf->bytes = size;
334
335 pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
336 buf->area, buf->bytes);
337
338 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
339 sport_handle->tx_buf = buf->area;
340 else
341 sport_handle->rx_buf = buf->area;
342
343/*
344 * Need to allocate local buffer when enable
345 * MMAP for SPORT working in TMD mode (include AC97).
346 */
347#if defined(CONFIG_SND_MMAP_SUPPORT)
348 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
349 if (!sport_handle->tx_dma_buf) {
350 sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
351 size, &sport_handle->tx_dma_phy, GFP_KERNEL);
352 if (!sport_handle->tx_dma_buf) {
353 pr_err("Failed to allocate memory for tx dma \
354 buf - Please increase uncached DMA \
355 memory region\n");
356 return -ENOMEM;
357 } else
358 memset(sport_handle->tx_dma_buf, 0, size);
359 } else
360 memset(sport_handle->tx_dma_buf, 0, size);
361 } else {
362 if (!sport_handle->rx_dma_buf) {
363 sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
364 size, &sport_handle->rx_dma_phy, GFP_KERNEL);
365 if (!sport_handle->rx_dma_buf) {
366 pr_err("Failed to allocate memory for rx dma \
367 buf - Please increase uncached DMA \
368 memory region\n");
369 return -ENOMEM;
370 } else
371 memset(sport_handle->rx_dma_buf, 0, size);
372 } else
373 memset(sport_handle->rx_dma_buf, 0, size);
374 }
375#endif
376 return 0;
377}
378
379static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
380{
381 struct snd_pcm_substream *substream;
382 struct snd_dma_buffer *buf;
383 int stream;
384#if defined(CONFIG_SND_MMAP_SUPPORT)
385 size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
386 sizeof(struct ac97_frame) / 4;
387#endif
388 for (stream = 0; stream < 2; stream++) {
389 substream = pcm->streams[stream].substream;
390 if (!substream)
391 continue;
392
393 buf = &substream->dma_buffer;
394 if (!buf->area)
395 continue;
396 dma_free_coherent(NULL, buf->bytes, buf->area, 0);
397 buf->area = NULL;
398#if defined(CONFIG_SND_MMAP_SUPPORT)
399 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
400 if (sport_handle->tx_dma_buf)
401 dma_free_coherent(NULL, size, \
402 sport_handle->tx_dma_buf, 0);
403 sport_handle->tx_dma_buf = NULL;
404 } else {
405
406 if (sport_handle->rx_dma_buf)
407 dma_free_coherent(NULL, size, \
408 sport_handle->rx_dma_buf, 0);
409 sport_handle->rx_dma_buf = NULL;
410 }
411#endif
412 }
413 if (sport_handle)
414 sport_done(sport_handle);
415}
416
417static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
418
419int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
420 struct snd_pcm *pcm)
421{
422 int ret = 0;
423
424 pr_debug("%s enter\n", __func__);
425 if (!card->dev->dma_mask)
426 card->dev->dma_mask = &bf5xx_pcm_dmamask;
427 if (!card->dev->coherent_dma_mask)
428 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
429
430 if (dai->playback.channels_min) {
431 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
432 SNDRV_PCM_STREAM_PLAYBACK);
433 if (ret)
434 goto out;
435 }
436
437 if (dai->capture.channels_min) {
438 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
439 SNDRV_PCM_STREAM_CAPTURE);
440 if (ret)
441 goto out;
442 }
443 out:
444 return ret;
445}
446
447struct snd_soc_platform bf5xx_ac97_soc_platform = {
448 .name = "bf5xx-audio",
449 .pcm_ops = &bf5xx_pcm_ac97_ops,
450 .pcm_new = bf5xx_pcm_ac97_new,
451 .pcm_free = bf5xx_pcm_free_dma_buffers,
452};
453EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
454
455MODULE_AUTHOR("Cliff Cai");
456MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
457MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.h b/sound/soc/blackfin/bf5xx-ac97-pcm.h
new file mode 100644
index 000000000000..350125a0ae21
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.h
@@ -0,0 +1,29 @@
1/*
2 * linux/sound/arm/bf5xx-ac97-pcm.h -- ALSA PCM interface for the Blackfin
3 *
4 * Copyright 2007 Analog Device Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#ifndef _BF5XX_AC97_PCM_H
12#define _BF5XX_AC97_PCM_H
13
14struct bf5xx_pcm_dma_params {
15 char *name; /* stream identifier */
16};
17
18struct bf5xx_gpio {
19 u32 sys;
20 u32 rx;
21 u32 tx;
22 u32 clk;
23 u32 frm;
24};
25
26/* platform data */
27extern struct snd_soc_platform bf5xx_ac97_soc_platform;
28
29#endif
diff --git a/sound/soc/blackfin/bf5xx-ac97.c b/sound/soc/blackfin/bf5xx-ac97.c
new file mode 100644
index 000000000000..5e5aafb6485f
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -0,0 +1,406 @@
1/*
2 * bf5xx-ac97.c -- AC97 support for the ADI blackfin chip.
3 *
4 * Author: Roy Huang
5 * Created: 11th. June 2007
6 * Copyright: Analog Device Inc.
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
11 */
12
13#include <linux/init.h>
14#include <linux/module.h>
15#include <linux/platform_device.h>
16#include <linux/interrupt.h>
17#include <linux/wait.h>
18#include <linux/delay.h>
19
20#include <sound/core.h>
21#include <sound/pcm.h>
22#include <sound/ac97_codec.h>
23#include <sound/initval.h>
24#include <sound/soc.h>
25
26#include <asm/irq.h>
27#include <asm/portmux.h>
28#include <linux/mutex.h>
29#include <linux/gpio.h>
30
31#include "bf5xx-sport.h"
32#include "bf5xx-ac97.h"
33
34#if defined(CONFIG_BF54x)
35#define PIN_REQ_SPORT_0 {P_SPORT0_TFS, P_SPORT0_DTPRI, P_SPORT0_TSCLK, \
36 P_SPORT0_RFS, P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
37
38#define PIN_REQ_SPORT_1 {P_SPORT1_TFS, P_SPORT1_DTPRI, P_SPORT1_TSCLK, \
39 P_SPORT1_RFS, P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
40
41#define PIN_REQ_SPORT_2 {P_SPORT2_TFS, P_SPORT2_DTPRI, P_SPORT2_TSCLK, \
42 P_SPORT2_RFS, P_SPORT2_DRPRI, P_SPORT2_RSCLK, 0}
43
44#define PIN_REQ_SPORT_3 {P_SPORT3_TFS, P_SPORT3_DTPRI, P_SPORT3_TSCLK, \
45 P_SPORT3_RFS, P_SPORT3_DRPRI, P_SPORT3_RSCLK, 0}
46#else
47#define PIN_REQ_SPORT_0 {P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS, \
48 P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0}
49
50#define PIN_REQ_SPORT_1 {P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS, \
51 P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0}
52#endif
53
54static int *cmd_count;
55static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
56
57#if defined(CONFIG_BF54x)
58static struct sport_param sport_params[4] = {
59 {
60 .dma_rx_chan = CH_SPORT0_RX,
61 .dma_tx_chan = CH_SPORT0_TX,
62 .err_irq = IRQ_SPORT0_ERR,
63 .regs = (struct sport_register *)SPORT0_TCR1,
64 },
65 {
66 .dma_rx_chan = CH_SPORT1_RX,
67 .dma_tx_chan = CH_SPORT1_TX,
68 .err_irq = IRQ_SPORT1_ERR,
69 .regs = (struct sport_register *)SPORT1_TCR1,
70 },
71 {
72 .dma_rx_chan = CH_SPORT2_RX,
73 .dma_tx_chan = CH_SPORT2_TX,
74 .err_irq = IRQ_SPORT2_ERR,
75 .regs = (struct sport_register *)SPORT2_TCR1,
76 },
77 {
78 .dma_rx_chan = CH_SPORT3_RX,
79 .dma_tx_chan = CH_SPORT3_TX,
80 .err_irq = IRQ_SPORT3_ERR,
81 .regs = (struct sport_register *)SPORT3_TCR1,
82 }
83};
84#else
85static struct sport_param sport_params[2] = {
86 {
87 .dma_rx_chan = CH_SPORT0_RX,
88 .dma_tx_chan = CH_SPORT0_TX,
89 .err_irq = IRQ_SPORT0_ERROR,
90 .regs = (struct sport_register *)SPORT0_TCR1,
91 },
92 {
93 .dma_rx_chan = CH_SPORT1_RX,
94 .dma_tx_chan = CH_SPORT1_TX,
95 .err_irq = IRQ_SPORT1_ERROR,
96 .regs = (struct sport_register *)SPORT1_TCR1,
97 }
98};
99#endif
100
101void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
102 size_t count)
103{
104 while (count--) {
105 dst->ac97_tag = TAG_VALID | TAG_PCM;
106 (dst++)->ac97_pcm = *src++;
107 }
108}
109EXPORT_SYMBOL(bf5xx_pcm_to_ac97);
110
111void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
112 size_t count)
113{
114 while (count--)
115 *(dst++) = (src++)->ac97_pcm;
116}
117EXPORT_SYMBOL(bf5xx_ac97_to_pcm);
118
119static unsigned int sport_tx_curr_frag(struct sport_device *sport)
120{
121 return sport->tx_curr_frag = sport_curr_offset_tx(sport) / \
122 sport->tx_fragsize;
123}
124
125static void enqueue_cmd(struct snd_ac97 *ac97, __u16 addr, __u16 data)
126{
127 struct sport_device *sport = sport_handle;
128 int nextfrag = sport_tx_curr_frag(sport);
129 struct ac97_frame *nextwrite;
130
131 sport_incfrag(sport, &nextfrag, 1);
132
133 nextwrite = (struct ac97_frame *)(sport->tx_buf + \
134 nextfrag * sport->tx_fragsize);
135 pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
136 sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
137 nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
138 nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
139 nextwrite[cmd_count[nextfrag]].ac97_data = data;
140 ++cmd_count[nextfrag];
141 pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
142 addr >> 8, data, nextfrag);
143}
144
145static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
146 unsigned short reg)
147{
148 struct ac97_frame out_frame[2], in_frame[2];
149
150 pr_debug("%s enter 0x%x\n", __func__, reg);
151
152 /* When dma descriptor is enabled, the register should not be read */
153 if (sport_handle->tx_run || sport_handle->rx_run) {
154 pr_err("Could you send a mail to cliff.cai@analog.com "
155 "to report this?\n");
156 return -EFAULT;
157 }
158
159 memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
160 memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
161 out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
162 out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
163 sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
164 (unsigned char *)&in_frame,
165 2 * sizeof(struct ac97_frame));
166 return in_frame[1].ac97_data;
167}
168
169void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
170 unsigned short val)
171{
172 pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
173
174 if (sport_handle->tx_run) {
175 enqueue_cmd(ac97, (reg << 8), val); /* write */
176 enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
177 } else {
178 struct ac97_frame frame;
179 memset(&frame, 0, sizeof(struct ac97_frame));
180 frame.ac97_tag = TAG_VALID | TAG_CMD;
181 frame.ac97_addr = (reg << 8);
182 frame.ac97_data = val;
183 sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
184 NULL, sizeof(struct ac97_frame));
185 }
186}
187
188static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
189{
190#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
191 (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
192
193#define CONCAT(a, b, c) a ## b ## c
194#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
195
196 u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
197 u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
198
199 pr_debug("%s enter\n", __func__);
200
201 peripheral_free(per);
202 gpio_request(gpio, "bf5xx-ac97");
203 gpio_direction_output(gpio, 1);
204 udelay(2);
205 gpio_set_value(gpio, 0);
206 udelay(1);
207 gpio_free(gpio);
208 peripheral_request(per, "soc-audio");
209#else
210 pr_info("%s: Not implemented\n", __func__);
211#endif
212}
213
214static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
215{
216#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
217 pr_debug("%s enter\n", __func__);
218
219 /* It is specified for bf548-ezkit */
220 gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
221 /* Keep reset pin low for 1 ms */
222 mdelay(1);
223 gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
224 /* Wait for bit clock recover */
225 mdelay(1);
226#else
227 pr_info("%s: Not implemented\n", __func__);
228#endif
229}
230
231struct snd_ac97_bus_ops soc_ac97_ops = {
232 .read = bf5xx_ac97_read,
233 .write = bf5xx_ac97_write,
234 .warm_reset = bf5xx_ac97_warm_reset,
235 .reset = bf5xx_ac97_cold_reset,
236};
237EXPORT_SYMBOL_GPL(soc_ac97_ops);
238
239#ifdef CONFIG_PM
240static int bf5xx_ac97_suspend(struct platform_device *pdev,
241 struct snd_soc_dai *dai)
242{
243 struct sport_device *sport =
244 (struct sport_device *)dai->private_data;
245
246 pr_debug("%s : sport %d\n", __func__, dai->id);
247 if (!dai->active)
248 return 0;
249 if (dai->capture.active)
250 sport_rx_stop(sport);
251 if (dai->playback.active)
252 sport_tx_stop(sport);
253 return 0;
254}
255
256static int bf5xx_ac97_resume(struct platform_device *pdev,
257 struct snd_soc_dai *dai)
258{
259 int ret;
260 struct sport_device *sport =
261 (struct sport_device *)dai->private_data;
262
263 pr_debug("%s : sport %d\n", __func__, dai->id);
264 if (!dai->active)
265 return 0;
266
267 ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
268 if (ret) {
269 pr_err("SPORT is busy!\n");
270 return -EBUSY;
271 }
272
273 ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
274 if (ret) {
275 pr_err("SPORT is busy!\n");
276 return -EBUSY;
277 }
278
279 ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
280 if (ret) {
281 pr_err("SPORT is busy!\n");
282 return -EBUSY;
283 }
284
285 if (dai->capture.active)
286 sport_rx_start(sport);
287 if (dai->playback.active)
288 sport_tx_start(sport);
289 return 0;
290}
291
292#else
293#define bf5xx_ac97_suspend NULL
294#define bf5xx_ac97_resume NULL
295#endif
296
297static int bf5xx_ac97_probe(struct platform_device *pdev,
298 struct snd_soc_dai *dai)
299{
300 int ret;
301#if defined(CONFIG_BF54x)
302 u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
303 PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
304#else
305 u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
306#endif
307 cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
308 if (cmd_count == NULL)
309 return -ENOMEM;
310
311 if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
312 pr_err("Requesting Peripherals failed\n");
313 return -EFAULT;
314 }
315
316#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
317 /* Request PB3 as reset pin */
318 if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
319 pr_err("Failed to request GPIO_%d for reset\n",
320 CONFIG_SND_BF5XX_RESET_GPIO_NUM);
321 peripheral_free_list(&sport_req[sport_num][0]);
322 return -1;
323 }
324 gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
325#endif
326 sport_handle = sport_init(&sport_params[sport_num], 2, \
327 sizeof(struct ac97_frame), NULL);
328 if (!sport_handle) {
329 peripheral_free_list(&sport_req[sport_num][0]);
330#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
331 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
332#endif
333 return -ENODEV;
334 }
335 /*SPORT works in TDM mode to simulate AC97 transfers*/
336 ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
337 if (ret) {
338 pr_err("SPORT is busy!\n");
339 kfree(sport_handle);
340 peripheral_free_list(&sport_req[sport_num][0]);
341#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
342 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
343#endif
344 return -EBUSY;
345 }
346
347 ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
348 if (ret) {
349 pr_err("SPORT is busy!\n");
350 kfree(sport_handle);
351 peripheral_free_list(&sport_req[sport_num][0]);
352#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
353 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
354#endif
355 return -EBUSY;
356 }
357
358 ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
359 if (ret) {
360 pr_err("SPORT is busy!\n");
361 kfree(sport_handle);
362 peripheral_free_list(&sport_req[sport_num][0]);
363#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
364 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
365#endif
366 return -EBUSY;
367 }
368 return 0;
369}
370
371static void bf5xx_ac97_remove(struct platform_device *pdev,
372 struct snd_soc_dai *dai)
373{
374 free_page((unsigned long)cmd_count);
375 cmd_count = NULL;
376#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
377 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
378#endif
379}
380
381struct snd_soc_dai bfin_ac97_dai = {
382 .name = "bf5xx-ac97",
383 .id = 0,
384 .type = SND_SOC_DAI_AC97,
385 .probe = bf5xx_ac97_probe,
386 .remove = bf5xx_ac97_remove,
387 .suspend = bf5xx_ac97_suspend,
388 .resume = bf5xx_ac97_resume,
389 .playback = {
390 .stream_name = "AC97 Playback",
391 .channels_min = 2,
392 .channels_max = 2,
393 .rates = SNDRV_PCM_RATE_48000,
394 .formats = SNDRV_PCM_FMTBIT_S16_LE, },
395 .capture = {
396 .stream_name = "AC97 Capture",
397 .channels_min = 2,
398 .channels_max = 2,
399 .rates = SNDRV_PCM_RATE_48000,
400 .formats = SNDRV_PCM_FMTBIT_S16_LE, },
401};
402EXPORT_SYMBOL_GPL(bfin_ac97_dai);
403
404MODULE_AUTHOR("Roy Huang");
405MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
406MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ac97.h b/sound/soc/blackfin/bf5xx-ac97.h
new file mode 100644
index 000000000000..3f77cc558dc0
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.h
@@ -0,0 +1,36 @@
1/*
2 * linux/sound/arm/bf5xx-ac97.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_AC97_H
10#define _BF5XX_AC97_H
11
12extern struct snd_ac97_bus_ops bf5xx_ac97_ops;
13extern struct snd_ac97 *ac97;
14/* Frame format in memory, only support stereo currently */
15struct ac97_frame {
16 u16 ac97_tag; /* slot 0 */
17 u16 ac97_addr; /* slot 1 */
18 u16 ac97_data; /* slot 2 */
19 u32 ac97_pcm; /* slot 3 and 4: left and right pcm data */
20} __attribute__ ((packed));
21
22#define TAG_VALID 0x8000
23#define TAG_CMD 0x6000
24#define TAG_PCM_LEFT 0x1000
25#define TAG_PCM_RIGHT 0x0800
26#define TAG_PCM (TAG_PCM_LEFT | TAG_PCM_RIGHT)
27
28extern struct snd_soc_dai bfin_ac97_dai;
29
30void bf5xx_pcm_to_ac97(struct ac97_frame *dst, const __u32 *src, \
31 size_t count);
32
33void bf5xx_ac97_to_pcm(const struct ac97_frame *src, __u32 *dst, \
34 size_t count);
35
36#endif
diff --git a/sound/soc/blackfin/bf5xx-ad1980.c b/sound/soc/blackfin/bf5xx-ad1980.c
new file mode 100644
index 000000000000..124425d22320
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad1980.c
@@ -0,0 +1,113 @@
1/*
2 * File: sound/soc/blackfin/bf5xx-ad1980.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Tue June 06 2008
6 * Description: Board driver for AD1980/1 audio codec
7 *
8 * Modified:
9 * Copyright 2008 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/moduleparam.h>
31#include <linux/device.h>
32#include <asm/dma.h>
33
34#include <sound/core.h>
35#include <sound/pcm.h>
36#include <sound/soc.h>
37
38#include <linux/gpio.h>
39#include <asm/portmux.h>
40
41#include "../codecs/ad1980.h"
42#include "bf5xx-sport.h"
43#include "bf5xx-ac97-pcm.h"
44#include "bf5xx-ac97.h"
45
46static struct snd_soc_machine bf5xx_board;
47
48static int bf5xx_board_startup(struct snd_pcm_substream *substream)
49{
50 struct snd_soc_pcm_runtime *rtd = substream->private_data;
51 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
52
53 pr_debug("%s enter\n", __func__);
54 cpu_dai->private_data = sport_handle;
55 return 0;
56}
57
58static struct snd_soc_ops bf5xx_board_ops = {
59 .startup = bf5xx_board_startup,
60};
61
62static struct snd_soc_dai_link bf5xx_board_dai = {
63 .name = "AC97",
64 .stream_name = "AC97 HiFi",
65 .cpu_dai = &bfin_ac97_dai,
66 .codec_dai = &ad1980_dai,
67 .ops = &bf5xx_board_ops,
68};
69
70static struct snd_soc_machine bf5xx_board = {
71 .name = "bf5xx-board",
72 .dai_link = &bf5xx_board_dai,
73 .num_links = 1,
74};
75
76static struct snd_soc_device bf5xx_board_snd_devdata = {
77 .machine = &bf5xx_board,
78 .platform = &bf5xx_ac97_soc_platform,
79 .codec_dev = &soc_codec_dev_ad1980,
80};
81
82static struct platform_device *bf5xx_board_snd_device;
83
84static int __init bf5xx_board_init(void)
85{
86 int ret;
87
88 bf5xx_board_snd_device = platform_device_alloc("soc-audio", -1);
89 if (!bf5xx_board_snd_device)
90 return -ENOMEM;
91
92 platform_set_drvdata(bf5xx_board_snd_device, &bf5xx_board_snd_devdata);
93 bf5xx_board_snd_devdata.dev = &bf5xx_board_snd_device->dev;
94 ret = platform_device_add(bf5xx_board_snd_device);
95
96 if (ret)
97 platform_device_put(bf5xx_board_snd_device);
98
99 return ret;
100}
101
102static void __exit bf5xx_board_exit(void)
103{
104 platform_device_unregister(bf5xx_board_snd_device);
105}
106
107module_init(bf5xx_board_init);
108module_exit(bf5xx_board_exit);
109
110/* Module information */
111MODULE_AUTHOR("Cliff Cai");
112MODULE_DESCRIPTION("ALSA SoC AD1980/1 BF5xx board");
113MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-ad73311.c b/sound/soc/blackfin/bf5xx-ad73311.c
new file mode 100644
index 000000000000..622c9b909532
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ad73311.c
@@ -0,0 +1,240 @@
1/*
2 * File: sound/soc/blackfin/bf5xx-ad73311.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Thur Sep 25 2008
6 * Description: Board driver for ad73311 sound chip
7 *
8 * Modified:
9 * Copyright 2008 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/moduleparam.h>
31#include <linux/device.h>
32#include <linux/delay.h>
33#include <linux/gpio.h>
34
35#include <sound/core.h>
36#include <sound/pcm.h>
37#include <sound/soc.h>
38#include <sound/soc-dapm.h>
39#include <sound/pcm_params.h>
40
41#include <asm/blackfin.h>
42#include <asm/cacheflush.h>
43#include <asm/irq.h>
44#include <asm/dma.h>
45#include <asm/portmux.h>
46
47#include "../codecs/ad73311.h"
48#include "bf5xx-sport.h"
49#include "bf5xx-i2s-pcm.h"
50#include "bf5xx-i2s.h"
51
52#if CONFIG_SND_BF5XX_SPORT_NUM == 0
53#define bfin_write_SPORT_TCR1 bfin_write_SPORT0_TCR1
54#define bfin_read_SPORT_TCR1 bfin_read_SPORT0_TCR1
55#define bfin_write_SPORT_TCR2 bfin_write_SPORT0_TCR2
56#define bfin_write_SPORT_TX16 bfin_write_SPORT0_TX16
57#define bfin_read_SPORT_STAT bfin_read_SPORT0_STAT
58#else
59#define bfin_write_SPORT_TCR1 bfin_write_SPORT1_TCR1
60#define bfin_read_SPORT_TCR1 bfin_read_SPORT1_TCR1
61#define bfin_write_SPORT_TCR2 bfin_write_SPORT1_TCR2
62#define bfin_write_SPORT_TX16 bfin_write_SPORT1_TX16
63#define bfin_read_SPORT_STAT bfin_read_SPORT1_STAT
64#endif
65
66#define GPIO_SE CONFIG_SND_BFIN_AD73311_SE
67
68static struct snd_soc_machine bf5xx_ad73311;
69
70static int snd_ad73311_startup(void)
71{
72 pr_debug("%s enter\n", __func__);
73
74 /* Pull up SE pin on AD73311L */
75 gpio_set_value(GPIO_SE, 1);
76 return 0;
77}
78
79static int snd_ad73311_configure(void)
80{
81 unsigned short ctrl_regs[6];
82 unsigned short status = 0;
83 int count = 0;
84
85 /* DMCLK = MCLK = 16.384 MHz
86 * SCLK = DMCLK/8 = 2.048 MHz
87 * Sample Rate = DMCLK/2048 = 8 KHz
88 */
89 ctrl_regs[0] = AD_CONTROL | AD_WRITE | CTRL_REG_B | REGB_MCDIV(0) | \
90 REGB_SCDIV(0) | REGB_DIRATE(0);
91 ctrl_regs[1] = AD_CONTROL | AD_WRITE | CTRL_REG_C | REGC_PUDEV | \
92 REGC_PUADC | REGC_PUDAC | REGC_PUREF | REGC_REFUSE ;
93 ctrl_regs[2] = AD_CONTROL | AD_WRITE | CTRL_REG_D | REGD_OGS(2) | \
94 REGD_IGS(2);
95 ctrl_regs[3] = AD_CONTROL | AD_WRITE | CTRL_REG_E | REGE_DA(0x1f);
96 ctrl_regs[4] = AD_CONTROL | AD_WRITE | CTRL_REG_F | REGF_SEEN ;
97 ctrl_regs[5] = AD_CONTROL | AD_WRITE | CTRL_REG_A | REGA_MODE_DATA;
98
99 local_irq_disable();
100 snd_ad73311_startup();
101 udelay(1);
102
103 bfin_write_SPORT_TCR1(TFSR);
104 bfin_write_SPORT_TCR2(0xF);
105 SSYNC();
106
107 /* SPORT Tx Register is a 8 x 16 FIFO, all the data can be put to
108 * FIFO before enable SPORT to transfer the data
109 */
110 for (count = 0; count < 6; count++)
111 bfin_write_SPORT_TX16(ctrl_regs[count]);
112 SSYNC();
113 bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() | TSPEN);
114 SSYNC();
115
116 /* When TUVF is set, the data is already send out */
117 while (!(status & TUVF) && count++ < 10000) {
118 udelay(1);
119 status = bfin_read_SPORT_STAT();
120 SSYNC();
121 }
122 bfin_write_SPORT_TCR1(bfin_read_SPORT_TCR1() & ~TSPEN);
123 SSYNC();
124 local_irq_enable();
125
126 if (count == 10000) {
127 printk(KERN_ERR "ad73311: failed to configure codec\n");
128 return -1;
129 }
130 return 0;
131}
132
133static int bf5xx_probe(struct platform_device *pdev)
134{
135 int err;
136 if (gpio_request(GPIO_SE, "AD73311_SE")) {
137 printk(KERN_ERR "%s: Failed ro request GPIO_%d\n", __func__, GPIO_SE);
138 return -EBUSY;
139 }
140
141 gpio_direction_output(GPIO_SE, 0);
142
143 err = snd_ad73311_configure();
144 if (err < 0)
145 return -EFAULT;
146
147 return 0;
148}
149
150static int bf5xx_ad73311_startup(struct snd_pcm_substream *substream)
151{
152 struct snd_soc_pcm_runtime *rtd = substream->private_data;
153 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
154
155 pr_debug("%s enter\n", __func__);
156 cpu_dai->private_data = sport_handle;
157 return 0;
158}
159
160static int bf5xx_ad73311_hw_params(struct snd_pcm_substream *substream,
161 struct snd_pcm_hw_params *params)
162{
163 struct snd_soc_pcm_runtime *rtd = substream->private_data;
164 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
165 int ret = 0;
166
167 pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
168 params_format(params));
169
170 /* set cpu DAI configuration */
171 ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A |
172 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
173 if (ret < 0)
174 return ret;
175
176 return 0;
177}
178
179
180static struct snd_soc_ops bf5xx_ad73311_ops = {
181 .startup = bf5xx_ad73311_startup,
182 .hw_params = bf5xx_ad73311_hw_params,
183};
184
185static struct snd_soc_dai_link bf5xx_ad73311_dai = {
186 .name = "ad73311",
187 .stream_name = "AD73311",
188 .cpu_dai = &bf5xx_i2s_dai,
189 .codec_dai = &ad73311_dai,
190 .ops = &bf5xx_ad73311_ops,
191};
192
193static struct snd_soc_machine bf5xx_ad73311 = {
194 .name = "bf5xx_ad73311",
195 .probe = bf5xx_probe,
196 .dai_link = &bf5xx_ad73311_dai,
197 .num_links = 1,
198};
199
200static struct snd_soc_device bf5xx_ad73311_snd_devdata = {
201 .machine = &bf5xx_ad73311,
202 .platform = &bf5xx_i2s_soc_platform,
203 .codec_dev = &soc_codec_dev_ad73311,
204};
205
206static struct platform_device *bf52x_ad73311_snd_device;
207
208static int __init bf5xx_ad73311_init(void)
209{
210 int ret;
211
212 pr_debug("%s enter\n", __func__);
213 bf52x_ad73311_snd_device = platform_device_alloc("soc-audio", -1);
214 if (!bf52x_ad73311_snd_device)
215 return -ENOMEM;
216
217 platform_set_drvdata(bf52x_ad73311_snd_device, &bf5xx_ad73311_snd_devdata);
218 bf5xx_ad73311_snd_devdata.dev = &bf52x_ad73311_snd_device->dev;
219 ret = platform_device_add(bf52x_ad73311_snd_device);
220
221 if (ret)
222 platform_device_put(bf52x_ad73311_snd_device);
223
224 return ret;
225}
226
227static void __exit bf5xx_ad73311_exit(void)
228{
229 pr_debug("%s enter\n", __func__);
230 platform_device_unregister(bf52x_ad73311_snd_device);
231}
232
233module_init(bf5xx_ad73311_init);
234module_exit(bf5xx_ad73311_exit);
235
236/* Module information */
237MODULE_AUTHOR("Cliff Cai");
238MODULE_DESCRIPTION("ALSA SoC AD73311 Blackfin");
239MODULE_LICENSE("GPL");
240
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.c b/sound/soc/blackfin/bf5xx-i2s-pcm.c
new file mode 100644
index 000000000000..61fccf925192
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.c
@@ -0,0 +1,288 @@
1/*
2 * File: sound/soc/blackfin/bf5xx-i2s-pcm.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Tue June 06 2008
6 * Description: DMA driver for i2s codec
7 *
8 * Modified:
9 * Copyright 2008 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/slab.h>
33#include <linux/dma-mapping.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-i2s-pcm.h"
43#include "bf5xx-i2s.h"
44#include "bf5xx-sport.h"
45
46static void bf5xx_dma_irq(void *data)
47{
48 struct snd_pcm_substream *pcm = data;
49 snd_pcm_period_elapsed(pcm);
50}
51
52static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
53 .info = SNDRV_PCM_INFO_INTERLEAVED |
54 SNDRV_PCM_INFO_MMAP |
55 SNDRV_PCM_INFO_MMAP_VALID |
56 SNDRV_PCM_INFO_BLOCK_TRANSFER,
57 .formats = SNDRV_PCM_FMTBIT_S16_LE |
58 SNDRV_PCM_FMTBIT_S24_LE |
59 SNDRV_PCM_FMTBIT_S32_LE,
60 .period_bytes_min = 32,
61 .period_bytes_max = 0x10000,
62 .periods_min = 1,
63 .periods_max = PAGE_SIZE/32,
64 .buffer_bytes_max = 0x20000, /* 128 kbytes */
65 .fifo_size = 16,
66};
67
68static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
69 struct snd_pcm_hw_params *params)
70{
71 size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
72 snd_pcm_lib_malloc_pages(substream, size);
73
74 return 0;
75}
76
77static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
78{
79 snd_pcm_lib_free_pages(substream);
80
81 return 0;
82}
83
84static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
85{
86 struct snd_pcm_runtime *runtime = substream->runtime;
87 struct sport_device *sport = runtime->private_data;
88 int period_bytes = frames_to_bytes(runtime, runtime->period_size);
89
90 pr_debug("%s enter\n", __func__);
91 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
92 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
93 sport_config_tx_dma(sport, runtime->dma_area,
94 runtime->periods, period_bytes);
95 } else {
96 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
97 sport_config_rx_dma(sport, runtime->dma_area,
98 runtime->periods, period_bytes);
99 }
100
101 return 0;
102}
103
104static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
105{
106 struct snd_pcm_runtime *runtime = substream->runtime;
107 struct sport_device *sport = runtime->private_data;
108 int ret = 0;
109
110 pr_debug("%s enter\n", __func__);
111 switch (cmd) {
112 case SNDRV_PCM_TRIGGER_START:
113 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
114 sport_tx_start(sport);
115 else
116 sport_rx_start(sport);
117 break;
118 case SNDRV_PCM_TRIGGER_STOP:
119 case SNDRV_PCM_TRIGGER_SUSPEND:
120 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
121 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
122 sport_tx_stop(sport);
123 else
124 sport_rx_stop(sport);
125 break;
126 default:
127 ret = -EINVAL;
128 }
129
130 return ret;
131}
132
133static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
134{
135 struct snd_pcm_runtime *runtime = substream->runtime;
136 struct sport_device *sport = runtime->private_data;
137 unsigned int diff;
138 snd_pcm_uframes_t frames;
139 pr_debug("%s enter\n", __func__);
140 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
141 diff = sport_curr_offset_tx(sport);
142 frames = bytes_to_frames(substream->runtime, diff);
143 } else {
144 diff = sport_curr_offset_rx(sport);
145 frames = bytes_to_frames(substream->runtime, diff);
146 }
147 return frames;
148}
149
150static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
151{
152 struct snd_pcm_runtime *runtime = substream->runtime;
153 int ret;
154
155 pr_debug("%s enter\n", __func__);
156 snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
157
158 ret = snd_pcm_hw_constraint_integer(runtime, \
159 SNDRV_PCM_HW_PARAM_PERIODS);
160 if (ret < 0)
161 goto out;
162
163 if (sport_handle != NULL)
164 runtime->private_data = sport_handle;
165 else {
166 pr_err("sport_handle is NULL\n");
167 return -1;
168 }
169 return 0;
170
171 out:
172 return ret;
173}
174
175static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
176 struct vm_area_struct *vma)
177{
178 struct snd_pcm_runtime *runtime = substream->runtime;
179 size_t size = vma->vm_end - vma->vm_start;
180 vma->vm_start = (unsigned long)runtime->dma_area;
181 vma->vm_end = vma->vm_start + size;
182 vma->vm_flags |= VM_SHARED;
183
184 return 0 ;
185}
186
187struct snd_pcm_ops bf5xx_pcm_i2s_ops = {
188 .open = bf5xx_pcm_open,
189 .ioctl = snd_pcm_lib_ioctl,
190 .hw_params = bf5xx_pcm_hw_params,
191 .hw_free = bf5xx_pcm_hw_free,
192 .prepare = bf5xx_pcm_prepare,
193 .trigger = bf5xx_pcm_trigger,
194 .pointer = bf5xx_pcm_pointer,
195 .mmap = bf5xx_pcm_mmap,
196};
197
198static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
199{
200 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
201 struct snd_dma_buffer *buf = &substream->dma_buffer;
202 size_t size = bf5xx_pcm_hardware.buffer_bytes_max;
203
204 buf->dev.type = SNDRV_DMA_TYPE_DEV;
205 buf->dev.dev = pcm->card->dev;
206 buf->private_data = NULL;
207 buf->area = dma_alloc_coherent(pcm->card->dev, size,
208 &buf->addr, GFP_KERNEL);
209 if (!buf->area) {
210 pr_err("Failed to allocate dma memory \
211 Please increase uncached DMA memory region\n");
212 return -ENOMEM;
213 }
214 buf->bytes = size;
215
216 pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
217 buf->area, buf->bytes);
218
219 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
220 sport_handle->tx_buf = buf->area;
221 else
222 sport_handle->rx_buf = buf->area;
223
224 return 0;
225}
226
227static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
228{
229 struct snd_pcm_substream *substream;
230 struct snd_dma_buffer *buf;
231 int stream;
232
233 for (stream = 0; stream < 2; stream++) {
234 substream = pcm->streams[stream].substream;
235 if (!substream)
236 continue;
237
238 buf = &substream->dma_buffer;
239 if (!buf->area)
240 continue;
241 dma_free_coherent(NULL, buf->bytes, buf->area, 0);
242 buf->area = NULL;
243 }
244 if (sport_handle)
245 sport_done(sport_handle);
246}
247
248static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
249
250int bf5xx_pcm_i2s_new(struct snd_card *card, struct snd_soc_dai *dai,
251 struct snd_pcm *pcm)
252{
253 int ret = 0;
254
255 pr_debug("%s enter\n", __func__);
256 if (!card->dev->dma_mask)
257 card->dev->dma_mask = &bf5xx_pcm_dmamask;
258 if (!card->dev->coherent_dma_mask)
259 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
260
261 if (dai->playback.channels_min) {
262 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
263 SNDRV_PCM_STREAM_PLAYBACK);
264 if (ret)
265 goto out;
266 }
267
268 if (dai->capture.channels_min) {
269 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
270 SNDRV_PCM_STREAM_CAPTURE);
271 if (ret)
272 goto out;
273 }
274 out:
275 return ret;
276}
277
278struct snd_soc_platform bf5xx_i2s_soc_platform = {
279 .name = "bf5xx-audio",
280 .pcm_ops = &bf5xx_pcm_i2s_ops,
281 .pcm_new = bf5xx_pcm_i2s_new,
282 .pcm_free = bf5xx_pcm_free_dma_buffers,
283};
284EXPORT_SYMBOL_GPL(bf5xx_i2s_soc_platform);
285
286MODULE_AUTHOR("Cliff Cai");
287MODULE_DESCRIPTION("ADI Blackfin I2S PCM DMA module");
288MODULE_LICENSE("GPL");
diff --git a/sound/soc/blackfin/bf5xx-i2s-pcm.h b/sound/soc/blackfin/bf5xx-i2s-pcm.h
new file mode 100644
index 000000000000..4d4609a97c59
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s-pcm.h
@@ -0,0 +1,29 @@
1/*
2 * linux/sound/arm/bf5xx-i2s-pcm.h -- ALSA PCM interface for the Blackfin
3 *
4 * Copyright 2007 Analog Device Inc.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#ifndef _BF5XX_I2S_PCM_H
12#define _BF5XX_I2S_PCM_H
13
14struct bf5xx_pcm_dma_params {
15 char *name; /* stream identifier */
16};
17
18struct bf5xx_gpio {
19 u32 sys;
20 u32 rx;
21 u32 tx;
22 u32 clk;
23 u32 frm;
24};
25
26/* platform data */
27extern struct snd_soc_platform bf5xx_i2s_soc_platform;
28
29#endif
diff --git a/sound/soc/blackfin/bf5xx-i2s.c b/sound/soc/blackfin/bf5xx-i2s.c
new file mode 100644
index 000000000000..827587f08180
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -0,0 +1,311 @@
1/*
2 * File: sound/soc/blackfin/bf5xx-i2s.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Tue June 06 2008
6 * Description: Blackfin I2S CPU DAI driver
7 *
8 * Modified:
9 * Copyright 2008 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/init.h>
30#include <linux/module.h>
31#include <linux/device.h>
32#include <linux/delay.h>
33#include <sound/core.h>
34#include <sound/pcm.h>
35#include <sound/pcm_params.h>
36#include <sound/initval.h>
37#include <sound/soc.h>
38
39#include <asm/irq.h>
40#include <asm/portmux.h>
41#include <linux/mutex.h>
42#include <linux/gpio.h>
43
44#include "bf5xx-sport.h"
45#include "bf5xx-i2s.h"
46
47struct bf5xx_i2s_port {
48 u16 tcr1;
49 u16 rcr1;
50 u16 tcr2;
51 u16 rcr2;
52 int counter;
53};
54
55static struct bf5xx_i2s_port bf5xx_i2s;
56static int sport_num = CONFIG_SND_BF5XX_SPORT_NUM;
57
58static struct sport_param sport_params[2] = {
59 {
60 .dma_rx_chan = CH_SPORT0_RX,
61 .dma_tx_chan = CH_SPORT0_TX,
62 .err_irq = IRQ_SPORT0_ERROR,
63 .regs = (struct sport_register *)SPORT0_TCR1,
64 },
65 {
66 .dma_rx_chan = CH_SPORT1_RX,
67 .dma_tx_chan = CH_SPORT1_TX,
68 .err_irq = IRQ_SPORT1_ERROR,
69 .regs = (struct sport_register *)SPORT1_TCR1,
70 }
71};
72
73static u16 sport_req[][7] = {
74 { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
75 P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0},
76 { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
77 P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0},
78};
79
80static int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
81 unsigned int fmt)
82{
83 int ret = 0;
84
85 /* interface format:support I2S,slave mode */
86 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
87 case SND_SOC_DAIFMT_I2S:
88 bf5xx_i2s.tcr1 |= TFSR | TCKFE;
89 bf5xx_i2s.rcr1 |= RFSR | RCKFE;
90 bf5xx_i2s.tcr2 |= TSFSE;
91 bf5xx_i2s.rcr2 |= RSFSE;
92 break;
93 case SND_SOC_DAIFMT_DSP_A:
94 bf5xx_i2s.tcr1 |= TFSR;
95 bf5xx_i2s.rcr1 |= RFSR;
96 break;
97 case SND_SOC_DAIFMT_LEFT_J:
98 ret = -EINVAL;
99 break;
100 default:
101 ret = -EINVAL;
102 break;
103 }
104
105 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
106 case SND_SOC_DAIFMT_CBS_CFS:
107 ret = -EINVAL;
108 break;
109 case SND_SOC_DAIFMT_CBM_CFS:
110 ret = -EINVAL;
111 break;
112 case SND_SOC_DAIFMT_CBM_CFM:
113 break;
114 case SND_SOC_DAIFMT_CBS_CFM:
115 ret = -EINVAL;
116 break;
117 default:
118 ret = -EINVAL;
119 break;
120 }
121
122 return ret;
123}
124
125static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
126{
127 pr_debug("%s enter\n", __func__);
128
129 /*this counter is used for counting how many pcm streams are opened*/
130 bf5xx_i2s.counter++;
131 return 0;
132}
133
134static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
135 struct snd_pcm_hw_params *params)
136{
137 int ret = 0;
138
139 bf5xx_i2s.tcr2 &= ~0x1f;
140 bf5xx_i2s.rcr2 &= ~0x1f;
141 switch (params_format(params)) {
142 case SNDRV_PCM_FORMAT_S16_LE:
143 bf5xx_i2s.tcr2 |= 15;
144 bf5xx_i2s.rcr2 |= 15;
145 sport_handle->wdsize = 2;
146 break;
147 case SNDRV_PCM_FORMAT_S24_LE:
148 bf5xx_i2s.tcr2 |= 23;
149 bf5xx_i2s.rcr2 |= 23;
150 sport_handle->wdsize = 3;
151 break;
152 case SNDRV_PCM_FORMAT_S32_LE:
153 bf5xx_i2s.tcr2 |= 31;
154 bf5xx_i2s.rcr2 |= 31;
155 sport_handle->wdsize = 4;
156 break;
157 }
158
159 if (bf5xx_i2s.counter == 1) {
160 /*
161 * TX and RX are not independent,they are enabled at the
162 * same time, even if only one side is running. So, we
163 * need to configure both of them at the time when the first
164 * stream is opened.
165 *
166 * CPU DAI:slave mode.
167 */
168 ret = sport_config_rx(sport_handle, bf5xx_i2s.rcr1,
169 bf5xx_i2s.rcr2, 0, 0);
170 if (ret) {
171 pr_err("SPORT is busy!\n");
172 return -EBUSY;
173 }
174
175 ret = sport_config_tx(sport_handle, bf5xx_i2s.tcr1,
176 bf5xx_i2s.tcr2, 0, 0);
177 if (ret) {
178 pr_err("SPORT is busy!\n");
179 return -EBUSY;
180 }
181 }
182
183 return 0;
184}
185
186static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
187{
188 pr_debug("%s enter\n", __func__);
189 bf5xx_i2s.counter--;
190}
191
192static int bf5xx_i2s_probe(struct platform_device *pdev,
193 struct snd_soc_dai *dai)
194{
195 pr_debug("%s enter\n", __func__);
196 if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
197 pr_err("Requesting Peripherals failed\n");
198 return -EFAULT;
199 }
200
201 /* request DMA for SPORT */
202 sport_handle = sport_init(&sport_params[sport_num], 4, \
203 2 * sizeof(u32), NULL);
204 if (!sport_handle) {
205 peripheral_free_list(&sport_req[sport_num][0]);
206 return -ENODEV;
207 }
208
209 return 0;
210}
211
212static void bf5xx_i2s_remove(struct platform_device *pdev,
213 struct snd_soc_dai *dai)
214{
215 pr_debug("%s enter\n", __func__);
216 peripheral_free_list(&sport_req[sport_num][0]);
217}
218
219#ifdef CONFIG_PM
220static int bf5xx_i2s_suspend(struct platform_device *dev,
221 struct snd_soc_dai *dai)
222{
223 struct sport_device *sport =
224 (struct sport_device *)dai->private_data;
225
226 pr_debug("%s : sport %d\n", __func__, dai->id);
227 if (!dai->active)
228 return 0;
229 if (dai->capture.active)
230 sport_rx_stop(sport);
231 if (dai->playback.active)
232 sport_tx_stop(sport);
233 return 0;
234}
235
236static int bf5xx_i2s_resume(struct platform_device *pdev,
237 struct snd_soc_dai *dai)
238{
239 int ret;
240 struct sport_device *sport =
241 (struct sport_device *)dai->private_data;
242
243 pr_debug("%s : sport %d\n", __func__, dai->id);
244 if (!dai->active)
245 return 0;
246
247 ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
248 if (ret) {
249 pr_err("SPORT is busy!\n");
250 return -EBUSY;
251 }
252
253 ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
254 if (ret) {
255 pr_err("SPORT is busy!\n");
256 return -EBUSY;
257 }
258
259 if (dai->capture.active)
260 sport_rx_start(sport);
261 if (dai->playback.active)
262 sport_tx_start(sport);
263 return 0;
264}
265
266#else
267#define bf5xx_i2s_suspend NULL
268#define bf5xx_i2s_resume NULL
269#endif
270
271#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
272 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
273 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
274 SNDRV_PCM_RATE_96000)
275
276#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
277 SNDRV_PCM_FMTBIT_S32_LE)
278
279struct snd_soc_dai bf5xx_i2s_dai = {
280 .name = "bf5xx-i2s",
281 .id = 0,
282 .type = SND_SOC_DAI_I2S,
283 .probe = bf5xx_i2s_probe,
284 .remove = bf5xx_i2s_remove,
285 .suspend = bf5xx_i2s_suspend,
286 .resume = bf5xx_i2s_resume,
287 .playback = {
288 .channels_min = 1,
289 .channels_max = 2,
290 .rates = BF5XX_I2S_RATES,
291 .formats = BF5XX_I2S_FORMATS,},
292 .capture = {
293 .channels_min = 1,
294 .channels_max = 2,
295 .rates = BF5XX_I2S_RATES,
296 .formats = BF5XX_I2S_FORMATS,},
297 .ops = {
298 .startup = bf5xx_i2s_startup,
299 .shutdown = bf5xx_i2s_shutdown,
300 .hw_params = bf5xx_i2s_hw_params,},
301 .dai_ops = {
302 .set_fmt = bf5xx_i2s_set_dai_fmt,
303 },
304};
305EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
306
307/* Module information */
308MODULE_AUTHOR("Cliff Cai");
309MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
310MODULE_LICENSE("GPL");
311
diff --git a/sound/soc/blackfin/bf5xx-i2s.h b/sound/soc/blackfin/bf5xx-i2s.h
new file mode 100644
index 000000000000..7107d1a0b06b
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.h
@@ -0,0 +1,14 @@
1/*
2 * linux/sound/arm/bf5xx-i2s.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_I2S_H
10#define _BF5XX_I2S_H
11
12extern struct snd_soc_dai bf5xx_i2s_dai;
13
14#endif
diff --git a/sound/soc/blackfin/bf5xx-sport.c b/sound/soc/blackfin/bf5xx-sport.c
new file mode 100644
index 000000000000..3b99e484d555
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.c
@@ -0,0 +1,1032 @@
1/*
2 * File: bf5xx_sport.c
3 * Based on:
4 * Author: Roy Huang <roy.huang@analog.com>
5 *
6 * Created: Tue Sep 21 10:52:42 CEST 2004
7 * Description:
8 * Blackfin SPORT Driver
9 *
10 * Copyright 2004-2007 Analog Devices Inc.
11 *
12 * Bugs: Enter bugs at http://blackfin.uclinux.org/
13 *
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, see the file COPYING, or write
26 * to the Free Software Foundation, Inc.,
27 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 */
29
30#include <linux/kernel.h>
31#include <linux/slab.h>
32#include <linux/delay.h>
33#include <linux/dma-mapping.h>
34#include <linux/gpio.h>
35#include <linux/bug.h>
36#include <asm/portmux.h>
37#include <asm/dma.h>
38#include <asm/blackfin.h>
39#include <asm/cacheflush.h>
40
41#include "bf5xx-sport.h"
42/* delay between frame sync pulse and first data bit in multichannel mode */
43#define FRAME_DELAY (1<<12)
44
45struct sport_device *sport_handle;
46EXPORT_SYMBOL(sport_handle);
47/* note: multichannel is in units of 8 channels,
48 * tdm_count is # channels NOT / 8 ! */
49int sport_set_multichannel(struct sport_device *sport,
50 int tdm_count, u32 mask, int packed)
51{
52 pr_debug("%s tdm_count=%d mask:0x%08x packed=%d\n", __func__,
53 tdm_count, mask, packed);
54
55 if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
56 return -EBUSY;
57
58 if (tdm_count & 0x7)
59 return -EINVAL;
60
61 if (tdm_count > 32)
62 return -EINVAL; /* Only support less than 32 channels now */
63
64 if (tdm_count) {
65 sport->regs->mcmc1 = ((tdm_count>>3)-1) << 12;
66 sport->regs->mcmc2 = FRAME_DELAY | MCMEN | \
67 (packed ? (MCDTXPE|MCDRXPE) : 0);
68
69 sport->regs->mtcs0 = mask;
70 sport->regs->mrcs0 = mask;
71 sport->regs->mtcs1 = 0;
72 sport->regs->mrcs1 = 0;
73 sport->regs->mtcs2 = 0;
74 sport->regs->mrcs2 = 0;
75 sport->regs->mtcs3 = 0;
76 sport->regs->mrcs3 = 0;
77 } else {
78 sport->regs->mcmc1 = 0;
79 sport->regs->mcmc2 = 0;
80
81 sport->regs->mtcs0 = 0;
82 sport->regs->mrcs0 = 0;
83 }
84
85 sport->regs->mtcs1 = 0; sport->regs->mtcs2 = 0; sport->regs->mtcs3 = 0;
86 sport->regs->mrcs1 = 0; sport->regs->mrcs2 = 0; sport->regs->mrcs3 = 0;
87
88 SSYNC();
89
90 return 0;
91}
92EXPORT_SYMBOL(sport_set_multichannel);
93
94int sport_config_rx(struct sport_device *sport, unsigned int rcr1,
95 unsigned int rcr2, unsigned int clkdiv, unsigned int fsdiv)
96{
97 if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
98 return -EBUSY;
99
100 sport->regs->rcr1 = rcr1;
101 sport->regs->rcr2 = rcr2;
102 sport->regs->rclkdiv = clkdiv;
103 sport->regs->rfsdiv = fsdiv;
104
105 SSYNC();
106
107 return 0;
108}
109EXPORT_SYMBOL(sport_config_rx);
110
111int sport_config_tx(struct sport_device *sport, unsigned int tcr1,
112 unsigned int tcr2, unsigned int clkdiv, unsigned int fsdiv)
113{
114 if ((sport->regs->tcr1 & TSPEN) || (sport->regs->rcr1 & RSPEN))
115 return -EBUSY;
116
117 sport->regs->tcr1 = tcr1;
118 sport->regs->tcr2 = tcr2;
119 sport->regs->tclkdiv = clkdiv;
120 sport->regs->tfsdiv = fsdiv;
121
122 SSYNC();
123
124 return 0;
125}
126EXPORT_SYMBOL(sport_config_tx);
127
128static void setup_desc(struct dmasg *desc, void *buf, int fragcount,
129 size_t fragsize, unsigned int cfg,
130 unsigned int x_count, unsigned int ycount, size_t wdsize)
131{
132
133 int i;
134
135 for (i = 0; i < fragcount; ++i) {
136 desc[i].next_desc_addr = (unsigned long)&(desc[i + 1]);
137 desc[i].start_addr = (unsigned long)buf + i*fragsize;
138 desc[i].cfg = cfg;
139 desc[i].x_count = x_count;
140 desc[i].x_modify = wdsize;
141 desc[i].y_count = ycount;
142 desc[i].y_modify = wdsize;
143 }
144
145 /* make circular */
146 desc[fragcount-1].next_desc_addr = (unsigned long)desc;
147
148 pr_debug("setup desc: desc0=%p, next0=%lx, desc1=%p,"
149 "next1=%lx\nx_count=%x,y_count=%x,addr=0x%lx,cfs=0x%x\n",
150 &(desc[0]), desc[0].next_desc_addr,
151 &(desc[1]), desc[1].next_desc_addr,
152 desc[0].x_count, desc[0].y_count,
153 desc[0].start_addr, desc[0].cfg);
154}
155
156static int sport_start(struct sport_device *sport)
157{
158 enable_dma(sport->dma_rx_chan);
159 enable_dma(sport->dma_tx_chan);
160 sport->regs->rcr1 |= RSPEN;
161 sport->regs->tcr1 |= TSPEN;
162 SSYNC();
163
164 return 0;
165}
166
167static int sport_stop(struct sport_device *sport)
168{
169 sport->regs->tcr1 &= ~TSPEN;
170 sport->regs->rcr1 &= ~RSPEN;
171 SSYNC();
172
173 disable_dma(sport->dma_rx_chan);
174 disable_dma(sport->dma_tx_chan);
175 return 0;
176}
177
178static inline int sport_hook_rx_dummy(struct sport_device *sport)
179{
180 struct dmasg *desc, temp_desc;
181 unsigned long flags;
182
183 BUG_ON(sport->dummy_rx_desc == NULL);
184 BUG_ON(sport->curr_rx_desc == sport->dummy_rx_desc);
185
186 /* Maybe the dummy buffer descriptor ring is damaged */
187 sport->dummy_rx_desc->next_desc_addr = \
188 (unsigned long)(sport->dummy_rx_desc+1);
189
190 local_irq_save(flags);
191 desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_rx_chan);
192 /* Copy the descriptor which will be damaged to backup */
193 temp_desc = *desc;
194 desc->x_count = 0xa;
195 desc->y_count = 0;
196 desc->next_desc_addr = (unsigned long)(sport->dummy_rx_desc);
197 local_irq_restore(flags);
198 /* Waiting for dummy buffer descriptor is already hooked*/
199 while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
200 sizeof(struct dmasg)) !=
201 (unsigned long)sport->dummy_rx_desc)
202 ;
203 sport->curr_rx_desc = sport->dummy_rx_desc;
204 /* Restore the damaged descriptor */
205 *desc = temp_desc;
206
207 return 0;
208}
209
210static inline int sport_rx_dma_start(struct sport_device *sport, int dummy)
211{
212 if (dummy) {
213 sport->dummy_rx_desc->next_desc_addr = \
214 (unsigned long) sport->dummy_rx_desc;
215 sport->curr_rx_desc = sport->dummy_rx_desc;
216 } else
217 sport->curr_rx_desc = sport->dma_rx_desc;
218
219 set_dma_next_desc_addr(sport->dma_rx_chan, \
220 (unsigned long)(sport->curr_rx_desc));
221 set_dma_x_count(sport->dma_rx_chan, 0);
222 set_dma_x_modify(sport->dma_rx_chan, 0);
223 set_dma_config(sport->dma_rx_chan, (DMAFLOW_LARGE | NDSIZE_9 | \
224 WDSIZE_32 | WNR));
225 set_dma_curr_addr(sport->dma_rx_chan, sport->curr_rx_desc->start_addr);
226 SSYNC();
227
228 return 0;
229}
230
231static inline int sport_tx_dma_start(struct sport_device *sport, int dummy)
232{
233 if (dummy) {
234 sport->dummy_tx_desc->next_desc_addr = \
235 (unsigned long) sport->dummy_tx_desc;
236 sport->curr_tx_desc = sport->dummy_tx_desc;
237 } else
238 sport->curr_tx_desc = sport->dma_tx_desc;
239
240 set_dma_next_desc_addr(sport->dma_tx_chan, \
241 (unsigned long)(sport->curr_tx_desc));
242 set_dma_x_count(sport->dma_tx_chan, 0);
243 set_dma_x_modify(sport->dma_tx_chan, 0);
244 set_dma_config(sport->dma_tx_chan,
245 (DMAFLOW_LARGE | NDSIZE_9 | WDSIZE_32));
246 set_dma_curr_addr(sport->dma_tx_chan, sport->curr_tx_desc->start_addr);
247 SSYNC();
248
249 return 0;
250}
251
252int sport_rx_start(struct sport_device *sport)
253{
254 unsigned long flags;
255 pr_debug("%s enter\n", __func__);
256 if (sport->rx_run)
257 return -EBUSY;
258 if (sport->tx_run) {
259 /* tx is running, rx is not running */
260 BUG_ON(sport->dma_rx_desc == NULL);
261 BUG_ON(sport->curr_rx_desc != sport->dummy_rx_desc);
262 local_irq_save(flags);
263 while ((get_dma_curr_desc_ptr(sport->dma_rx_chan) -
264 sizeof(struct dmasg)) !=
265 (unsigned long)sport->dummy_rx_desc)
266 ;
267 sport->dummy_rx_desc->next_desc_addr =
268 (unsigned long)(sport->dma_rx_desc);
269 local_irq_restore(flags);
270 sport->curr_rx_desc = sport->dma_rx_desc;
271 } else {
272 sport_tx_dma_start(sport, 1);
273 sport_rx_dma_start(sport, 0);
274 sport_start(sport);
275 }
276
277 sport->rx_run = 1;
278
279 return 0;
280}
281EXPORT_SYMBOL(sport_rx_start);
282
283int sport_rx_stop(struct sport_device *sport)
284{
285 pr_debug("%s enter\n", __func__);
286
287 if (!sport->rx_run)
288 return 0;
289 if (sport->tx_run) {
290 /* TX dma is still running, hook the dummy buffer */
291 sport_hook_rx_dummy(sport);
292 } else {
293 /* Both rx and tx dma will be stopped */
294 sport_stop(sport);
295 sport->curr_rx_desc = NULL;
296 sport->curr_tx_desc = NULL;
297 }
298
299 sport->rx_run = 0;
300
301 return 0;
302}
303EXPORT_SYMBOL(sport_rx_stop);
304
305static inline int sport_hook_tx_dummy(struct sport_device *sport)
306{
307 struct dmasg *desc, temp_desc;
308 unsigned long flags;
309
310 BUG_ON(sport->dummy_tx_desc == NULL);
311 BUG_ON(sport->curr_tx_desc == sport->dummy_tx_desc);
312
313 sport->dummy_tx_desc->next_desc_addr = \
314 (unsigned long)(sport->dummy_tx_desc+1);
315
316 /* Shorten the time on last normal descriptor */
317 local_irq_save(flags);
318 desc = (struct dmasg *)get_dma_next_desc_ptr(sport->dma_tx_chan);
319 /* Store the descriptor which will be damaged */
320 temp_desc = *desc;
321 desc->x_count = 0xa;
322 desc->y_count = 0;
323 desc->next_desc_addr = (unsigned long)(sport->dummy_tx_desc);
324 local_irq_restore(flags);
325 /* Waiting for dummy buffer descriptor is already hooked*/
326 while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) - \
327 sizeof(struct dmasg)) != \
328 (unsigned long)sport->dummy_tx_desc)
329 ;
330 sport->curr_tx_desc = sport->dummy_tx_desc;
331 /* Restore the damaged descriptor */
332 *desc = temp_desc;
333
334 return 0;
335}
336
337int sport_tx_start(struct sport_device *sport)
338{
339 unsigned flags;
340 pr_debug("%s: tx_run:%d, rx_run:%d\n", __func__,
341 sport->tx_run, sport->rx_run);
342 if (sport->tx_run)
343 return -EBUSY;
344 if (sport->rx_run) {
345 BUG_ON(sport->dma_tx_desc == NULL);
346 BUG_ON(sport->curr_tx_desc != sport->dummy_tx_desc);
347 /* Hook the normal buffer descriptor */
348 local_irq_save(flags);
349 while ((get_dma_curr_desc_ptr(sport->dma_tx_chan) -
350 sizeof(struct dmasg)) !=
351 (unsigned long)sport->dummy_tx_desc)
352 ;
353 sport->dummy_tx_desc->next_desc_addr =
354 (unsigned long)(sport->dma_tx_desc);
355 local_irq_restore(flags);
356 sport->curr_tx_desc = sport->dma_tx_desc;
357 } else {
358
359 sport_tx_dma_start(sport, 0);
360 /* Let rx dma run the dummy buffer */
361 sport_rx_dma_start(sport, 1);
362 sport_start(sport);
363 }
364 sport->tx_run = 1;
365 return 0;
366}
367EXPORT_SYMBOL(sport_tx_start);
368
369int sport_tx_stop(struct sport_device *sport)
370{
371 if (!sport->tx_run)
372 return 0;
373 if (sport->rx_run) {
374 /* RX is still running, hook the dummy buffer */
375 sport_hook_tx_dummy(sport);
376 } else {
377 /* Both rx and tx dma stopped */
378 sport_stop(sport);
379 sport->curr_rx_desc = NULL;
380 sport->curr_tx_desc = NULL;
381 }
382
383 sport->tx_run = 0;
384
385 return 0;
386}
387EXPORT_SYMBOL(sport_tx_stop);
388
389static inline int compute_wdsize(size_t wdsize)
390{
391 switch (wdsize) {
392 case 1:
393 return WDSIZE_8;
394 case 2:
395 return WDSIZE_16;
396 case 4:
397 default:
398 return WDSIZE_32;
399 }
400}
401
402int sport_config_rx_dma(struct sport_device *sport, void *buf,
403 int fragcount, size_t fragsize)
404{
405 unsigned int x_count;
406 unsigned int y_count;
407 unsigned int cfg;
408 dma_addr_t addr;
409
410 pr_debug("%s buf:%p, frag:%d, fragsize:0x%lx\n", __func__, \
411 buf, fragcount, fragsize);
412
413 x_count = fragsize / sport->wdsize;
414 y_count = 0;
415
416 /* for fragments larger than 64k words we use 2d dma,
417 * denote fragecount as two numbers' mutliply and both of them
418 * are less than 64k.*/
419 if (x_count >= 0x10000) {
420 int i, count = x_count;
421
422 for (i = 16; i > 0; i--) {
423 x_count = 1 << i;
424 if ((count & (x_count - 1)) == 0) {
425 y_count = count >> i;
426 if (y_count < 0x10000)
427 break;
428 }
429 }
430 if (i == 0)
431 return -EINVAL;
432 }
433 pr_debug("%s(x_count:0x%x, y_count:0x%x)\n", __func__,
434 x_count, y_count);
435
436 if (sport->dma_rx_desc)
437 dma_free_coherent(NULL, sport->rx_desc_bytes,
438 sport->dma_rx_desc, 0);
439
440 /* Allocate a new descritor ring as current one. */
441 sport->dma_rx_desc = dma_alloc_coherent(NULL, \
442 fragcount * sizeof(struct dmasg), &addr, 0);
443 sport->rx_desc_bytes = fragcount * sizeof(struct dmasg);
444
445 if (!sport->dma_rx_desc) {
446 pr_err("Failed to allocate memory for rx desc\n");
447 return -ENOMEM;
448 }
449
450 sport->rx_buf = buf;
451 sport->rx_fragsize = fragsize;
452 sport->rx_frags = fragcount;
453
454 cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | WNR | \
455 (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
456
457 if (y_count != 0)
458 cfg |= DMA2D;
459
460 setup_desc(sport->dma_rx_desc, buf, fragcount, fragsize,
461 cfg|DMAEN, x_count, y_count, sport->wdsize);
462
463 return 0;
464}
465EXPORT_SYMBOL(sport_config_rx_dma);
466
467int sport_config_tx_dma(struct sport_device *sport, void *buf, \
468 int fragcount, size_t fragsize)
469{
470 unsigned int x_count;
471 unsigned int y_count;
472 unsigned int cfg;
473 dma_addr_t addr;
474
475 pr_debug("%s buf:%p, fragcount:%d, fragsize:0x%lx\n",
476 __func__, buf, fragcount, fragsize);
477
478 x_count = fragsize/sport->wdsize;
479 y_count = 0;
480
481 /* for fragments larger than 64k words we use 2d dma,
482 * denote fragecount as two numbers' mutliply and both of them
483 * are less than 64k.*/
484 if (x_count >= 0x10000) {
485 int i, count = x_count;
486
487 for (i = 16; i > 0; i--) {
488 x_count = 1 << i;
489 if ((count & (x_count - 1)) == 0) {
490 y_count = count >> i;
491 if (y_count < 0x10000)
492 break;
493 }
494 }
495 if (i == 0)
496 return -EINVAL;
497 }
498 pr_debug("%s x_count:0x%x, y_count:0x%x\n", __func__,
499 x_count, y_count);
500
501
502 if (sport->dma_tx_desc) {
503 dma_free_coherent(NULL, sport->tx_desc_bytes, \
504 sport->dma_tx_desc, 0);
505 }
506
507 sport->dma_tx_desc = dma_alloc_coherent(NULL, \
508 fragcount * sizeof(struct dmasg), &addr, 0);
509 sport->tx_desc_bytes = fragcount * sizeof(struct dmasg);
510 if (!sport->dma_tx_desc) {
511 pr_err("Failed to allocate memory for tx desc\n");
512 return -ENOMEM;
513 }
514
515 sport->tx_buf = buf;
516 sport->tx_fragsize = fragsize;
517 sport->tx_frags = fragcount;
518 cfg = 0x7000 | DI_EN | compute_wdsize(sport->wdsize) | \
519 (DESC_ELEMENT_COUNT << 8); /* large descriptor mode */
520
521 if (y_count != 0)
522 cfg |= DMA2D;
523
524 setup_desc(sport->dma_tx_desc, buf, fragcount, fragsize,
525 cfg|DMAEN, x_count, y_count, sport->wdsize);
526
527 return 0;
528}
529EXPORT_SYMBOL(sport_config_tx_dma);
530
531/* setup dummy dma descriptor ring, which don't generate interrupts,
532 * the x_modify is set to 0 */
533static int sport_config_rx_dummy(struct sport_device *sport)
534{
535 struct dmasg *desc;
536 unsigned config;
537
538 pr_debug("%s entered\n", __func__);
539#if L1_DATA_A_LENGTH != 0
540 desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
541#else
542 {
543 dma_addr_t addr;
544 desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
545 }
546#endif
547 if (desc == NULL) {
548 pr_err("Failed to allocate memory for dummy rx desc\n");
549 return -ENOMEM;
550 }
551 memset(desc, 0, 2 * sizeof(*desc));
552 sport->dummy_rx_desc = desc;
553 desc->start_addr = (unsigned long)sport->dummy_buf;
554 config = DMAFLOW_LARGE | NDSIZE_9 | compute_wdsize(sport->wdsize)
555 | WNR | DMAEN;
556 desc->cfg = config;
557 desc->x_count = sport->dummy_count/sport->wdsize;
558 desc->x_modify = sport->wdsize;
559 desc->y_count = 0;
560 desc->y_modify = 0;
561 memcpy(desc+1, desc, sizeof(*desc));
562 desc->next_desc_addr = (unsigned long)(desc+1);
563 desc[1].next_desc_addr = (unsigned long)desc;
564 return 0;
565}
566
567static int sport_config_tx_dummy(struct sport_device *sport)
568{
569 struct dmasg *desc;
570 unsigned int config;
571
572 pr_debug("%s entered\n", __func__);
573
574#if L1_DATA_A_LENGTH != 0
575 desc = (struct dmasg *) l1_data_sram_alloc(2 * sizeof(*desc));
576#else
577 {
578 dma_addr_t addr;
579 desc = dma_alloc_coherent(NULL, 2 * sizeof(*desc), &addr, 0);
580 }
581#endif
582 if (!desc) {
583 pr_err("Failed to allocate memory for dummy tx desc\n");
584 return -ENOMEM;
585 }
586 memset(desc, 0, 2 * sizeof(*desc));
587 sport->dummy_tx_desc = desc;
588 desc->start_addr = (unsigned long)sport->dummy_buf + \
589 sport->dummy_count;
590 config = DMAFLOW_LARGE | NDSIZE_9 |
591 compute_wdsize(sport->wdsize) | DMAEN;
592 desc->cfg = config;
593 desc->x_count = sport->dummy_count/sport->wdsize;
594 desc->x_modify = sport->wdsize;
595 desc->y_count = 0;
596 desc->y_modify = 0;
597 memcpy(desc+1, desc, sizeof(*desc));
598 desc->next_desc_addr = (unsigned long)(desc+1);
599 desc[1].next_desc_addr = (unsigned long)desc;
600 return 0;
601}
602
603unsigned long sport_curr_offset_rx(struct sport_device *sport)
604{
605 unsigned long curr = get_dma_curr_addr(sport->dma_rx_chan);
606
607 return (unsigned char *)curr - sport->rx_buf;
608}
609EXPORT_SYMBOL(sport_curr_offset_rx);
610
611unsigned long sport_curr_offset_tx(struct sport_device *sport)
612{
613 unsigned long curr = get_dma_curr_addr(sport->dma_tx_chan);
614
615 return (unsigned char *)curr - sport->tx_buf;
616}
617EXPORT_SYMBOL(sport_curr_offset_tx);
618
619void sport_incfrag(struct sport_device *sport, int *frag, int tx)
620{
621 ++(*frag);
622 if (tx == 1 && *frag == sport->tx_frags)
623 *frag = 0;
624
625 if (tx == 0 && *frag == sport->rx_frags)
626 *frag = 0;
627}
628EXPORT_SYMBOL(sport_incfrag);
629
630void sport_decfrag(struct sport_device *sport, int *frag, int tx)
631{
632 --(*frag);
633 if (tx == 1 && *frag == 0)
634 *frag = sport->tx_frags;
635
636 if (tx == 0 && *frag == 0)
637 *frag = sport->rx_frags;
638}
639EXPORT_SYMBOL(sport_decfrag);
640
641static int sport_check_status(struct sport_device *sport,
642 unsigned int *sport_stat,
643 unsigned int *rx_stat,
644 unsigned int *tx_stat)
645{
646 int status = 0;
647
648 if (sport_stat) {
649 SSYNC();
650 status = sport->regs->stat;
651 if (status & (TOVF|TUVF|ROVF|RUVF))
652 sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
653 SSYNC();
654 *sport_stat = status;
655 }
656
657 if (rx_stat) {
658 SSYNC();
659 status = get_dma_curr_irqstat(sport->dma_rx_chan);
660 if (status & (DMA_DONE|DMA_ERR))
661 clear_dma_irqstat(sport->dma_rx_chan);
662 SSYNC();
663 *rx_stat = status;
664 }
665
666 if (tx_stat) {
667 SSYNC();
668 status = get_dma_curr_irqstat(sport->dma_tx_chan);
669 if (status & (DMA_DONE|DMA_ERR))
670 clear_dma_irqstat(sport->dma_tx_chan);
671 SSYNC();
672 *tx_stat = status;
673 }
674
675 return 0;
676}
677
678int sport_dump_stat(struct sport_device *sport, char *buf, size_t len)
679{
680 int ret;
681
682 ret = snprintf(buf, len,
683 "sts: 0x%04x\n"
684 "rx dma %d sts: 0x%04x tx dma %d sts: 0x%04x\n",
685 sport->regs->stat,
686 sport->dma_rx_chan,
687 get_dma_curr_irqstat(sport->dma_rx_chan),
688 sport->dma_tx_chan,
689 get_dma_curr_irqstat(sport->dma_tx_chan));
690 buf += ret;
691 len -= ret;
692
693 ret += snprintf(buf, len,
694 "curr_rx_desc:0x%p, curr_tx_desc:0x%p\n"
695 "dma_rx_desc:0x%p, dma_tx_desc:0x%p\n"
696 "dummy_rx_desc:0x%p, dummy_tx_desc:0x%p\n",
697 sport->curr_rx_desc, sport->curr_tx_desc,
698 sport->dma_rx_desc, sport->dma_tx_desc,
699 sport->dummy_rx_desc, sport->dummy_tx_desc);
700
701 return ret;
702}
703
704static irqreturn_t rx_handler(int irq, void *dev_id)
705{
706 unsigned int rx_stat;
707 struct sport_device *sport = dev_id;
708
709 pr_debug("%s enter\n", __func__);
710 sport_check_status(sport, NULL, &rx_stat, NULL);
711 if (!(rx_stat & DMA_DONE))
712 pr_err("rx dma is already stopped\n");
713
714 if (sport->rx_callback) {
715 sport->rx_callback(sport->rx_data);
716 return IRQ_HANDLED;
717 }
718
719 return IRQ_NONE;
720}
721
722static irqreturn_t tx_handler(int irq, void *dev_id)
723{
724 unsigned int tx_stat;
725 struct sport_device *sport = dev_id;
726 pr_debug("%s enter\n", __func__);
727 sport_check_status(sport, NULL, NULL, &tx_stat);
728 if (!(tx_stat & DMA_DONE)) {
729 pr_err("tx dma is already stopped\n");
730 return IRQ_HANDLED;
731 }
732 if (sport->tx_callback) {
733 sport->tx_callback(sport->tx_data);
734 return IRQ_HANDLED;
735 }
736
737 return IRQ_NONE;
738}
739
740static irqreturn_t err_handler(int irq, void *dev_id)
741{
742 unsigned int status = 0;
743 struct sport_device *sport = dev_id;
744
745 pr_debug("%s\n", __func__);
746 if (sport_check_status(sport, &status, NULL, NULL)) {
747 pr_err("error checking status ??");
748 return IRQ_NONE;
749 }
750
751 if (status & (TOVF|TUVF|ROVF|RUVF)) {
752 pr_info("sport status error:%s%s%s%s\n",
753 status & TOVF ? " TOVF" : "",
754 status & TUVF ? " TUVF" : "",
755 status & ROVF ? " ROVF" : "",
756 status & RUVF ? " RUVF" : "");
757 if (status & TOVF || status & TUVF) {
758 disable_dma(sport->dma_tx_chan);
759 if (sport->tx_run)
760 sport_tx_dma_start(sport, 0);
761 else
762 sport_tx_dma_start(sport, 1);
763 enable_dma(sport->dma_tx_chan);
764 } else {
765 disable_dma(sport->dma_rx_chan);
766 if (sport->rx_run)
767 sport_rx_dma_start(sport, 0);
768 else
769 sport_rx_dma_start(sport, 1);
770 enable_dma(sport->dma_rx_chan);
771 }
772 }
773 status = sport->regs->stat;
774 if (status & (TOVF|TUVF|ROVF|RUVF))
775 sport->regs->stat = (status & (TOVF|TUVF|ROVF|RUVF));
776 SSYNC();
777
778 if (sport->err_callback)
779 sport->err_callback(sport->err_data);
780
781 return IRQ_HANDLED;
782}
783
784int sport_set_rx_callback(struct sport_device *sport,
785 void (*rx_callback)(void *), void *rx_data)
786{
787 BUG_ON(rx_callback == NULL);
788 sport->rx_callback = rx_callback;
789 sport->rx_data = rx_data;
790
791 return 0;
792}
793EXPORT_SYMBOL(sport_set_rx_callback);
794
795int sport_set_tx_callback(struct sport_device *sport,
796 void (*tx_callback)(void *), void *tx_data)
797{
798 BUG_ON(tx_callback == NULL);
799 sport->tx_callback = tx_callback;
800 sport->tx_data = tx_data;
801
802 return 0;
803}
804EXPORT_SYMBOL(sport_set_tx_callback);
805
806int sport_set_err_callback(struct sport_device *sport,
807 void (*err_callback)(void *), void *err_data)
808{
809 BUG_ON(err_callback == NULL);
810 sport->err_callback = err_callback;
811 sport->err_data = err_data;
812
813 return 0;
814}
815EXPORT_SYMBOL(sport_set_err_callback);
816
817struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
818 unsigned dummy_count, void *private_data)
819{
820 int ret;
821 struct sport_device *sport;
822 pr_debug("%s enter\n", __func__);
823 BUG_ON(param == NULL);
824 BUG_ON(wdsize == 0 || dummy_count == 0);
825 sport = kmalloc(sizeof(struct sport_device), GFP_KERNEL);
826 if (!sport) {
827 pr_err("Failed to allocate for sport device\n");
828 return NULL;
829 }
830
831 memset(sport, 0, sizeof(struct sport_device));
832 sport->dma_rx_chan = param->dma_rx_chan;
833 sport->dma_tx_chan = param->dma_tx_chan;
834 sport->err_irq = param->err_irq;
835 sport->regs = param->regs;
836 sport->private_data = private_data;
837
838 if (request_dma(sport->dma_rx_chan, "SPORT RX Data") == -EBUSY) {
839 pr_err("Failed to request RX dma %d\n", \
840 sport->dma_rx_chan);
841 goto __init_err1;
842 }
843 if (set_dma_callback(sport->dma_rx_chan, rx_handler, sport) != 0) {
844 pr_err("Failed to request RX irq %d\n", \
845 sport->dma_rx_chan);
846 goto __init_err2;
847 }
848
849 if (request_dma(sport->dma_tx_chan, "SPORT TX Data") == -EBUSY) {
850 pr_err("Failed to request TX dma %d\n", \
851 sport->dma_tx_chan);
852 goto __init_err2;
853 }
854
855 if (set_dma_callback(sport->dma_tx_chan, tx_handler, sport) != 0) {
856 pr_err("Failed to request TX irq %d\n", \
857 sport->dma_tx_chan);
858 goto __init_err3;
859 }
860
861 if (request_irq(sport->err_irq, err_handler, IRQF_SHARED, "SPORT err",
862 sport) < 0) {
863 pr_err("Failed to request err irq:%d\n", \
864 sport->err_irq);
865 goto __init_err3;
866 }
867
868 pr_err("dma rx:%d tx:%d, err irq:%d, regs:%p\n",
869 sport->dma_rx_chan, sport->dma_tx_chan,
870 sport->err_irq, sport->regs);
871
872 sport->wdsize = wdsize;
873 sport->dummy_count = dummy_count;
874
875#if L1_DATA_A_LENGTH != 0
876 sport->dummy_buf = l1_data_sram_alloc(dummy_count * 2);
877#else
878 sport->dummy_buf = kmalloc(dummy_count * 2, GFP_KERNEL);
879#endif
880 if (sport->dummy_buf == NULL) {
881 pr_err("Failed to allocate dummy buffer\n");
882 goto __error;
883 }
884
885 memset(sport->dummy_buf, 0, dummy_count * 2);
886 ret = sport_config_rx_dummy(sport);
887 if (ret) {
888 pr_err("Failed to config rx dummy ring\n");
889 goto __error;
890 }
891 ret = sport_config_tx_dummy(sport);
892 if (ret) {
893 pr_err("Failed to config tx dummy ring\n");
894 goto __error;
895 }
896
897 return sport;
898__error:
899 free_irq(sport->err_irq, sport);
900__init_err3:
901 free_dma(sport->dma_tx_chan);
902__init_err2:
903 free_dma(sport->dma_rx_chan);
904__init_err1:
905 kfree(sport);
906 return NULL;
907}
908EXPORT_SYMBOL(sport_init);
909
910void sport_done(struct sport_device *sport)
911{
912 if (sport == NULL)
913 return;
914
915 sport_stop(sport);
916 if (sport->dma_rx_desc)
917 dma_free_coherent(NULL, sport->rx_desc_bytes,
918 sport->dma_rx_desc, 0);
919 if (sport->dma_tx_desc)
920 dma_free_coherent(NULL, sport->tx_desc_bytes,
921 sport->dma_tx_desc, 0);
922
923#if L1_DATA_A_LENGTH != 0
924 l1_data_sram_free(sport->dummy_rx_desc);
925 l1_data_sram_free(sport->dummy_tx_desc);
926 l1_data_sram_free(sport->dummy_buf);
927#else
928 dma_free_coherent(NULL, 2*sizeof(struct dmasg),
929 sport->dummy_rx_desc, 0);
930 dma_free_coherent(NULL, 2*sizeof(struct dmasg),
931 sport->dummy_tx_desc, 0);
932 kfree(sport->dummy_buf);
933#endif
934 free_dma(sport->dma_rx_chan);
935 free_dma(sport->dma_tx_chan);
936 free_irq(sport->err_irq, sport);
937
938 kfree(sport);
939 sport = NULL;
940}
941EXPORT_SYMBOL(sport_done);
942/*
943* It is only used to send several bytes when dma is not enabled
944 * sport controller is configured but not enabled.
945 * Multichannel cannot works with pio mode */
946/* Used by ac97 to write and read codec register */
947int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
948 u8 *in_data, int len)
949{
950 unsigned short dma_config;
951 unsigned short status;
952 unsigned long flags;
953 unsigned long wait = 0;
954
955 pr_debug("%s enter, out_data:%p, in_data:%p len:%d\n", \
956 __func__, out_data, in_data, len);
957 pr_debug("tcr1:0x%04x, tcr2:0x%04x, tclkdiv:0x%04x, tfsdiv:0x%04x\n"
958 "mcmc1:0x%04x, mcmc2:0x%04x\n",
959 sport->regs->tcr1, sport->regs->tcr2,
960 sport->regs->tclkdiv, sport->regs->tfsdiv,
961 sport->regs->mcmc1, sport->regs->mcmc2);
962 flush_dcache_range((unsigned)out_data, (unsigned)(out_data + len));
963
964 /* Enable tx dma */
965 dma_config = (RESTART | WDSIZE_16 | DI_EN);
966 set_dma_start_addr(sport->dma_tx_chan, (unsigned long)out_data);
967 set_dma_x_count(sport->dma_tx_chan, len/2);
968 set_dma_x_modify(sport->dma_tx_chan, 2);
969 set_dma_config(sport->dma_tx_chan, dma_config);
970 enable_dma(sport->dma_tx_chan);
971
972 if (in_data != NULL) {
973 invalidate_dcache_range((unsigned)in_data, \
974 (unsigned)(in_data + len));
975 /* Enable rx dma */
976 dma_config = (RESTART | WDSIZE_16 | WNR | DI_EN);
977 set_dma_start_addr(sport->dma_rx_chan, (unsigned long)in_data);
978 set_dma_x_count(sport->dma_rx_chan, len/2);
979 set_dma_x_modify(sport->dma_rx_chan, 2);
980 set_dma_config(sport->dma_rx_chan, dma_config);
981 enable_dma(sport->dma_rx_chan);
982 }
983
984 local_irq_save(flags);
985 sport->regs->tcr1 |= TSPEN;
986 sport->regs->rcr1 |= RSPEN;
987 SSYNC();
988
989 status = get_dma_curr_irqstat(sport->dma_tx_chan);
990 while (status & DMA_RUN) {
991 udelay(1);
992 status = get_dma_curr_irqstat(sport->dma_tx_chan);
993 pr_debug("DMA status:0x%04x\n", status);
994 if (wait++ > 100)
995 goto __over;
996 }
997 status = sport->regs->stat;
998 wait = 0;
999
1000 while (!(status & TXHRE)) {
1001 pr_debug("sport status:0x%04x\n", status);
1002 udelay(1);
1003 status = *(unsigned short *)&sport->regs->stat;
1004 if (wait++ > 1000)
1005 goto __over;
1006 }
1007 /* Wait for the last byte sent out */
1008 udelay(20);
1009 pr_debug("sport status:0x%04x\n", status);
1010
1011__over:
1012 sport->regs->tcr1 &= ~TSPEN;
1013 sport->regs->rcr1 &= ~RSPEN;
1014 SSYNC();
1015 disable_dma(sport->dma_tx_chan);
1016 /* Clear the status */
1017 clear_dma_irqstat(sport->dma_tx_chan);
1018 if (in_data != NULL) {
1019 disable_dma(sport->dma_rx_chan);
1020 clear_dma_irqstat(sport->dma_rx_chan);
1021 }
1022 SSYNC();
1023 local_irq_restore(flags);
1024
1025 return 0;
1026}
1027EXPORT_SYMBOL(sport_send_and_recv);
1028
1029MODULE_AUTHOR("Roy Huang");
1030MODULE_DESCRIPTION("SPORT driver for ADI Blackfin");
1031MODULE_LICENSE("GPL");
1032
diff --git a/sound/soc/blackfin/bf5xx-sport.h b/sound/soc/blackfin/bf5xx-sport.h
new file mode 100644
index 000000000000..fcadcc081f7f
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -0,0 +1,194 @@
1/*
2 * File: bf5xx_ac97_sport.h
3 * Based on:
4 * Author: Roy Huang <roy.huang@analog.com>
5 *
6 * Created:
7 * Description:
8 *
9 * Copyright 2004-2007 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
30#ifndef __BF5XX_SPORT_H__
31#define __BF5XX_SPORT_H__
32
33#include <linux/types.h>
34#include <linux/wait.h>
35#include <linux/workqueue.h>
36#include <asm/dma.h>
37
38struct sport_register {
39 u16 tcr1; u16 reserved0;
40 u16 tcr2; u16 reserved1;
41 u16 tclkdiv; u16 reserved2;
42 u16 tfsdiv; u16 reserved3;
43 u32 tx;
44 u32 reserved_l0;
45 u32 rx;
46 u32 reserved_l1;
47 u16 rcr1; u16 reserved4;
48 u16 rcr2; u16 reserved5;
49 u16 rclkdiv; u16 reserved6;
50 u16 rfsdiv; u16 reserved7;
51 u16 stat; u16 reserved8;
52 u16 chnl; u16 reserved9;
53 u16 mcmc1; u16 reserved10;
54 u16 mcmc2; u16 reserved11;
55 u32 mtcs0;
56 u32 mtcs1;
57 u32 mtcs2;
58 u32 mtcs3;
59 u32 mrcs0;
60 u32 mrcs1;
61 u32 mrcs2;
62 u32 mrcs3;
63};
64
65#define DESC_ELEMENT_COUNT 9
66
67struct sport_device {
68 int dma_rx_chan;
69 int dma_tx_chan;
70 int err_irq;
71 struct sport_register *regs;
72
73 unsigned char *rx_buf;
74 unsigned char *tx_buf;
75 unsigned int rx_fragsize;
76 unsigned int tx_fragsize;
77 unsigned int rx_frags;
78 unsigned int tx_frags;
79 unsigned int wdsize;
80
81 /* for dummy dma transfer */
82 void *dummy_buf;
83 unsigned int dummy_count;
84
85 /* DMA descriptor ring head of current audio stream*/
86 struct dmasg *dma_rx_desc;
87 struct dmasg *dma_tx_desc;
88 unsigned int rx_desc_bytes;
89 unsigned int tx_desc_bytes;
90
91 unsigned int rx_run:1; /* rx is running */
92 unsigned int tx_run:1; /* tx is running */
93
94 struct dmasg *dummy_rx_desc;
95 struct dmasg *dummy_tx_desc;
96
97 struct dmasg *curr_rx_desc;
98 struct dmasg *curr_tx_desc;
99
100 int rx_curr_frag;
101 int tx_curr_frag;
102
103 unsigned int rcr1;
104 unsigned int rcr2;
105 int rx_tdm_count;
106
107 unsigned int tcr1;
108 unsigned int tcr2;
109 int tx_tdm_count;
110
111 void (*rx_callback)(void *data);
112 void *rx_data;
113 void (*tx_callback)(void *data);
114 void *tx_data;
115 void (*err_callback)(void *data);
116 void *err_data;
117 unsigned char *tx_dma_buf;
118 unsigned char *rx_dma_buf;
119#ifdef CONFIG_SND_MMAP_SUPPORT
120 dma_addr_t tx_dma_phy;
121 dma_addr_t rx_dma_phy;
122 int tx_pos;/*pcm sample count*/
123 int rx_pos;
124 unsigned int tx_buffer_size;
125 unsigned int rx_buffer_size;
126 int tx_delay_pos;
127 int once;
128#endif
129 void *private_data;
130};
131
132extern struct sport_device *sport_handle;
133
134struct sport_param {
135 int dma_rx_chan;
136 int dma_tx_chan;
137 int err_irq;
138 struct sport_register *regs;
139};
140
141struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
142 unsigned dummy_count, void *private_data);
143
144void sport_done(struct sport_device *sport);
145
146/* first use these ...*/
147
148/* note: multichannel is in units of 8 channels, tdm_count is number of channels
149 * NOT / 8 ! all channels are enabled by default */
150int sport_set_multichannel(struct sport_device *sport, int tdm_count,
151 u32 mask, int packed);
152
153int sport_config_rx(struct sport_device *sport,
154 unsigned int rcr1, unsigned int rcr2,
155 unsigned int clkdiv, unsigned int fsdiv);
156
157int sport_config_tx(struct sport_device *sport,
158 unsigned int tcr1, unsigned int tcr2,
159 unsigned int clkdiv, unsigned int fsdiv);
160
161/* ... then these: */
162
163/* buffer size (in bytes) == fragcount * fragsize_bytes */
164
165/* this is not a very general api, it sets the dma to 2d autobuffer mode */
166
167int sport_config_rx_dma(struct sport_device *sport, void *buf,
168 int fragcount, size_t fragsize_bytes);
169
170int sport_config_tx_dma(struct sport_device *sport, void *buf,
171 int fragcount, size_t fragsize_bytes);
172
173int sport_tx_start(struct sport_device *sport);
174int sport_tx_stop(struct sport_device *sport);
175int sport_rx_start(struct sport_device *sport);
176int sport_rx_stop(struct sport_device *sport);
177
178/* for use in interrupt handler */
179unsigned long sport_curr_offset_rx(struct sport_device *sport);
180unsigned long sport_curr_offset_tx(struct sport_device *sport);
181
182void sport_incfrag(struct sport_device *sport, int *frag, int tx);
183void sport_decfrag(struct sport_device *sport, int *frag, int tx);
184
185int sport_set_rx_callback(struct sport_device *sport,
186 void (*rx_callback)(void *), void *rx_data);
187int sport_set_tx_callback(struct sport_device *sport,
188 void (*tx_callback)(void *), void *tx_data);
189int sport_set_err_callback(struct sport_device *sport,
190 void (*err_callback)(void *), void *err_data);
191
192int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
193 u8 *in_data, int len);
194#endif /* BF53X_SPORT_H */
diff --git a/sound/soc/blackfin/bf5xx-ssm2602.c b/sound/soc/blackfin/bf5xx-ssm2602.c
new file mode 100644
index 000000000000..e15f67fd7769
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ssm2602.c
@@ -0,0 +1,186 @@
1/*
2 * File: sound/soc/blackfin/bf5xx-ssm2602.c
3 * Author: Cliff Cai <Cliff.Cai@analog.com>
4 *
5 * Created: Tue June 06 2008
6 * Description: board driver for SSM2602 sound chip
7 *
8 * Modified:
9 * Copyright 2008 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/moduleparam.h>
31#include <linux/device.h>
32
33#include <sound/core.h>
34#include <sound/pcm.h>
35#include <sound/soc.h>
36#include <sound/soc-dapm.h>
37#include <sound/pcm_params.h>
38
39#include <asm/dma.h>
40#include <asm/portmux.h>
41#include <linux/gpio.h>
42#include "../codecs/ssm2602.h"
43#include "bf5xx-sport.h"
44#include "bf5xx-i2s-pcm.h"
45#include "bf5xx-i2s.h"
46
47static struct snd_soc_machine bf5xx_ssm2602;
48
49static int bf5xx_ssm2602_startup(struct snd_pcm_substream *substream)
50{
51 struct snd_soc_pcm_runtime *rtd = substream->private_data;
52 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
53
54 pr_debug("%s enter\n", __func__);
55 cpu_dai->private_data = sport_handle;
56 return 0;
57}
58
59static int bf5xx_ssm2602_hw_params(struct snd_pcm_substream *substream,
60 struct snd_pcm_hw_params *params)
61{
62 struct snd_soc_pcm_runtime *rtd = substream->private_data;
63 struct snd_soc_dai *codec_dai = rtd->dai->codec_dai;
64 struct snd_soc_dai *cpu_dai = rtd->dai->cpu_dai;
65 unsigned int clk = 0;
66 int ret = 0;
67
68 pr_debug("%s rate %d format %x\n", __func__, params_rate(params),
69 params_format(params));
70 /*
71 * If you are using a crystal source which frequency is not 12MHz
72 * then modify the below case statement with frequency of the crystal.
73 *
74 * If you are using the SPORT to generate clocking then this is
75 * where to do it.
76 */
77
78 switch (params_rate(params)) {
79 case 8000:
80 case 16000:
81 case 48000:
82 case 96000:
83 case 11025:
84 case 22050:
85 case 44100:
86 clk = 12000000;
87 break;
88 }
89
90 /*
91 * CODEC is master for BCLK and LRC in this configuration.
92 */
93
94 /* set codec DAI configuration */
95 ret = codec_dai->dai_ops.set_fmt(codec_dai, SND_SOC_DAIFMT_I2S |
96 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
97 if (ret < 0)
98 return ret;
99 /* set cpu DAI configuration */
100 ret = cpu_dai->dai_ops.set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S |
101 SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM);
102 if (ret < 0)
103 return ret;
104
105 ret = codec_dai->dai_ops.set_sysclk(codec_dai, SSM2602_SYSCLK, clk,
106 SND_SOC_CLOCK_IN);
107 if (ret < 0)
108 return ret;
109
110 return 0;
111}
112
113static struct snd_soc_ops bf5xx_ssm2602_ops = {
114 .startup = bf5xx_ssm2602_startup,
115 .hw_params = bf5xx_ssm2602_hw_params,
116};
117
118static struct snd_soc_dai_link bf5xx_ssm2602_dai = {
119 .name = "ssm2602",
120 .stream_name = "SSM2602",
121 .cpu_dai = &bf5xx_i2s_dai,
122 .codec_dai = &ssm2602_dai,
123 .ops = &bf5xx_ssm2602_ops,
124};
125
126/*
127 * SSM2602 2 wire address is determined by CSB
128 * state during powerup.
129 * low = 0x1a
130 * high = 0x1b
131 */
132
133static struct ssm2602_setup_data bf5xx_ssm2602_setup = {
134 .i2c_bus = 0,
135 .i2c_address = 0x1b,
136};
137
138static struct snd_soc_machine bf5xx_ssm2602 = {
139 .name = "bf5xx_ssm2602",
140 .dai_link = &bf5xx_ssm2602_dai,
141 .num_links = 1,
142};
143
144static struct snd_soc_device bf5xx_ssm2602_snd_devdata = {
145 .machine = &bf5xx_ssm2602,
146 .platform = &bf5xx_i2s_soc_platform,
147 .codec_dev = &soc_codec_dev_ssm2602,
148 .codec_data = &bf5xx_ssm2602_setup,
149};
150
151static struct platform_device *bf52x_ssm2602_snd_device;
152
153static int __init bf5xx_ssm2602_init(void)
154{
155 int ret;
156
157 pr_debug("%s enter\n", __func__);
158 bf52x_ssm2602_snd_device = platform_device_alloc("soc-audio", -1);
159 if (!bf52x_ssm2602_snd_device)
160 return -ENOMEM;
161
162 platform_set_drvdata(bf52x_ssm2602_snd_device,
163 &bf5xx_ssm2602_snd_devdata);
164 bf5xx_ssm2602_snd_devdata.dev = &bf52x_ssm2602_snd_device->dev;
165 ret = platform_device_add(bf52x_ssm2602_snd_device);
166
167 if (ret)
168 platform_device_put(bf52x_ssm2602_snd_device);
169
170 return ret;
171}
172
173static void __exit bf5xx_ssm2602_exit(void)
174{
175 pr_debug("%s enter\n", __func__);
176 platform_device_unregister(bf52x_ssm2602_snd_device);
177}
178
179module_init(bf5xx_ssm2602_init);
180module_exit(bf5xx_ssm2602_exit);
181
182/* Module information */
183MODULE_AUTHOR("Cliff Cai");
184MODULE_DESCRIPTION("ALSA SoC SSM2602 BF527-EZKIT");
185MODULE_LICENSE("GPL");
186