1 /* 2 * kirkwood-dma.c 3 * 4 * (c) 2010 Arnaud Patard <apatard@mandriva.com> 5 * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org> 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the 9 * Free Software Foundation; either version 2 of the License, or (at your 10 * option) any later version. 11 */ 12 13 #include <linux/init.h> 14 #include <linux/module.h> 15 #include <linux/device.h> 16 #include <linux/io.h> 17 #include <linux/slab.h> 18 #include <linux/interrupt.h> 19 #include <linux/dma-mapping.h> 20 #include <linux/mbus.h> 21 #include <sound/soc.h> 22 #include "kirkwood.h" 23 24 #define KIRKWOOD_RATES \ 25 (SNDRV_PCM_RATE_44100 | \ 26 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000) 27 #define KIRKWOOD_FORMATS \ 28 (SNDRV_PCM_FMTBIT_S16_LE | \ 29 SNDRV_PCM_FMTBIT_S24_LE | \ 30 SNDRV_PCM_FMTBIT_S32_LE) 31 32 struct kirkwood_dma_priv { 33 struct snd_pcm_substream *play_stream; 34 struct snd_pcm_substream *rec_stream; 35 struct kirkwood_dma_data *data; 36 }; 37 38 static struct snd_pcm_hardware kirkwood_dma_snd_hw = { 39 .info = (SNDRV_PCM_INFO_INTERLEAVED | 40 SNDRV_PCM_INFO_MMAP | 41 SNDRV_PCM_INFO_MMAP_VALID | 42 SNDRV_PCM_INFO_BLOCK_TRANSFER | 43 SNDRV_PCM_INFO_PAUSE), 44 .formats = KIRKWOOD_FORMATS, 45 .rates = KIRKWOOD_RATES, 46 .rate_min = 44100, 47 .rate_max = 96000, 48 .channels_min = 1, 49 .channels_max = 2, 50 .buffer_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS, 51 .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES, 52 .period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES, 53 .periods_min = KIRKWOOD_SND_MIN_PERIODS, 54 .periods_max = KIRKWOOD_SND_MAX_PERIODS, 55 .fifo_size = 0, 56 }; 57 58 static u64 kirkwood_dma_dmamask = 0xFFFFFFFFUL; 59 60 static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id) 61 { 62 struct kirkwood_dma_priv *prdata = dev_id; 63 struct kirkwood_dma_data *priv = prdata->data; 64 unsigned long mask, status, cause; 65 66 mask = readl(priv->io + KIRKWOOD_INT_MASK); 67 status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask; 68 69 cause = readl(priv->io + KIRKWOOD_ERR_CAUSE); 70 if (unlikely(cause)) { 71 printk(KERN_WARNING "%s: got err interrupt 0x%lx\n", 72 __func__, cause); 73 writel(cause, priv->io + KIRKWOOD_ERR_CAUSE); 74 return IRQ_HANDLED; 75 } 76 77 /* we've enabled only bytes interrupts ... */ 78 if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \ 79 KIRKWOOD_INT_CAUSE_REC_BYTES)) { 80 printk(KERN_WARNING "%s: unexpected interrupt %lx\n", 81 __func__, status); 82 return IRQ_NONE; 83 } 84 85 /* ack int */ 86 writel(status, priv->io + KIRKWOOD_INT_CAUSE); 87 88 if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES) 89 snd_pcm_period_elapsed(prdata->play_stream); 90 91 if (status & KIRKWOOD_INT_CAUSE_REC_BYTES) 92 snd_pcm_period_elapsed(prdata->rec_stream); 93 94 return IRQ_HANDLED; 95 } 96 97 static void kirkwood_dma_conf_mbus_windows(void __iomem *base, int win, 98 unsigned long dma, 99 struct mbus_dram_target_info *dram) 100 { 101 int i; 102 103 /* First disable and clear windows */ 104 writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win)); 105 writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win)); 106 107 /* try to find matching cs for current dma address */ 108 for (i = 0; i < dram->num_cs; i++) { 109 struct mbus_dram_window *cs = dram->cs + i; 110 if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) { 111 writel(cs->base & 0xffff0000, 112 base + KIRKWOOD_AUDIO_WIN_BASE_REG(win)); 113 writel(((cs->size - 1) & 0xffff0000) | 114 (cs->mbus_attr << 8) | 115 (dram->mbus_dram_target_id << 4) | 1, 116 base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win)); 117 } 118 } 119 } 120 121 static int kirkwood_dma_open(struct snd_pcm_substream *substream) 122 { 123 int err; 124 struct snd_pcm_runtime *runtime = substream->runtime; 125 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; 126 struct snd_soc_platform *platform = soc_runtime->platform; 127 struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; 128 struct kirkwood_dma_data *priv; 129 struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform); 130 unsigned long addr; 131 132 priv = snd_soc_dai_get_dma_data(cpu_dai, substream); 133 snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw); 134 135 /* Ensure that all constraints linked to dma burst are fullfilled */ 136 err = snd_pcm_hw_constraint_minmax(runtime, 137 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 138 priv->burst * 2, 139 KIRKWOOD_AUDIO_BUF_MAX-1); 140 if (err < 0) 141 return err; 142 143 err = snd_pcm_hw_constraint_step(runtime, 0, 144 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 145 priv->burst); 146 if (err < 0) 147 return err; 148 149 err = snd_pcm_hw_constraint_step(substream->runtime, 0, 150 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 151 priv->burst); 152 if (err < 0) 153 return err; 154 155 if (prdata == NULL) { 156 prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL); 157 if (prdata == NULL) 158 return -ENOMEM; 159 160 prdata->data = priv; 161 162 err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED, 163 "kirkwood-i2s", prdata); 164 if (err) { 165 kfree(prdata); 166 return -EBUSY; 167 } 168 169 snd_soc_platform_set_drvdata(platform, prdata); 170 171 /* 172 * Enable Error interrupts. We're only ack'ing them but 173 * it's usefull for diagnostics 174 */ 175 writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK); 176 } 177 178 addr = virt_to_phys(substream->dma_buffer.area); 179 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 180 prdata->play_stream = substream; 181 kirkwood_dma_conf_mbus_windows(priv->io, 182 KIRKWOOD_PLAYBACK_WIN, addr, priv->dram); 183 } else { 184 prdata->rec_stream = substream; 185 kirkwood_dma_conf_mbus_windows(priv->io, 186 KIRKWOOD_RECORD_WIN, addr, priv->dram); 187 } 188 189 return 0; 190 } 191 192 static int kirkwood_dma_close(struct snd_pcm_substream *substream) 193 { 194 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; 195 struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; 196 struct snd_soc_platform *platform = soc_runtime->platform; 197 struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform); 198 struct kirkwood_dma_data *priv; 199 200 priv = snd_soc_dai_get_dma_data(cpu_dai, substream); 201 202 if (!prdata || !priv) 203 return 0; 204 205 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 206 prdata->play_stream = NULL; 207 else 208 prdata->rec_stream = NULL; 209 210 if (!prdata->play_stream && !prdata->rec_stream) { 211 writel(0, priv->io + KIRKWOOD_ERR_MASK); 212 free_irq(priv->irq, prdata); 213 kfree(prdata); 214 snd_soc_platform_set_drvdata(platform, NULL); 215 } 216 217 return 0; 218 } 219 220 static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream, 221 struct snd_pcm_hw_params *params) 222 { 223 struct snd_pcm_runtime *runtime = substream->runtime; 224 225 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 226 runtime->dma_bytes = params_buffer_bytes(params); 227 228 return 0; 229 } 230 231 static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream) 232 { 233 snd_pcm_set_runtime_buffer(substream, NULL); 234 return 0; 235 } 236 237 static int kirkwood_dma_prepare(struct snd_pcm_substream *substream) 238 { 239 struct snd_pcm_runtime *runtime = substream->runtime; 240 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; 241 struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; 242 struct kirkwood_dma_data *priv; 243 unsigned long size, count; 244 245 priv = snd_soc_dai_get_dma_data(cpu_dai, substream); 246 247 /* compute buffer size in term of "words" as requested in specs */ 248 size = frames_to_bytes(runtime, runtime->buffer_size); 249 size = (size>>2)-1; 250 count = snd_pcm_lib_period_bytes(substream); 251 252 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 253 writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT); 254 writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR); 255 writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE); 256 } else { 257 writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT); 258 writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR); 259 writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE); 260 } 261 262 263 return 0; 264 } 265 266 static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream 267 *substream) 268 { 269 struct snd_soc_pcm_runtime *soc_runtime = substream->private_data; 270 struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai; 271 struct kirkwood_dma_data *priv; 272 snd_pcm_uframes_t count; 273 274 priv = snd_soc_dai_get_dma_data(cpu_dai, substream); 275 276 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 277 count = bytes_to_frames(substream->runtime, 278 readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT)); 279 else 280 count = bytes_to_frames(substream->runtime, 281 readl(priv->io + KIRKWOOD_REC_BYTE_COUNT)); 282 283 return count; 284 } 285 286 struct snd_pcm_ops kirkwood_dma_ops = { 287 .open = kirkwood_dma_open, 288 .close = kirkwood_dma_close, 289 .ioctl = snd_pcm_lib_ioctl, 290 .hw_params = kirkwood_dma_hw_params, 291 .hw_free = kirkwood_dma_hw_free, 292 .prepare = kirkwood_dma_prepare, 293 .pointer = kirkwood_dma_pointer, 294 }; 295 296 static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm, 297 int stream) 298 { 299 struct snd_pcm_substream *substream = pcm->streams[stream].substream; 300 struct snd_dma_buffer *buf = &substream->dma_buffer; 301 size_t size = kirkwood_dma_snd_hw.buffer_bytes_max; 302 303 buf->dev.type = SNDRV_DMA_TYPE_DEV; 304 buf->dev.dev = pcm->card->dev; 305 buf->area = dma_alloc_coherent(pcm->card->dev, size, 306 &buf->addr, GFP_KERNEL); 307 if (!buf->area) 308 return -ENOMEM; 309 buf->bytes = size; 310 buf->private_data = NULL; 311 312 return 0; 313 } 314 315 static int kirkwood_dma_new(struct snd_card *card, 316 struct snd_soc_dai *dai, struct snd_pcm *pcm) 317 { 318 int ret; 319 320 if (!card->dev->dma_mask) 321 card->dev->dma_mask = &kirkwood_dma_dmamask; 322 if (!card->dev->coherent_dma_mask) 323 card->dev->coherent_dma_mask = 0xffffffff; 324 325 if (dai->driver->playback.channels_min) { 326 ret = kirkwood_dma_preallocate_dma_buffer(pcm, 327 SNDRV_PCM_STREAM_PLAYBACK); 328 if (ret) 329 return ret; 330 } 331 332 if (dai->driver->capture.channels_min) { 333 ret = kirkwood_dma_preallocate_dma_buffer(pcm, 334 SNDRV_PCM_STREAM_CAPTURE); 335 if (ret) 336 return ret; 337 } 338 339 return 0; 340 } 341 342 static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm) 343 { 344 struct snd_pcm_substream *substream; 345 struct snd_dma_buffer *buf; 346 int stream; 347 348 for (stream = 0; stream < 2; stream++) { 349 substream = pcm->streams[stream].substream; 350 if (!substream) 351 continue; 352 buf = &substream->dma_buffer; 353 if (!buf->area) 354 continue; 355 356 dma_free_coherent(pcm->card->dev, buf->bytes, 357 buf->area, buf->addr); 358 buf->area = NULL; 359 } 360 } 361 362 static struct snd_soc_platform_driver kirkwood_soc_platform = { 363 .ops = &kirkwood_dma_ops, 364 .pcm_new = kirkwood_dma_new, 365 .pcm_free = kirkwood_dma_free_dma_buffers, 366 }; 367 368 static int __devinit kirkwood_soc_platform_probe(struct platform_device *pdev) 369 { 370 return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform); 371 } 372 373 static int __devexit kirkwood_soc_platform_remove(struct platform_device *pdev) 374 { 375 snd_soc_unregister_platform(&pdev->dev); 376 return 0; 377 } 378 379 static struct platform_driver kirkwood_pcm_driver = { 380 .driver = { 381 .name = "kirkwood-pcm-audio", 382 .owner = THIS_MODULE, 383 }, 384 385 .probe = kirkwood_soc_platform_probe, 386 .remove = __devexit_p(kirkwood_soc_platform_remove), 387 }; 388 389 static int __init kirkwood_pcm_init(void) 390 { 391 return platform_driver_register(&kirkwood_pcm_driver); 392 } 393 module_init(kirkwood_pcm_init); 394 395 static void __exit kirkwood_pcm_exit(void) 396 { 397 platform_driver_unregister(&kirkwood_pcm_driver); 398 } 399 module_exit(kirkwood_pcm_exit); 400 401 MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>"); 402 MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module"); 403 MODULE_LICENSE("GPL"); 404 MODULE_ALIAS("platform:kirkwood-pcm-audio"); 405