1 /* 2 * imx-pcm-dma-mx2.c -- ALSA Soc Audio Layer 3 * 4 * Copyright 2009 Sascha Hauer <s.hauer@pengutronix.de> 5 * 6 * This code is based on code copyrighted by Freescale, 7 * Liam Girdwood, Javier Martin and probably others. 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License as published by the 11 * Free Software Foundation; either version 2 of the License, or (at your 12 * option) any later version. 13 */ 14 #include <linux/clk.h> 15 #include <linux/delay.h> 16 #include <linux/device.h> 17 #include <linux/dma-mapping.h> 18 #include <linux/init.h> 19 #include <linux/interrupt.h> 20 #include <linux/module.h> 21 #include <linux/platform_device.h> 22 #include <linux/slab.h> 23 #include <linux/dmaengine.h> 24 #include <linux/types.h> 25 26 #include <sound/core.h> 27 #include <sound/initval.h> 28 #include <sound/pcm.h> 29 #include <sound/pcm_params.h> 30 #include <sound/soc.h> 31 #include <sound/dmaengine_pcm.h> 32 33 #include <mach/dma.h> 34 35 #include "imx-pcm.h" 36 37 static bool filter(struct dma_chan *chan, void *param) 38 { 39 if (!imx_dma_is_general_purpose(chan)) 40 return false; 41 42 chan->private = param; 43 44 return true; 45 } 46 47 static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, 48 struct snd_pcm_hw_params *params) 49 { 50 struct snd_soc_pcm_runtime *rtd = substream->private_data; 51 struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream); 52 struct imx_pcm_dma_params *dma_params; 53 struct dma_slave_config slave_config; 54 int ret; 55 56 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 57 58 ret = snd_hwparams_to_dma_slave_config(substream, params, &slave_config); 59 if (ret) 60 return ret; 61 62 slave_config.device_fc = false; 63 64 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 65 slave_config.dst_addr = dma_params->dma_addr; 66 slave_config.dst_maxburst = dma_params->burstsize; 67 } else { 68 slave_config.src_addr = dma_params->dma_addr; 69 slave_config.src_maxburst = dma_params->burstsize; 70 } 71 72 ret = dmaengine_slave_config(chan, &slave_config); 73 if (ret) 74 return ret; 75 76 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 77 78 return 0; 79 } 80 81 static struct snd_pcm_hardware snd_imx_hardware = { 82 .info = SNDRV_PCM_INFO_INTERLEAVED | 83 SNDRV_PCM_INFO_BLOCK_TRANSFER | 84 SNDRV_PCM_INFO_MMAP | 85 SNDRV_PCM_INFO_MMAP_VALID | 86 SNDRV_PCM_INFO_PAUSE | 87 SNDRV_PCM_INFO_RESUME, 88 .formats = SNDRV_PCM_FMTBIT_S16_LE, 89 .rate_min = 8000, 90 .channels_min = 2, 91 .channels_max = 2, 92 .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, 93 .period_bytes_min = 128, 94 .period_bytes_max = 65535, /* Limited by SDMA engine */ 95 .periods_min = 2, 96 .periods_max = 255, 97 .fifo_size = 0, 98 }; 99 100 static int snd_imx_open(struct snd_pcm_substream *substream) 101 { 102 struct snd_soc_pcm_runtime *rtd = substream->private_data; 103 struct imx_pcm_dma_params *dma_params; 104 struct imx_dma_data *dma_data; 105 int ret; 106 107 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); 108 109 dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); 110 111 dma_data = kzalloc(sizeof(*dma_data), GFP_KERNEL); 112 if (!dma_data) 113 return -ENOMEM; 114 115 dma_data->peripheral_type = dma_params->shared_peripheral ? 116 IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI; 117 dma_data->priority = DMA_PRIO_HIGH; 118 dma_data->dma_request = dma_params->dma; 119 120 ret = snd_dmaengine_pcm_open(substream, filter, dma_data); 121 if (ret) { 122 kfree(dma_data); 123 return ret; 124 } 125 126 snd_dmaengine_pcm_set_data(substream, dma_data); 127 128 return 0; 129 } 130 131 static int snd_imx_close(struct snd_pcm_substream *substream) 132 { 133 struct imx_dma_data *dma_data = snd_dmaengine_pcm_get_data(substream); 134 135 snd_dmaengine_pcm_close(substream); 136 kfree(dma_data); 137 138 return 0; 139 } 140 141 static struct snd_pcm_ops imx_pcm_ops = { 142 .open = snd_imx_open, 143 .close = snd_imx_close, 144 .ioctl = snd_pcm_lib_ioctl, 145 .hw_params = snd_imx_pcm_hw_params, 146 .trigger = snd_dmaengine_pcm_trigger, 147 .pointer = snd_dmaengine_pcm_pointer_no_residue, 148 .mmap = snd_imx_pcm_mmap, 149 }; 150 151 static struct snd_soc_platform_driver imx_soc_platform_mx2 = { 152 .ops = &imx_pcm_ops, 153 .pcm_new = imx_pcm_new, 154 .pcm_free = imx_pcm_free, 155 }; 156 157 static int __devinit imx_soc_platform_probe(struct platform_device *pdev) 158 { 159 return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2); 160 } 161 162 static int __devexit imx_soc_platform_remove(struct platform_device *pdev) 163 { 164 snd_soc_unregister_platform(&pdev->dev); 165 return 0; 166 } 167 168 static struct platform_driver imx_pcm_driver = { 169 .driver = { 170 .name = "imx-pcm-audio", 171 .owner = THIS_MODULE, 172 }, 173 .probe = imx_soc_platform_probe, 174 .remove = __devexit_p(imx_soc_platform_remove), 175 }; 176 177 module_platform_driver(imx_pcm_driver); 178 MODULE_LICENSE("GPL"); 179 MODULE_ALIAS("platform:imx-pcm-audio"); 180