1/* 2 * s3c-dma.c -- ALSA Soc Audio Layer 3 * 4 * (c) 2006 Wolfson Microelectronics PLC. 5 * Graeme Gregory graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com 6 * 7 * Copyright 2004-2005 Simtec Electronics 8 * http://armlinux.simtec.co.uk/ 9 * Ben Dooks <ben@simtec.co.uk> 10 * 11 * This program is free software; you can redistribute it and/or modify it 12 * under the terms of the GNU General Public License as published by the 13 * Free Software Foundation; either version 2 of the License, or (at your 14 * option) any later version. 15 */ 16 17#include <linux/module.h> 18#include <linux/init.h> 19#include <linux/io.h> 20#include <linux/platform_device.h> 21#include <linux/slab.h> 22#include <linux/dma-mapping.h> 23 24#include <sound/core.h> 25#include <sound/pcm.h> 26#include <sound/pcm_params.h> 27#include <sound/soc.h> 28 29#include <asm/dma.h> 30#include <mach/hardware.h> 31#include <mach/dma.h> 32 33#include "s3c-dma.h" 34 35static const struct snd_pcm_hardware s3c_dma_hardware = { 36 .info = SNDRV_PCM_INFO_INTERLEAVED | 37 SNDRV_PCM_INFO_BLOCK_TRANSFER | 38 SNDRV_PCM_INFO_MMAP | 39 SNDRV_PCM_INFO_MMAP_VALID | 40 SNDRV_PCM_INFO_PAUSE | 41 SNDRV_PCM_INFO_RESUME, 42 .formats = SNDRV_PCM_FMTBIT_S16_LE | 43 SNDRV_PCM_FMTBIT_U16_LE | 44 SNDRV_PCM_FMTBIT_U8 | 45 SNDRV_PCM_FMTBIT_S8, 46 .channels_min = 2, 47 .channels_max = 2, 48 .buffer_bytes_max = 128*1024, 49 .period_bytes_min = PAGE_SIZE, 50 .period_bytes_max = PAGE_SIZE*2, 51 .periods_min = 2, 52 .periods_max = 128, 53 .fifo_size = 32, 54}; 55 56struct s3c24xx_runtime_data { 57 spinlock_t lock; 58 int state; 59 unsigned int dma_loaded; 60 unsigned int dma_limit; 61 unsigned int dma_period; 62 dma_addr_t dma_start; 63 dma_addr_t dma_pos; 64 dma_addr_t dma_end; 65 struct s3c_dma_params *params; 66}; 67 68/* s3c_dma_enqueue 69 * 70 * place a dma buffer onto the queue for the dma system 71 * to handle. 72*/ 73static void s3c_dma_enqueue(struct snd_pcm_substream *substream) 74{ 75 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; 76 dma_addr_t pos = prtd->dma_pos; 77 unsigned int limit; 78 int ret; 79 80 pr_debug("Entered %s\n", __func__); 81 82 if (s3c_dma_has_circular()) 83 limit = (prtd->dma_end - prtd->dma_start) / prtd->dma_period; 84 else 85 limit = prtd->dma_limit; 86 87 pr_debug("%s: loaded %d, limit %d\n", 88 __func__, prtd->dma_loaded, limit); 89 90 while (prtd->dma_loaded < limit) { 91 unsigned long len = prtd->dma_period; 92 93 pr_debug("dma_loaded: %d\n", prtd->dma_loaded); 94 95 if ((pos + len) > prtd->dma_end) { 96 len = prtd->dma_end - pos; 97 pr_debug("%s: corrected dma len %ld\n", __func__, len); 98 } 99 100 ret = s3c2410_dma_enqueue(prtd->params->channel, 101 substream, pos, len); 102 103 if (ret == 0) { 104 prtd->dma_loaded++; 105 pos += prtd->dma_period; 106 if (pos >= prtd->dma_end) 107 pos = prtd->dma_start; 108 } else 109 break; 110 } 111 112 prtd->dma_pos = pos; 113} 114 115static void s3c24xx_audio_buffdone(struct s3c2410_dma_chan *channel, 116 void *dev_id, int size, 117 enum s3c2410_dma_buffresult result) 118{ 119 struct snd_pcm_substream *substream = dev_id; 120 struct s3c24xx_runtime_data *prtd; 121 122 pr_debug("Entered %s\n", __func__); 123 124 if (result == S3C2410_RES_ABORT || result == S3C2410_RES_ERR) 125 return; 126 127 prtd = substream->runtime->private_data; 128 129 if (substream) 130 snd_pcm_period_elapsed(substream); 131 132 spin_lock(&prtd->lock); 133 if (prtd->state & ST_RUNNING && !s3c_dma_has_circular()) { 134 prtd->dma_loaded--; 135 s3c_dma_enqueue(substream); 136 } 137 138 spin_unlock(&prtd->lock); 139} 140 141static int s3c_dma_hw_params(struct snd_pcm_substream *substream, 142 struct snd_pcm_hw_params *params) 143{ 144 struct snd_pcm_runtime *runtime = substream->runtime; 145 struct s3c24xx_runtime_data *prtd = runtime->private_data; 146 struct snd_soc_pcm_runtime *rtd = substream->private_data; 147 unsigned long totbytes = params_buffer_bytes(params); 148 struct s3c_dma_params *dma = 149 snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); 150 int ret = 0; 151 152 153 pr_debug("Entered %s\n", __func__); 154 155 if (!dma) 156 return 0; 157 158 /* this may get called several times by oss emulation 159 * with different params -HW */ 160 if (prtd->params == NULL) { 161 /* prepare DMA */ 162 prtd->params = dma; 163 164 pr_debug("params %p, client %p, channel %d\n", prtd->params, 165 prtd->params->client, prtd->params->channel); 166 167 ret = s3c2410_dma_request(prtd->params->channel, 168 prtd->params->client, NULL); 169 170 if (ret < 0) { 171 printk(KERN_ERR "failed to get dma channel\n"); 172 return ret; 173 } 174 175 /* use the circular buffering if we have it available. */ 176 if (s3c_dma_has_circular()) 177 s3c2410_dma_setflags(prtd->params->channel, 178 S3C2410_DMAF_CIRCULAR); 179 } 180 181 s3c2410_dma_set_buffdone_fn(prtd->params->channel, 182 s3c24xx_audio_buffdone); 183 184 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 185 186 runtime->dma_bytes = totbytes; 187 188 spin_lock_irq(&prtd->lock); 189 prtd->dma_loaded = 0; 190 prtd->dma_limit = runtime->hw.periods_min; 191 prtd->dma_period = params_period_bytes(params); 192 prtd->dma_start = runtime->dma_addr; 193 prtd->dma_pos = prtd->dma_start; 194 prtd->dma_end = prtd->dma_start + totbytes; 195 spin_unlock_irq(&prtd->lock); 196 197 return 0; 198} 199 200static int s3c_dma_hw_free(struct snd_pcm_substream *substream) 201{ 202 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; 203 204 pr_debug("Entered %s\n", __func__); 205 206 /* TODO - do we need to ensure DMA flushed */ 207 snd_pcm_set_runtime_buffer(substream, NULL); 208 209 if (prtd->params) { 210 s3c2410_dma_free(prtd->params->channel, prtd->params->client); 211 prtd->params = NULL; 212 } 213 214 return 0; 215} 216 217static int s3c_dma_prepare(struct snd_pcm_substream *substream) 218{ 219 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; 220 int ret = 0; 221 222 pr_debug("Entered %s\n", __func__); 223 224 if (!prtd->params) 225 return 0; 226 227 /* channel needs configuring for mem=>device, increment memory addr, 228 * sync to pclk, half-word transfers to the IIS-FIFO. */ 229 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 230 s3c2410_dma_devconfig(prtd->params->channel, 231 S3C2410_DMASRC_MEM, 232 prtd->params->dma_addr); 233 } else { 234 s3c2410_dma_devconfig(prtd->params->channel, 235 S3C2410_DMASRC_HW, 236 prtd->params->dma_addr); 237 } 238 239 s3c2410_dma_config(prtd->params->channel, 240 prtd->params->dma_size); 241 242 /* flush the DMA channel */ 243 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_FLUSH); 244 prtd->dma_loaded = 0; 245 prtd->dma_pos = prtd->dma_start; 246 247 /* enqueue dma buffers */ 248 s3c_dma_enqueue(substream); 249 250 return ret; 251} 252 253static int s3c_dma_trigger(struct snd_pcm_substream *substream, int cmd) 254{ 255 struct s3c24xx_runtime_data *prtd = substream->runtime->private_data; 256 int ret = 0; 257 258 pr_debug("Entered %s\n", __func__); 259 260 spin_lock(&prtd->lock); 261 262 switch (cmd) { 263 case SNDRV_PCM_TRIGGER_START: 264 case SNDRV_PCM_TRIGGER_RESUME: 265 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 266 prtd->state |= ST_RUNNING; 267 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_START); 268 break; 269 270 case SNDRV_PCM_TRIGGER_STOP: 271 case SNDRV_PCM_TRIGGER_SUSPEND: 272 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 273 prtd->state &= ~ST_RUNNING; 274 s3c2410_dma_ctrl(prtd->params->channel, S3C2410_DMAOP_STOP); 275 break; 276 277 default: 278 ret = -EINVAL; 279 break; 280 } 281 282 spin_unlock(&prtd->lock); 283 284 return ret; 285} 286 287static snd_pcm_uframes_t 288s3c_dma_pointer(struct snd_pcm_substream *substream) 289{ 290 struct snd_pcm_runtime *runtime = substream->runtime; 291 struct s3c24xx_runtime_data *prtd = runtime->private_data; 292 unsigned long res; 293 dma_addr_t src, dst; 294 295 pr_debug("Entered %s\n", __func__); 296 297 spin_lock(&prtd->lock); 298 s3c2410_dma_getposition(prtd->params->channel, &src, &dst); 299 300 if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) 301 res = dst - prtd->dma_start; 302 else 303 res = src - prtd->dma_start; 304 305 spin_unlock(&prtd->lock); 306 307 pr_debug("Pointer %x %x\n", src, dst); 308 309 /* we seem to be getting the odd error from the pcm library due 310 * to out-of-bounds pointers. this is maybe due to the dma engine 311 * not having loaded the new values for the channel before being 312 * callled... (todo - fix ) 313 */ 314 315 if (res >= snd_pcm_lib_buffer_bytes(substream)) { 316 if (res == snd_pcm_lib_buffer_bytes(substream)) 317 res = 0; 318 } 319 320 return bytes_to_frames(substream->runtime, res); 321} 322 323static int s3c_dma_open(struct snd_pcm_substream *substream) 324{ 325 struct snd_pcm_runtime *runtime = substream->runtime; 326 struct s3c24xx_runtime_data *prtd; 327 328 pr_debug("Entered %s\n", __func__); 329 330 snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS); 331 snd_soc_set_runtime_hwparams(substream, &s3c_dma_hardware); 332 333 prtd = kzalloc(sizeof(struct s3c24xx_runtime_data), GFP_KERNEL); 334 if (prtd == NULL) 335 return -ENOMEM; 336 337 spin_lock_init(&prtd->lock); 338 339 runtime->private_data = prtd; 340 return 0; 341} 342 343static int s3c_dma_close(struct snd_pcm_substream *substream) 344{ 345 struct snd_pcm_runtime *runtime = substream->runtime; 346 struct s3c24xx_runtime_data *prtd = runtime->private_data; 347 348 pr_debug("Entered %s\n", __func__); 349 350 if (!prtd) 351 pr_debug("s3c_dma_close called with prtd == NULL\n"); 352 353 kfree(prtd); 354 355 return 0; 356} 357 358static int s3c_dma_mmap(struct snd_pcm_substream *substream, 359 struct vm_area_struct *vma) 360{ 361 struct snd_pcm_runtime *runtime = substream->runtime; 362 363 pr_debug("Entered %s\n", __func__); 364 365 return dma_mmap_writecombine(substream->pcm->card->dev, vma, 366 runtime->dma_area, 367 runtime->dma_addr, 368 runtime->dma_bytes); 369} 370 371static struct snd_pcm_ops s3c_dma_ops = { 372 .open = s3c_dma_open, 373 .close = s3c_dma_close, 374 .ioctl = snd_pcm_lib_ioctl, 375 .hw_params = s3c_dma_hw_params, 376 .hw_free = s3c_dma_hw_free, 377 .prepare = s3c_dma_prepare, 378 .trigger = s3c_dma_trigger, 379 .pointer = s3c_dma_pointer, 380 .mmap = s3c_dma_mmap, 381}; 382 383static int s3c_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) 384{ 385 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 386 struct snd_dma_buffer *buf = &substream->dma_buffer; 387 size_t size = s3c_dma_hardware.buffer_bytes_max; 388 389 pr_debug("Entered %s\n", __func__); 390 391 buf->dev.type = SNDRV_DMA_TYPE_DEV; 392 buf->dev.dev = pcm->card->dev; 393 buf->private_data = NULL; 394 buf->area = dma_alloc_writecombine(pcm->card->dev, size, 395 &buf->addr, GFP_KERNEL); 396 if (!buf->area) 397 return -ENOMEM; 398 buf->bytes = size; 399 return 0; 400} 401 402static void s3c_dma_free_dma_buffers(struct snd_pcm *pcm) 403{ 404 struct snd_pcm_substream *substream; 405 struct snd_dma_buffer *buf; 406 int stream; 407 408 pr_debug("Entered %s\n", __func__); 409 410 for (stream = 0; stream < 2; stream++) { 411 substream = pcm->streams[stream].substream; 412 if (!substream) 413 continue; 414 415 buf = &substream->dma_buffer; 416 if (!buf->area) 417 continue; 418 419 dma_free_writecombine(pcm->card->dev, buf->bytes, 420 buf->area, buf->addr); 421 buf->area = NULL; 422 } 423} 424 425static u64 s3c_dma_mask = DMA_BIT_MASK(32); 426 427static int s3c_dma_new(struct snd_card *card, 428 struct snd_soc_dai *dai, struct snd_pcm *pcm) 429{ 430 int ret = 0; 431 432 pr_debug("Entered %s\n", __func__); 433 434 if (!card->dev->dma_mask) 435 card->dev->dma_mask = &s3c_dma_mask; 436 if (!card->dev->coherent_dma_mask) 437 card->dev->coherent_dma_mask = 0xffffffff; 438 439 if (dai->playback.channels_min) { 440 ret = s3c_preallocate_dma_buffer(pcm, 441 SNDRV_PCM_STREAM_PLAYBACK); 442 if (ret) 443 goto out; 444 } 445 446 if (dai->capture.channels_min) { 447 ret = s3c_preallocate_dma_buffer(pcm, 448 SNDRV_PCM_STREAM_CAPTURE); 449 if (ret) 450 goto out; 451 } 452 out: 453 return ret; 454} 455 456struct snd_soc_platform s3c24xx_soc_platform = { 457 .name = "s3c24xx-audio", 458 .pcm_ops = &s3c_dma_ops, 459 .pcm_new = s3c_dma_new, 460 .pcm_free = s3c_dma_free_dma_buffers, 461}; 462EXPORT_SYMBOL_GPL(s3c24xx_soc_platform); 463 464static int __init s3c24xx_soc_platform_init(void) 465{ 466 return snd_soc_register_platform(&s3c24xx_soc_platform); 467} 468module_init(s3c24xx_soc_platform_init); 469 470static void __exit s3c24xx_soc_platform_exit(void) 471{ 472 snd_soc_unregister_platform(&s3c24xx_soc_platform); 473} 474module_exit(s3c24xx_soc_platform_exit); 475 476MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>"); 477MODULE_DESCRIPTION("Samsung S3C Audio DMA module"); 478MODULE_LICENSE("GPL"); 479