diff options
Diffstat (limited to 'sound/soc/blackfin/bf5xx-ac97-pcm.c')
-rw-r--r-- | sound/soc/blackfin/bf5xx-ac97-pcm.c | 113 |
1 files changed, 62 insertions, 51 deletions
diff --git a/sound/soc/blackfin/bf5xx-ac97-pcm.c b/sound/soc/blackfin/bf5xx-ac97-pcm.c index 25e50d2ea1ec..8067cfafa3a7 100644 --- a/sound/soc/blackfin/bf5xx-ac97-pcm.c +++ b/sound/soc/blackfin/bf5xx-ac97-pcm.c | |||
@@ -43,24 +43,34 @@ | |||
43 | #include "bf5xx-ac97.h" | 43 | #include "bf5xx-ac97.h" |
44 | #include "bf5xx-sport.h" | 44 | #include "bf5xx-sport.h" |
45 | 45 | ||
46 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 46 | static unsigned int ac97_chan_mask[] = { |
47 | SP_FL, /* Mono */ | ||
48 | SP_STEREO, /* Stereo */ | ||
49 | SP_2DOT1, /* 2.1*/ | ||
50 | SP_QUAD,/*Quadraquic*/ | ||
51 | SP_FL | SP_FR | SP_FC | SP_SL | SP_SR,/*5 channels */ | ||
52 | SP_5DOT1, /* 5.1 */ | ||
53 | }; | ||
54 | |||
55 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
47 | static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, | 56 | static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, |
48 | snd_pcm_uframes_t count) | 57 | snd_pcm_uframes_t count) |
49 | { | 58 | { |
50 | struct snd_pcm_runtime *runtime = substream->runtime; | 59 | struct snd_pcm_runtime *runtime = substream->runtime; |
51 | struct sport_device *sport = runtime->private_data; | 60 | struct sport_device *sport = runtime->private_data; |
61 | unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1]; | ||
52 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 62 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
53 | bf5xx_pcm_to_ac97( | 63 | bf5xx_pcm_to_ac97((struct ac97_frame *)sport->tx_dma_buf + |
54 | (struct ac97_frame *)sport->tx_dma_buf + sport->tx_pos, | 64 | sport->tx_pos, (__u16 *)runtime->dma_area + sport->tx_pos * |
55 | (__u32 *)runtime->dma_area + sport->tx_pos, count); | 65 | runtime->channels, count, chan_mask); |
56 | sport->tx_pos += runtime->period_size; | 66 | sport->tx_pos += runtime->period_size; |
57 | if (sport->tx_pos >= runtime->buffer_size) | 67 | if (sport->tx_pos >= runtime->buffer_size) |
58 | sport->tx_pos %= runtime->buffer_size; | 68 | sport->tx_pos %= runtime->buffer_size; |
59 | sport->tx_delay_pos = sport->tx_pos; | 69 | sport->tx_delay_pos = sport->tx_pos; |
60 | } else { | 70 | } else { |
61 | bf5xx_ac97_to_pcm( | 71 | bf5xx_ac97_to_pcm((struct ac97_frame *)sport->rx_dma_buf + |
62 | (struct ac97_frame *)sport->rx_dma_buf + sport->rx_pos, | 72 | sport->rx_pos, (__u16 *)runtime->dma_area + sport->rx_pos * |
63 | (__u32 *)runtime->dma_area + sport->rx_pos, count); | 73 | runtime->channels, count); |
64 | sport->rx_pos += runtime->period_size; | 74 | sport->rx_pos += runtime->period_size; |
65 | if (sport->rx_pos >= runtime->buffer_size) | 75 | if (sport->rx_pos >= runtime->buffer_size) |
66 | sport->rx_pos %= runtime->buffer_size; | 76 | sport->rx_pos %= runtime->buffer_size; |
@@ -71,7 +81,7 @@ static void bf5xx_mmap_copy(struct snd_pcm_substream *substream, | |||
71 | static void bf5xx_dma_irq(void *data) | 81 | static void bf5xx_dma_irq(void *data) |
72 | { | 82 | { |
73 | struct snd_pcm_substream *pcm = data; | 83 | struct snd_pcm_substream *pcm = data; |
74 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 84 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
75 | struct snd_pcm_runtime *runtime = pcm->runtime; | 85 | struct snd_pcm_runtime *runtime = pcm->runtime; |
76 | struct sport_device *sport = runtime->private_data; | 86 | struct sport_device *sport = runtime->private_data; |
77 | bf5xx_mmap_copy(pcm, runtime->period_size); | 87 | bf5xx_mmap_copy(pcm, runtime->period_size); |
@@ -90,17 +100,14 @@ static void bf5xx_dma_irq(void *data) | |||
90 | * The total rx/tx buffer is for ac97 frame to hold all pcm data | 100 | * The total rx/tx buffer is for ac97 frame to hold all pcm data |
91 | * is 0x20000 * sizeof(struct ac97_frame) / 4. | 101 | * is 0x20000 * sizeof(struct ac97_frame) / 4. |
92 | */ | 102 | */ |
93 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
94 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | 103 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { |
95 | .info = SNDRV_PCM_INFO_INTERLEAVED | | 104 | .info = SNDRV_PCM_INFO_INTERLEAVED | |
105 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
96 | SNDRV_PCM_INFO_MMAP | | 106 | SNDRV_PCM_INFO_MMAP | |
97 | SNDRV_PCM_INFO_MMAP_VALID | | 107 | SNDRV_PCM_INFO_MMAP_VALID | |
98 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
99 | #else | ||
100 | static const struct snd_pcm_hardware bf5xx_pcm_hardware = { | ||
101 | .info = SNDRV_PCM_INFO_INTERLEAVED | | ||
102 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
103 | #endif | 108 | #endif |
109 | SNDRV_PCM_INFO_BLOCK_TRANSFER, | ||
110 | |||
104 | .formats = SNDRV_PCM_FMTBIT_S16_LE, | 111 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
105 | .period_bytes_min = 32, | 112 | .period_bytes_min = 32, |
106 | .period_bytes_max = 0x10000, | 113 | .period_bytes_max = 0x10000, |
@@ -123,10 +130,20 @@ static int bf5xx_pcm_hw_params(struct snd_pcm_substream *substream, | |||
123 | 130 | ||
124 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) | 131 | static int bf5xx_pcm_hw_free(struct snd_pcm_substream *substream) |
125 | { | 132 | { |
133 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
126 | struct snd_pcm_runtime *runtime = substream->runtime; | 134 | struct snd_pcm_runtime *runtime = substream->runtime; |
135 | struct sport_device *sport = runtime->private_data; | ||
127 | 136 | ||
128 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 137 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
129 | memset(runtime->dma_area, 0, runtime->buffer_size); | 138 | sport->once = 0; |
139 | if (runtime->dma_area) | ||
140 | memset(runtime->dma_area, 0, runtime->buffer_size); | ||
141 | memset(sport->tx_dma_buf, 0, runtime->buffer_size * | ||
142 | sizeof(struct ac97_frame)); | ||
143 | } else | ||
144 | memset(sport->rx_dma_buf, 0, runtime->buffer_size * | ||
145 | sizeof(struct ac97_frame)); | ||
146 | #endif | ||
130 | snd_pcm_lib_free_pages(substream); | 147 | snd_pcm_lib_free_pages(substream); |
131 | return 0; | 148 | return 0; |
132 | } | 149 | } |
@@ -139,7 +156,7 @@ static int bf5xx_pcm_prepare(struct snd_pcm_substream *substream) | |||
139 | /* An intermediate buffer is introduced for implementing mmap for | 156 | /* An intermediate buffer is introduced for implementing mmap for |
140 | * SPORT working in TMD mode(include AC97). | 157 | * SPORT working in TMD mode(include AC97). |
141 | */ | 158 | */ |
142 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 159 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
143 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 160 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
144 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); | 161 | sport_set_tx_callback(sport, bf5xx_dma_irq, substream); |
145 | sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, | 162 | sport_config_tx_dma(sport, sport->tx_dma_buf, runtime->periods, |
@@ -173,24 +190,24 @@ static int bf5xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) | |||
173 | switch (cmd) { | 190 | switch (cmd) { |
174 | case SNDRV_PCM_TRIGGER_START: | 191 | case SNDRV_PCM_TRIGGER_START: |
175 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 192 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
193 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) | ||
176 | bf5xx_mmap_copy(substream, runtime->period_size); | 194 | bf5xx_mmap_copy(substream, runtime->period_size); |
177 | snd_pcm_period_elapsed(substream); | ||
178 | sport->tx_delay_pos = 0; | 195 | sport->tx_delay_pos = 0; |
196 | #endif | ||
179 | sport_tx_start(sport); | 197 | sport_tx_start(sport); |
180 | } | 198 | } else |
181 | else | ||
182 | sport_rx_start(sport); | 199 | sport_rx_start(sport); |
183 | break; | 200 | break; |
184 | case SNDRV_PCM_TRIGGER_STOP: | 201 | case SNDRV_PCM_TRIGGER_STOP: |
185 | case SNDRV_PCM_TRIGGER_SUSPEND: | 202 | case SNDRV_PCM_TRIGGER_SUSPEND: |
186 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | 203 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: |
187 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 204 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
188 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 205 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
189 | sport->tx_pos = 0; | 206 | sport->tx_pos = 0; |
190 | #endif | 207 | #endif |
191 | sport_tx_stop(sport); | 208 | sport_tx_stop(sport); |
192 | } else { | 209 | } else { |
193 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 210 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
194 | sport->rx_pos = 0; | 211 | sport->rx_pos = 0; |
195 | #endif | 212 | #endif |
196 | sport_rx_stop(sport); | 213 | sport_rx_stop(sport); |
@@ -208,7 +225,7 @@ static snd_pcm_uframes_t bf5xx_pcm_pointer(struct snd_pcm_substream *substream) | |||
208 | struct sport_device *sport = runtime->private_data; | 225 | struct sport_device *sport = runtime->private_data; |
209 | unsigned int curr; | 226 | unsigned int curr; |
210 | 227 | ||
211 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 228 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
212 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 229 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
213 | curr = sport->tx_delay_pos; | 230 | curr = sport->tx_delay_pos; |
214 | else | 231 | else |
@@ -249,22 +266,7 @@ static int bf5xx_pcm_open(struct snd_pcm_substream *substream) | |||
249 | return ret; | 266 | return ret; |
250 | } | 267 | } |
251 | 268 | ||
252 | static int bf5xx_pcm_close(struct snd_pcm_substream *substream) | 269 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
253 | { | ||
254 | struct snd_pcm_runtime *runtime = substream->runtime; | ||
255 | struct sport_device *sport = runtime->private_data; | ||
256 | |||
257 | pr_debug("%s enter\n", __func__); | ||
258 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | ||
259 | sport->once = 0; | ||
260 | memset(sport->tx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); | ||
261 | } else | ||
262 | memset(sport->rx_dma_buf, 0, runtime->buffer_size * sizeof(struct ac97_frame)); | ||
263 | |||
264 | return 0; | ||
265 | } | ||
266 | |||
267 | #ifdef CONFIG_SND_MMAP_SUPPORT | ||
268 | static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, | 270 | static int bf5xx_pcm_mmap(struct snd_pcm_substream *substream, |
269 | struct vm_area_struct *vma) | 271 | struct vm_area_struct *vma) |
270 | { | 272 | { |
@@ -281,32 +283,29 @@ static int bf5xx_pcm_copy(struct snd_pcm_substream *substream, int channel, | |||
281 | void __user *buf, snd_pcm_uframes_t count) | 283 | void __user *buf, snd_pcm_uframes_t count) |
282 | { | 284 | { |
283 | struct snd_pcm_runtime *runtime = substream->runtime; | 285 | struct snd_pcm_runtime *runtime = substream->runtime; |
284 | 286 | unsigned int chan_mask = ac97_chan_mask[runtime->channels - 1]; | |
285 | pr_debug("%s copy pos:0x%lx count:0x%lx\n", | 287 | pr_debug("%s copy pos:0x%lx count:0x%lx\n", |
286 | substream->stream ? "Capture" : "Playback", pos, count); | 288 | substream->stream ? "Capture" : "Playback", pos, count); |
287 | 289 | ||
288 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 290 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
289 | bf5xx_pcm_to_ac97( | 291 | bf5xx_pcm_to_ac97((struct ac97_frame *)runtime->dma_area + pos, |
290 | (struct ac97_frame *)runtime->dma_area + pos, | 292 | (__u16 *)buf, count, chan_mask); |
291 | buf, count); | ||
292 | else | 293 | else |
293 | bf5xx_ac97_to_pcm( | 294 | bf5xx_ac97_to_pcm((struct ac97_frame *)runtime->dma_area + pos, |
294 | (struct ac97_frame *)runtime->dma_area + pos, | 295 | (__u16 *)buf, count); |
295 | buf, count); | ||
296 | return 0; | 296 | return 0; |
297 | } | 297 | } |
298 | #endif | 298 | #endif |
299 | 299 | ||
300 | struct snd_pcm_ops bf5xx_pcm_ac97_ops = { | 300 | struct snd_pcm_ops bf5xx_pcm_ac97_ops = { |
301 | .open = bf5xx_pcm_open, | 301 | .open = bf5xx_pcm_open, |
302 | .close = bf5xx_pcm_close, | ||
303 | .ioctl = snd_pcm_lib_ioctl, | 302 | .ioctl = snd_pcm_lib_ioctl, |
304 | .hw_params = bf5xx_pcm_hw_params, | 303 | .hw_params = bf5xx_pcm_hw_params, |
305 | .hw_free = bf5xx_pcm_hw_free, | 304 | .hw_free = bf5xx_pcm_hw_free, |
306 | .prepare = bf5xx_pcm_prepare, | 305 | .prepare = bf5xx_pcm_prepare, |
307 | .trigger = bf5xx_pcm_trigger, | 306 | .trigger = bf5xx_pcm_trigger, |
308 | .pointer = bf5xx_pcm_pointer, | 307 | .pointer = bf5xx_pcm_pointer, |
309 | #ifdef CONFIG_SND_MMAP_SUPPORT | 308 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
310 | .mmap = bf5xx_pcm_mmap, | 309 | .mmap = bf5xx_pcm_mmap, |
311 | #else | 310 | #else |
312 | .copy = bf5xx_pcm_copy, | 311 | .copy = bf5xx_pcm_copy, |
@@ -344,7 +343,7 @@ static int bf5xx_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) | |||
344 | * Need to allocate local buffer when enable | 343 | * Need to allocate local buffer when enable |
345 | * MMAP for SPORT working in TMD mode (include AC97). | 344 | * MMAP for SPORT working in TMD mode (include AC97). |
346 | */ | 345 | */ |
347 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 346 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
348 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 347 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
349 | if (!sport_handle->tx_dma_buf) { | 348 | if (!sport_handle->tx_dma_buf) { |
350 | sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ | 349 | sport_handle->tx_dma_buf = dma_alloc_coherent(NULL, \ |
@@ -381,7 +380,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
381 | struct snd_pcm_substream *substream; | 380 | struct snd_pcm_substream *substream; |
382 | struct snd_dma_buffer *buf; | 381 | struct snd_dma_buffer *buf; |
383 | int stream; | 382 | int stream; |
384 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 383 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
385 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max * | 384 | size_t size = bf5xx_pcm_hardware.buffer_bytes_max * |
386 | sizeof(struct ac97_frame) / 4; | 385 | sizeof(struct ac97_frame) / 4; |
387 | #endif | 386 | #endif |
@@ -395,7 +394,7 @@ static void bf5xx_pcm_free_dma_buffers(struct snd_pcm *pcm) | |||
395 | continue; | 394 | continue; |
396 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); | 395 | dma_free_coherent(NULL, buf->bytes, buf->area, 0); |
397 | buf->area = NULL; | 396 | buf->area = NULL; |
398 | #if defined(CONFIG_SND_MMAP_SUPPORT) | 397 | #if defined(CONFIG_SND_BF5XX_MMAP_SUPPORT) |
399 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { | 398 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { |
400 | if (sport_handle->tx_dma_buf) | 399 | if (sport_handle->tx_dma_buf) |
401 | dma_free_coherent(NULL, size, \ | 400 | dma_free_coherent(NULL, size, \ |
@@ -452,6 +451,18 @@ struct snd_soc_platform bf5xx_ac97_soc_platform = { | |||
452 | }; | 451 | }; |
453 | EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); | 452 | EXPORT_SYMBOL_GPL(bf5xx_ac97_soc_platform); |
454 | 453 | ||
454 | static int __init bfin_ac97_init(void) | ||
455 | { | ||
456 | return snd_soc_register_platform(&bf5xx_ac97_soc_platform); | ||
457 | } | ||
458 | module_init(bfin_ac97_init); | ||
459 | |||
460 | static void __exit bfin_ac97_exit(void) | ||
461 | { | ||
462 | snd_soc_unregister_platform(&bf5xx_ac97_soc_platform); | ||
463 | } | ||
464 | module_exit(bfin_ac97_exit); | ||
465 | |||
455 | MODULE_AUTHOR("Cliff Cai"); | 466 | MODULE_AUTHOR("Cliff Cai"); |
456 | MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); | 467 | MODULE_DESCRIPTION("ADI Blackfin AC97 PCM DMA module"); |
457 | MODULE_LICENSE("GPL"); | 468 | MODULE_LICENSE("GPL"); |