aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc/blackfin
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/blackfin')
-rw-r--r--sound/soc/blackfin/Kconfig85
-rw-r--r--sound/soc/blackfin/Makefile20
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.c429
-rw-r--r--sound/soc/blackfin/bf5xx-ac97-pcm.h29
-rw-r--r--sound/soc/blackfin/bf5xx-ac97.c407
-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-i2s-pcm.c288
-rw-r--r--sound/soc/blackfin/bf5xx-i2s-pcm.h29
-rw-r--r--sound/soc/blackfin/bf5xx-i2s.c292
-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.h192
-rw-r--r--sound/soc/blackfin/bf5xx-ssm2602.c186
14 files changed, 3152 insertions, 0 deletions
diff --git a/sound/soc/blackfin/Kconfig b/sound/soc/blackfin/Kconfig
new file mode 100644
index 000000000000..f98331d099e7
--- /dev/null
+++ b/sound/soc/blackfin/Kconfig
@@ -0,0 +1,85 @@
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_AC97
21 tristate "SoC AC97 Audio for the ADI BF5xx chip"
22 depends on BLACKFIN && SND_SOC
23 help
24 Say Y or M if you want to add support for codecs attached to
25 the Blackfin SPORT (synchronous serial ports) interface in slot 16
26 mode (pseudo AC97 interface).
27 You will also need to select the audio interfaces to support below.
28
29 Note:
30 AC97 codecs which do not implment the slot-16 mode will not function
31 properly with this driver. This driver is known to work with the
32 Analog Devices line of AC97 codecs.
33
34config SND_MMAP_SUPPORT
35 bool "Enable MMAP Support"
36 depends on SND_BF5XX_AC97
37 default y
38 help
39 Say y if you want AC97 driver to support mmap mode.
40 We introduce an intermediate buffer to simulate mmap.
41
42config SND_BF5XX_SOC_SPORT
43 tristate
44
45config SND_BF5XX_SOC_I2S
46 tristate
47 select SND_BF5XX_SOC_SPORT
48
49config SND_BF5XX_SOC_AC97
50 tristate
51 select AC97_BUS
52 select SND_SOC_AC97_BUS
53 select SND_BF5XX_SOC_SPORT
54
55config SND_BF5XX_SOC_AD1980
56 tristate "SoC AD1980/1 Audio support for BF5xx"
57 depends on SND_BF5XX_AC97
58 select SND_BF5XX_SOC_AC97
59 select SND_SOC_AD1980
60 help
61 Say Y if you want to add support for SoC audio on BF5xx STAMP/EZKIT.
62
63config SND_BF5XX_SPORT_NUM
64 int "Set a SPORT for Sound chip"
65 depends on (SND_BF5XX_I2S || SND_BF5XX_AC97)
66 range 0 3 if BF54x
67 range 0 1 if (BF53x || BF561)
68 default 0
69 help
70 Set the correct SPORT for sound chip.
71
72config SND_BF5XX_HAVE_COLD_RESET
73 bool "BOARD has COLD Reset GPIO"
74 depends on SND_BF5XX_AC97
75 default y if BFIN548_EZKIT
76 default n if !BFIN548_EZKIT
77
78config SND_BF5XX_RESET_GPIO_NUM
79 int "Set a GPIO for cold reset"
80 depends on SND_BF5XX_HAVE_COLD_RESET
81 range 0 159
82 default 19 if BFIN548_EZKIT
83 default 5 if BFIN537_STAMP
84 help
85 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..9ea8bd9e0ba3
--- /dev/null
+++ b/sound/soc/blackfin/Makefile
@@ -0,0 +1,20 @@
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
17
18
19obj-$(CONFIG_SND_BF5XX_SOC_AD1980) += snd-ad1980.o
20obj-$(CONFIG_SND_BF5XX_SOC_SSM2602) += snd-ssm2602.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..51f4907c4831
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c
@@ -0,0 +1,429 @@
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 } else {
60 bf5xx_ac97_to_pcm(
61 (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos,
62 (__u32 *)runtime->dma_area + sport->rx_pos, count);
63 sport->rx_pos += runtime->period_size;
64 if (sport->rx_pos >= runtime->buffer_size)
65 sport->rx_pos %= runtime->buffer_size;
66 }
67}
68#endif
69
70static void bf5xx_dma_irq(void *data)
71{
72 struct snd_pcm_substream *pcm = data;
73#if defined(CONFIG_SND_MMAP_SUPPORT)
74 struct snd_pcm_runtime *runtime = pcm->runtime;
75 bf5xx_mmap_copy(pcm, runtime->period_size);
76#endif
77 snd_pcm_period_elapsed(pcm);
78}
79
80/* The memory size for pure pcm data is 128*1024 = 0x20000 bytes.
81 * The total rx/tx buffer is for ac97 frame to hold all pcm data
82 * is 0x20000 * sizeof(struct ac97_frame) / 4.
83 */
84#ifdef CONFIG_SND_MMAP_SUPPORT
85static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
86 .info = SNDRV_PCM_INFO_INTERLEAVED |
87 SNDRV_PCM_INFO_MMAP |
88 SNDRV_PCM_INFO_MMAP_VALID |
89 SNDRV_PCM_INFO_BLOCK_TRANSFER,
90#else
91static const struct snd_pcm_hardware bf5xx_pcm_hardware = {
92 .info = SNDRV_PCM_INFO_INTERLEAVED |
93 SNDRV_PCM_INFO_BLOCK_TRANSFER,
94#endif
95 .formats = SNDRV_PCM_FMTBIT_S16_LE,
96 .period_bytes_min = 32,
97 .period_bytes_max = 0x10000,
98 .periods_min = 1,
99 .periods_max = PAGE_SIZE/32,
100 .buffer_bytes_max = 0x20000, /* 128 kbytes */
101 .fifo_size = 16,
102};
103
104static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream,
105 struct snd_pcm_hw_params *params)
106{
107 size_t size = bf5xx_pcm_hardware.buffer_bytes_max
108 * sizeof(struct ac97_frame) / 4;
109
110 snd_pcm_lib_malloc_pages(substream, size);
111
112 return 0;
113}
114
115static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream)
116{
117 snd_pcm_lib_free_pages(substream);
118 return 0;
119}
120
121static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream)
122{
123 struct snd_pcm_runtime *runtime = substream->runtime;
124 struct sport_device *sport = runtime->private_data;
125
126 /* An intermediate buffer is introduced for implementing mmap for
127 * SPORT working in TMD mode(include AC97).
128 */
129#if defined(CONFIG_SND_MMAP_SUPPORT)
130 size_t size = bf5xx_pcm_hardware.buffer_bytes_max
131 * sizeof(struct ac97_frame) / 4;
132 /*clean up intermediate buffer*/
133 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
134 memset(sport->tx_dma_buf, 0, size);
135 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
136 sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods,
137 runtime->period_size * sizeof(struct ac97_frame));
138 } else {
139 memset(sport->rx_dma_buf, 0, size);
140 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
141 sport_config_rx_dma(sport, sport->rx_dma_buf, runtime->periods,
142 runtime->period_size * sizeof(struct ac97_frame));
143 }
144#else
145 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
146 sport_set_tx_callback(sport, bf5xx_dma_irq, substream);
147 sport_config_tx_dma(sport, runtime->dma_area, runtime->periods,
148 runtime->period_size * sizeof(struct ac97_frame));
149 } else {
150 sport_set_rx_callback(sport, bf5xx_dma_irq, substream);
151 sport_config_rx_dma(sport, runtime->dma_area, runtime->periods,
152 runtime->period_size * sizeof(struct ac97_frame));
153 }
154#endif
155 return 0;
156}
157
158static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
159{
160 struct snd_pcm_runtime *runtime = substream->runtime;
161 struct sport_device *sport = runtime->private_data;
162 int ret = 0;
163
164 pr_debug("%s enter\n", __func__);
165 switch (cmd) {
166 case SNDRV_PCM_TRIGGER_START:
167 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
168 sport_tx_start(sport);
169 else
170 sport_rx_start(sport);
171 break;
172 case SNDRV_PCM_TRIGGER_STOP:
173 case SNDRV_PCM_TRIGGER_SUSPEND:
174 case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
175 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
176#if defined(CONFIG_SND_MMAP_SUPPORT)
177 sport->tx_pos = 0;
178#endif
179 sport_tx_stop(sport);
180 } else {
181#if defined(CONFIG_SND_MMAP_SUPPORT)
182 sport->rx_pos = 0;
183#endif
184 sport_rx_stop(sport);
185 }
186 break;
187 default:
188 ret = -EINVAL;
189 }
190 return ret;
191}
192
193static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream)
194{
195 struct snd_pcm_runtime *runtime = substream->runtime;
196 struct sport_device *sport = runtime->private_data;
197 unsigned int curr;
198
199#if defined(CONFIG_SND_MMAP_SUPPORT)
200 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
201 curr = sport->tx_pos;
202 else
203 curr = sport->rx_pos;
204#else
205
206 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
207 curr = sport_curr_offset_tx(sport) / sizeof(struct ac97_frame);
208 else
209 curr = sport_curr_offset_rx(sport) / sizeof(struct ac97_frame);
210
211#endif
212 return curr;
213}
214
215static int bf5xx_pcm_open(struct snd_pcm_substream *substream)
216{
217 struct snd_pcm_runtime *runtime = substream->runtime;
218 int ret;
219
220 pr_debug("%s enter\n", __func__);
221 snd_soc_set_runtime_hwparams(substream, &bf5xx_pcm_hardware);
222
223 ret = snd_pcm_hw_constraint_integer(runtime,
224 SNDRV_PCM_HW_PARAM_PERIODS);
225 if (ret < 0)
226 goto out;
227
228 if (sport_handle != NULL)
229 runtime->private_data = sport_handle;
230 else {
231 pr_err("sport_handle is NULL\n");
232 return -1;
233 }
234 return 0;
235
236 out:
237 return ret;
238}
239
240#ifdef CONFIG_SND_MMAP_SUPPORT
241static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream,
242 struct vm_area_struct *vma)
243{
244 struct snd_pcm_runtime *runtime = substream->runtime;
245 size_t size = vma->vm_end - vma->vm_start;
246 vma->vm_start = (unsigned long)runtime->dma_area;
247 vma->vm_end = vma->vm_start + size;
248 vma->vm_flags |= VM_SHARED;
249 return 0 ;
250}
251#else
252static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel,
253 snd_pcm_uframes_t pos,
254 void __user *buf, snd_pcm_uframes_t count)
255{
256 struct snd_pcm_runtime *runtime = substream->runtime;
257
258 pr_debug("%s copy pos:0x%lx count:0x%lx\n",
259 substream->stream ? "Capture" : "Playback", pos, count);
260
261 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
262 bf5xx_pcm_to_ac97(
263 (struct ac97_frame *)runtime->dma_area + pos,
264 buf, count);
265 else
266 bf5xx_ac97_to_pcm(
267 (struct ac97_frame *)runtime->dma_area + pos,
268 buf, count);
269 return 0;
270}
271#endif
272
273struct snd_pcm_ops bf5xx_pcm_ac97_ops = {
274 .open = bf5xx_pcm_open,
275 .ioctl = snd_pcm_lib_ioctl,
276 .hw_params = bf5xx_pcm_hw_params,
277 .hw_free = bf5xx_pcm_hw_free,
278 .prepare = bf5xx_pcm_prepare,
279 .trigger = bf5xx_pcm_trigger,
280 .pointer = bf5xx_pcm_pointer,
281#ifdef CONFIG_SND_MMAP_SUPPORT
282 .mmap = bf5xx_pcm_mmap,
283#else
284 .copy = bf5xx_pcm_copy,
285#endif
286};
287
288static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
289{
290 struct snd_pcm_substream *substream = pcm->streams[stream].substream;
291 struct snd_dma_buffer *buf = &substream->dma_buffer;
292 size_t size = bf5xx_pcm_hardware.buffer_bytes_max
293 * sizeof(struct ac97_frame) / 4;
294
295 buf->dev.type = SNDRV_DMA_TYPE_DEV;
296 buf->dev.dev = pcm->card->dev;
297 buf->private_data = NULL;
298 buf->area = dma_alloc_coherent(pcm->card->dev, size,
299 &buf->addr, GFP_KERNEL);
300 if (!buf->area) {
301 pr_err("Failed to allocate dma memory\n");
302 pr_err("Please increase uncached DMA memory region\n");
303 return -ENOMEM;
304 }
305 buf->bytes = size;
306
307 pr_debug("%s, area:%p, size:0x%08lx\n", __func__,
308 buf->area, buf->bytes);
309
310 if (stream == SNDRV_PCM_STREAM_PLAYBACK)
311 sport_handle->tx_buf = buf->area;
312 else
313 sport_handle->rx_buf = buf->area;
314
315/*
316 * Need to allocate local buffer when enable
317 * MMAP for SPORT working in TMD mode (include AC97).
318 */
319#if defined(CONFIG_SND_MMAP_SUPPORT)
320 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
321 if (!sport_handle->tx_dma_buf) {
322 sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \
323 size, &sport_handle->tx_dma_phy, GFP_KERNEL);
324 if (!sport_handle->tx_dma_buf) {
325 pr_err("Failed to allocate memory for tx dma \
326 buf - Please increase uncached DMA \
327 memory region\n");
328 return -ENOMEM;
329 } else
330 memset(sport_handle->tx_dma_buf, 0, size);
331 } else
332 memset(sport_handle->tx_dma_buf, 0, size);
333 } else {
334 if (!sport_handle->rx_dma_buf) {
335 sport_handle->rx_dma_buf = dma_alloc_coherent(NULL, \
336 size, &sport_handle->rx_dma_phy, GFP_KERNEL);
337 if (!sport_handle->rx_dma_buf) {
338 pr_err("Failed to allocate memory for rx dma \
339 buf - Please increase uncached DMA \
340 memory region\n");
341 return -ENOMEM;
342 } else
343 memset(sport_handle->rx_dma_buf, 0, size);
344 } else
345 memset(sport_handle->rx_dma_buf, 0, size);
346 }
347#endif
348 return 0;
349}
350
351static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm)
352{
353 struct snd_pcm_substream *substream;
354 struct snd_dma_buffer *buf;
355 int stream;
356#if defined(CONFIG_SND_MMAP_SUPPORT)
357 size_t size = bf5xx_pcm_hardware.buffer_bytes_max *
358 sizeof(struct ac97_frame) / 4;
359#endif
360 for (stream = 0; stream < 2; stream++) {
361 substream = pcm->streams[stream].substream;
362 if (!substream)
363 continue;
364
365 buf = &substream->dma_buffer;
366 if (!buf->area)
367 continue;
368 dma_free_coherent(NULL, buf->bytes, buf->area, 0);
369 buf->area = NULL;
370#if defined(CONFIG_SND_MMAP_SUPPORT)
371 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
372 if (sport_handle->tx_dma_buf)
373 dma_free_coherent(NULL, size, \
374 sport_handle->tx_dma_buf, 0);
375 sport_handle->tx_dma_buf = NULL;
376 } else {
377
378 if (sport_handle->rx_dma_buf)
379 dma_free_coherent(NULL, size, \
380 sport_handle->rx_dma_buf, 0);
381 sport_handle->rx_dma_buf = NULL;
382 }
383#endif
384 }
385 if (sport_handle)
386 sport_done(sport_handle);
387}
388
389static u64 bf5xx_pcm_dmamask = DMA_32BIT_MASK;
390
391int bf5xx_pcm_ac97_new(struct snd_card *card, struct snd_soc_dai *dai,
392 struct snd_pcm *pcm)
393{
394 int ret = 0;
395
396 pr_debug("%s enter\n", __func__);
397 if (!card->dev->dma_mask)
398 card->dev->dma_mask = &bf5xx_pcm_dmamask;
399 if (!card->dev->coherent_dma_mask)
400 card->dev->coherent_dma_mask = DMA_32BIT_MASK;
401
402 if (dai->playback.channels_min) {
403 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
404 SNDRV_PCM_STREAM_PLAYBACK);
405 if (ret)
406 goto out;
407 }
408
409 if (dai->capture.channels_min) {
410 ret = bf5xx_pcm_preallocate_dma_buffer(pcm,
411 SNDRV_PCM_STREAM_CAPTURE);
412 if (ret)
413 goto out;
414 }
415 out:
416 return ret;
417}
418
419struct snd_soc_platform bf5xx_ac97_soc_platform = {
420 .name = "bf5xx-audio",
421 .pcm_ops = &bf5xx_pcm_ac97_ops,
422 .pcm_new = bf5xx_pcm_ac97_new,
423 .pcm_free = bf5xx_pcm_free_dma_buffers,
424};
425EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform);
426
427MODULE_AUTHOR("Cliff Cai");
428MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module");
429MODULE_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..c782e311fd56
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-ac97.c
@@ -0,0 +1,407 @@
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 sport_incfrag(sport, &nextfrag, 1);
133
134 nextwrite = (struct ac97_frame *)(sport->tx_buf + \
135 nextfrag * sport->tx_fragsize);
136 pr_debug("sport->tx_buf:%p, nextfrag:0x%x nextwrite:%p, cmd_count:%d\n",
137 sport->tx_buf, nextfrag, nextwrite, cmd_count[nextfrag]);
138 nextwrite[cmd_count[nextfrag]].ac97_tag |= TAG_CMD;
139 nextwrite[cmd_count[nextfrag]].ac97_addr = addr;
140 nextwrite[cmd_count[nextfrag]].ac97_data = data;
141 ++cmd_count[nextfrag];
142 pr_debug("ac97_sport: Inserting %02x/%04x into fragment %d\n",
143 addr >> 8, data, nextfrag);
144}
145
146static unsigned short bf5xx_ac97_read(struct snd_ac97 *ac97,
147 unsigned short reg)
148{
149 struct ac97_frame out_frame[2], in_frame[2];
150
151 pr_debug("%s enter 0x%x\n", __func__, reg);
152
153 /* When dma descriptor is enabled, the register should not be read */
154 if (sport_handle->tx_run || sport_handle->rx_run) {
155 pr_err("Could you send a mail to cliff.cai@analog.com "
156 "to report this?\n");
157 return -EFAULT;
158 }
159
160 memset(&out_frame, 0, 2 * sizeof(struct ac97_frame));
161 memset(&in_frame, 0, 2 * sizeof(struct ac97_frame));
162 out_frame[0].ac97_tag = TAG_VALID | TAG_CMD;
163 out_frame[0].ac97_addr = ((reg << 8) | 0x8000);
164 sport_send_and_recv(sport_handle, (unsigned char *)&out_frame,
165 (unsigned char *)&in_frame,
166 2 * sizeof(struct ac97_frame));
167 return in_frame[1].ac97_data;
168}
169
170void bf5xx_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
171 unsigned short val)
172{
173 pr_debug("%s enter 0x%x:0x%04x\n", __func__, reg, val);
174
175 if (sport_handle->tx_run) {
176 enqueue_cmd(ac97, (reg << 8), val); /* write */
177 enqueue_cmd(ac97, (reg << 8) | 0x8000, 0); /* read back */
178 } else {
179 struct ac97_frame frame;
180 memset(&frame, 0, sizeof(struct ac97_frame));
181 frame.ac97_tag = TAG_VALID | TAG_CMD;
182 frame.ac97_addr = (reg << 8);
183 frame.ac97_data = val;
184 sport_send_and_recv(sport_handle, (unsigned char *)&frame, \
185 NULL, sizeof(struct ac97_frame));
186 }
187}
188
189static void bf5xx_ac97_warm_reset(struct snd_ac97 *ac97)
190{
191#if defined(CONFIG_BF54x) || defined(CONFIG_BF561) || \
192 (defined(BF537_FAMILY) && (CONFIG_SND_BF5XX_SPORT_NUM == 1))
193
194#define CONCAT(a, b, c) a ## b ## c
195#define BFIN_SPORT_RFS(x) CONCAT(P_SPORT, x, _RFS)
196
197 u16 per = BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM);
198 u16 gpio = P_IDENT(BFIN_SPORT_RFS(CONFIG_SND_BF5XX_SPORT_NUM));
199
200 pr_debug("%s enter\n", __func__);
201
202 peripheral_free(per);
203 gpio_request(gpio, "bf5xx-ac97");
204 gpio_direction_output(gpio, 1);
205 udelay(2);
206 gpio_set_value(gpio, 0);
207 udelay(1);
208 gpio_free(gpio);
209 peripheral_request(per, "soc-audio");
210#else
211 pr_info("%s: Not implemented\n", __func__);
212#endif
213}
214
215static void bf5xx_ac97_cold_reset(struct snd_ac97 *ac97)
216{
217#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
218 pr_debug("%s enter\n", __func__);
219
220 /* It is specified for bf548-ezkit */
221 gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 0);
222 /* Keep reset pin low for 1 ms */
223 mdelay(1);
224 gpio_set_value(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
225 /* Wait for bit clock recover */
226 mdelay(1);
227#else
228 pr_info("%s: Not implemented\n", __func__);
229#endif
230}
231
232struct snd_ac97_bus_ops soc_ac97_ops = {
233 .read = bf5xx_ac97_read,
234 .write = bf5xx_ac97_write,
235 .warm_reset = bf5xx_ac97_warm_reset,
236 .reset = bf5xx_ac97_cold_reset,
237};
238EXPORT_SYMBOL_GPL(soc_ac97_ops);
239
240#ifdef CONFIG_PM
241static int bf5xx_ac97_suspend(struct platform_device *pdev,
242 struct snd_soc_dai *dai)
243{
244 struct sport_device *sport =
245 (struct sport_device *)dai->private_data;
246
247 pr_debug("%s : sport %d\n", __func__, dai->id);
248 if (!dai->active)
249 return 0;
250 if (dai->capture.active)
251 sport_rx_stop(sport);
252 if (dai->playback.active)
253 sport_tx_stop(sport);
254 return 0;
255}
256
257static int bf5xx_ac97_resume(struct platform_device *pdev,
258 struct snd_soc_dai *dai)
259{
260 int ret;
261 struct sport_device *sport =
262 (struct sport_device *)dai->private_data;
263
264 pr_debug("%s : sport %d\n", __func__, dai->id);
265 if (!dai->active)
266 return 0;
267
268 ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
269 if (ret) {
270 pr_err("SPORT is busy!\n");
271 return -EBUSY;
272 }
273
274 ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
275 if (ret) {
276 pr_err("SPORT is busy!\n");
277 return -EBUSY;
278 }
279
280 ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
281 if (ret) {
282 pr_err("SPORT is busy!\n");
283 return -EBUSY;
284 }
285
286 if (dai->capture.active)
287 sport_rx_start(sport);
288 if (dai->playback.active)
289 sport_tx_start(sport);
290 return 0;
291}
292
293#else
294#define bf5xx_ac97_suspend NULL
295#define bf5xx_ac97_resume NULL
296#endif
297
298static int bf5xx_ac97_probe(struct platform_device *pdev,
299 struct snd_soc_dai *dai)
300{
301 int ret;
302#if defined(CONFIG_BF54x)
303 u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1,
304 PIN_REQ_SPORT_2, PIN_REQ_SPORT_3};
305#else
306 u16 sport_req[][7] = {PIN_REQ_SPORT_0, PIN_REQ_SPORT_1};
307#endif
308 cmd_count = (int *)get_zeroed_page(GFP_KERNEL);
309 if (cmd_count == NULL)
310 return -ENOMEM;
311
312 if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
313 pr_err("Requesting Peripherals failed\n");
314 return -EFAULT;
315 }
316
317#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
318 /* Request PB3 as reset pin */
319 if (gpio_request(CONFIG_SND_BF5XX_RESET_GPIO_NUM, "SND_AD198x RESET")) {
320 pr_err("Failed to request GPIO_%d for reset\n",
321 CONFIG_SND_BF5XX_RESET_GPIO_NUM);
322 peripheral_free_list(&sport_req[sport_num][0]);
323 return -1;
324 }
325 gpio_direction_output(CONFIG_SND_BF5XX_RESET_GPIO_NUM, 1);
326#endif
327 sport_handle = sport_init(&sport_params[sport_num], 2, \
328 sizeof(struct ac97_frame), NULL);
329 if (!sport_handle) {
330 peripheral_free_list(&sport_req[sport_num][0]);
331#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
332 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
333#endif
334 return -ENODEV;
335 }
336 /*SPORT works in TDM mode to simulate AC97 transfers*/
337 ret = sport_set_multichannel(sport_handle, 16, 0x1F, 1);
338 if (ret) {
339 pr_err("SPORT is busy!\n");
340 kfree(sport_handle);
341 peripheral_free_list(&sport_req[sport_num][0]);
342#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
343 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
344#endif
345 return -EBUSY;
346 }
347
348 ret = sport_config_rx(sport_handle, IRFS, 0xF, 0, (16*16-1));
349 if (ret) {
350 pr_err("SPORT is busy!\n");
351 kfree(sport_handle);
352 peripheral_free_list(&sport_req[sport_num][0]);
353#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
354 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
355#endif
356 return -EBUSY;
357 }
358
359 ret = sport_config_tx(sport_handle, ITFS, 0xF, 0, (16*16-1));
360 if (ret) {
361 pr_err("SPORT is busy!\n");
362 kfree(sport_handle);
363 peripheral_free_list(&sport_req[sport_num][0]);
364#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
365 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
366#endif
367 return -EBUSY;
368 }
369 return 0;
370}
371
372static void bf5xx_ac97_remove(struct platform_device *pdev,
373 struct snd_soc_dai *dai)
374{
375 free_page((unsigned long)cmd_count);
376 cmd_count = NULL;
377#ifdef CONFIG_SND_BF5XX_HAVE_COLD_RESET
378 gpio_free(CONFIG_SND_BF5XX_RESET_GPIO_NUM);
379#endif
380}
381
382struct snd_soc_dai bfin_ac97_dai = {
383 .name = "bf5xx-ac97",
384 .id = 0,
385 .type = SND_SOC_DAI_AC97,
386 .probe = bf5xx_ac97_probe,
387 .remove = bf5xx_ac97_remove,
388 .suspend = bf5xx_ac97_suspend,
389 .resume = bf5xx_ac97_resume,
390 .playback = {
391 .stream_name = "AC97 Playback",
392 .channels_min = 2,
393 .channels_max = 2,
394 .rates = SNDRV_PCM_RATE_48000,
395 .formats = SNDRV_PCM_FMTBIT_S16_LE, },
396 .capture = {
397 .stream_name = "AC97 Capture",
398 .channels_min = 2,
399 .channels_max = 2,
400 .rates = SNDRV_PCM_RATE_48000,
401 .formats = SNDRV_PCM_FMTBIT_S16_LE, },
402};
403EXPORT_SYMBOL_GPL(bfin_ac97_dai);
404
405MODULE_AUTHOR("Roy Huang");
406MODULE_DESCRIPTION("AC97 driver for ADI Blackfin");
407MODULE_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-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..43a4092eeb89
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-i2s.c
@@ -0,0 +1,292 @@
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 int bf5xx_i2s_set_dai_fmt(struct snd_soc_dai *cpu_dai,
74 unsigned int fmt)
75{
76 int ret = 0;
77
78 /* interface format:support I2S,slave mode */
79 switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
80 case SND_SOC_DAIFMT_I2S:
81 break;
82 case SND_SOC_DAIFMT_LEFT_J:
83 ret = -EINVAL;
84 break;
85 default:
86 ret = -EINVAL;
87 break;
88 }
89
90 switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
91 case SND_SOC_DAIFMT_CBS_CFS:
92 ret = -EINVAL;
93 break;
94 case SND_SOC_DAIFMT_CBM_CFS:
95 ret = -EINVAL;
96 break;
97 case SND_SOC_DAIFMT_CBM_CFM:
98 break;
99 case SND_SOC_DAIFMT_CBS_CFM:
100 ret = -EINVAL;
101 break;
102 default:
103 ret = -EINVAL;
104 break;
105 }
106
107 return ret;
108}
109
110static int bf5xx_i2s_startup(struct snd_pcm_substream *substream)
111{
112 pr_debug("%s enter\n", __func__);
113
114 /*this counter is used for counting how many pcm streams are opened*/
115 bf5xx_i2s.counter++;
116 return 0;
117}
118
119static int bf5xx_i2s_hw_params(struct snd_pcm_substream *substream,
120 struct snd_pcm_hw_params *params)
121{
122 int ret = 0;
123
124 bf5xx_i2s.tcr2 &= ~0x1f;
125 bf5xx_i2s.rcr2 &= ~0x1f;
126 switch (params_format(params)) {
127 case SNDRV_PCM_FORMAT_S16_LE:
128 bf5xx_i2s.tcr2 |= 15;
129 bf5xx_i2s.rcr2 |= 15;
130 break;
131 case SNDRV_PCM_FORMAT_S24_LE:
132 bf5xx_i2s.tcr2 |= 23;
133 bf5xx_i2s.rcr2 |= 23;
134 break;
135 case SNDRV_PCM_FORMAT_S32_LE:
136 bf5xx_i2s.tcr2 |= 31;
137 bf5xx_i2s.rcr2 |= 31;
138 break;
139 }
140
141 if (bf5xx_i2s.counter == 1) {
142 /*
143 * TX and RX are not independent,they are enabled at the
144 * same time, even if only one side is running. So, we
145 * need to configure both of them at the time when the first
146 * stream is opened.
147 *
148 * CPU DAI format:I2S, slave mode.
149 */
150 ret = sport_config_rx(sport_handle, RFSR | RCKFE,
151 RSFSE|bf5xx_i2s.rcr2, 0, 0);
152 if (ret) {
153 pr_err("SPORT is busy!\n");
154 return -EBUSY;
155 }
156
157 ret = sport_config_tx(sport_handle, TFSR | TCKFE,
158 TSFSE|bf5xx_i2s.tcr2, 0, 0);
159 if (ret) {
160 pr_err("SPORT is busy!\n");
161 return -EBUSY;
162 }
163 }
164
165 return 0;
166}
167
168static void bf5xx_i2s_shutdown(struct snd_pcm_substream *substream)
169{
170 pr_debug("%s enter\n", __func__);
171 bf5xx_i2s.counter--;
172}
173
174static int bf5xx_i2s_probe(struct platform_device *pdev,
175 struct snd_soc_dai *dai)
176{
177 u16 sport_req[][7] = {
178 { P_SPORT0_DTPRI, P_SPORT0_TSCLK, P_SPORT0_RFS,
179 P_SPORT0_DRPRI, P_SPORT0_RSCLK, 0},
180 { P_SPORT1_DTPRI, P_SPORT1_TSCLK, P_SPORT1_RFS,
181 P_SPORT1_DRPRI, P_SPORT1_RSCLK, 0},
182 };
183
184 pr_debug("%s enter\n", __func__);
185 if (peripheral_request_list(&sport_req[sport_num][0], "soc-audio")) {
186 pr_err("Requesting Peripherals failed\n");
187 return -EFAULT;
188 }
189
190 /* request DMA for SPORT */
191 sport_handle = sport_init(&sport_params[sport_num], 4, \
192 2 * sizeof(u32), NULL);
193 if (!sport_handle) {
194 peripheral_free_list(&sport_req[sport_num][0]);
195 return -ENODEV;
196 }
197
198 return 0;
199}
200
201#ifdef CONFIG_PM
202static int bf5xx_i2s_suspend(struct platform_device *dev,
203 struct snd_soc_dai *dai)
204{
205 struct sport_device *sport =
206 (struct sport_device *)dai->private_data;
207
208 pr_debug("%s : sport %d\n", __func__, dai->id);
209 if (!dai->active)
210 return 0;
211 if (dai->capture.active)
212 sport_rx_stop(sport);
213 if (dai->playback.active)
214 sport_tx_stop(sport);
215 return 0;
216}
217
218static int bf5xx_i2s_resume(struct platform_device *pdev,
219 struct snd_soc_dai *dai)
220{
221 int ret;
222 struct sport_device *sport =
223 (struct sport_device *)dai->private_data;
224
225 pr_debug("%s : sport %d\n", __func__, dai->id);
226 if (!dai->active)
227 return 0;
228
229 ret = sport_config_rx(sport_handle, RFSR | RCKFE, RSFSE|0x1f, 0, 0);
230 if (ret) {
231 pr_err("SPORT is busy!\n");
232 return -EBUSY;
233 }
234
235 ret = sport_config_tx(sport_handle, TFSR | TCKFE, TSFSE|0x1f, 0, 0);
236 if (ret) {
237 pr_err("SPORT is busy!\n");
238 return -EBUSY;
239 }
240
241 if (dai->capture.active)
242 sport_rx_start(sport);
243 if (dai->playback.active)
244 sport_tx_start(sport);
245 return 0;
246}
247
248#else
249#define bf5xx_i2s_suspend NULL
250#define bf5xx_i2s_resume NULL
251#endif
252
253#define BF5XX_I2S_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
254 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | \
255 SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
256 SNDRV_PCM_RATE_96000)
257
258#define BF5XX_I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |\
259 SNDRV_PCM_FMTBIT_S32_LE)
260
261struct snd_soc_dai bf5xx_i2s_dai = {
262 .name = "bf5xx-i2s",
263 .id = 0,
264 .type = SND_SOC_DAI_I2S,
265 .probe = bf5xx_i2s_probe,
266 .suspend = bf5xx_i2s_suspend,
267 .resume = bf5xx_i2s_resume,
268 .playback = {
269 .channels_min = 2,
270 .channels_max = 2,
271 .rates = BF5XX_I2S_RATES,
272 .formats = BF5XX_I2S_FORMATS,},
273 .capture = {
274 .channels_min = 2,
275 .channels_max = 2,
276 .rates = BF5XX_I2S_RATES,
277 .formats = BF5XX_I2S_FORMATS,},
278 .ops = {
279 .startup = bf5xx_i2s_startup,
280 .shutdown = bf5xx_i2s_shutdown,
281 .hw_params = bf5xx_i2s_hw_params,},
282 .dai_ops = {
283 .set_fmt = bf5xx_i2s_set_dai_fmt,
284 },
285};
286EXPORT_SYMBOL_GPL(bf5xx_i2s_dai);
287
288/* Module information */
289MODULE_AUTHOR("Cliff Cai");
290MODULE_DESCRIPTION("I2S driver for ADI Blackfin");
291MODULE_LICENSE("GPL");
292
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..4c163454bbf8
--- /dev/null
+++ b/sound/soc/blackfin/bf5xx-sport.h
@@ -0,0 +1,192 @@
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#endif
127 void *private_data;
128};
129
130extern struct sport_device *sport_handle;
131
132struct sport_param {
133 int dma_rx_chan;
134 int dma_tx_chan;
135 int err_irq;
136 struct sport_register *regs;
137};
138
139struct sport_device *sport_init(struct sport_param *param, unsigned wdsize,
140 unsigned dummy_count, void *private_data);
141
142void sport_done(struct sport_device *sport);
143
144/* first use these ...*/
145
146/* note: multichannel is in units of 8 channels, tdm_count is number of channels
147 * NOT / 8 ! all channels are enabled by default */
148int sport_set_multichannel(struct sport_device *sport, int tdm_count,
149 u32 mask, int packed);
150
151int sport_config_rx(struct sport_device *sport,
152 unsigned int rcr1, unsigned int rcr2,
153 unsigned int clkdiv, unsigned int fsdiv);
154
155int sport_config_tx(struct sport_device *sport,
156 unsigned int tcr1, unsigned int tcr2,
157 unsigned int clkdiv, unsigned int fsdiv);
158
159/* ... then these: */
160
161/* buffer size (in bytes) == fragcount * fragsize_bytes */
162
163/* this is not a very general api, it sets the dma to 2d autobuffer mode */
164
165int sport_config_rx_dma(struct sport_device *sport, void *buf,
166 int fragcount, size_t fragsize_bytes);
167
168int sport_config_tx_dma(struct sport_device *sport, void *buf,
169 int fragcount, size_t fragsize_bytes);
170
171int sport_tx_start(struct sport_device *sport);
172int sport_tx_stop(struct sport_device *sport);
173int sport_rx_start(struct sport_device *sport);
174int sport_rx_stop(struct sport_device *sport);
175
176/* for use in interrupt handler */
177unsigned long sport_curr_offset_rx(struct sport_device *sport);
178unsigned long sport_curr_offset_tx(struct sport_device *sport);
179
180void sport_incfrag(struct sport_device *sport, int *frag, int tx);
181void sport_decfrag(struct sport_device *sport, int *frag, int tx);
182
183int sport_set_rx_callback(struct sport_device *sport,
184 void (*rx_callback)(void *), void *rx_data);
185int sport_set_tx_callback(struct sport_device *sport,
186 void (*tx_callback)(void *), void *tx_data);
187int sport_set_err_callback(struct sport_device *sport,
188 void (*err_callback)(void *), void *err_data);
189
190int sport_send_and_recv(struct sport_device *sport, u8 *out_data, \
191 u8 *in_data, int len);
192#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