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 24#include <sound/core.h> 25#include <sound/initval.h> 26#include <sound/pcm.h> 27#include <sound/pcm_params.h> 28#include <sound/soc.h> 29 30#include <mach/dma-mx1-mx2.h> 31 32#include "imx-ssi.h" 33 34struct imx_pcm_runtime_data { 35 int sg_count; 36 struct scatterlist *sg_list; 37 int period; 38 int periods; 39 unsigned long dma_addr; 40 int dma; 41 struct snd_pcm_substream *substream; 42 unsigned long offset; 43 unsigned long size; 44 unsigned long period_cnt; 45 void *buf; 46 int period_time; 47}; 48 49/* Called by the DMA framework when a period has elapsed */ 50static void imx_ssi_dma_progression(int channel, void *data, 51 struct scatterlist *sg) 52{ 53 struct snd_pcm_substream *substream = data; 54 struct snd_pcm_runtime *runtime = substream->runtime; 55 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 56 57 if (!sg) 58 return; 59 60 runtime = iprtd->substream->runtime; 61 62 iprtd->offset = sg->dma_address - runtime->dma_addr; 63 64 snd_pcm_period_elapsed(iprtd->substream); 65} 66 67static void imx_ssi_dma_callback(int channel, void *data) 68{ 69 pr_err("%s shouldn't be called\n", __func__); 70} 71 72static void snd_imx_dma_err_callback(int channel, void *data, int err) 73{ 74 struct snd_pcm_substream *substream = data; 75 struct snd_soc_pcm_runtime *rtd = substream->private_data; 76 struct imx_pcm_dma_params *dma_params = 77 snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); 78 struct snd_pcm_runtime *runtime = substream->runtime; 79 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 80 int ret; 81 82 pr_err("DMA timeout on channel %d -%s%s%s%s\n", 83 channel, 84 err & IMX_DMA_ERR_BURST ? " burst" : "", 85 err & IMX_DMA_ERR_REQUEST ? " request" : "", 86 err & IMX_DMA_ERR_TRANSFER ? " transfer" : "", 87 err & IMX_DMA_ERR_BUFFER ? " buffer" : ""); 88 89 imx_dma_disable(iprtd->dma); 90 ret = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count, 91 IMX_DMA_LENGTH_LOOP, dma_params->dma_addr, 92 substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 93 DMA_MODE_WRITE : DMA_MODE_READ); 94 if (!ret) 95 imx_dma_enable(iprtd->dma); 96} 97 98static int imx_ssi_dma_alloc(struct snd_pcm_substream *substream) 99{ 100 struct snd_soc_pcm_runtime *rtd = substream->private_data; 101 struct imx_pcm_dma_params *dma_params; 102 struct snd_pcm_runtime *runtime = substream->runtime; 103 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 104 int ret; 105 106 dma_params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); 107 108 iprtd->dma = imx_dma_request_by_prio(DRV_NAME, DMA_PRIO_HIGH); 109 if (iprtd->dma < 0) { 110 pr_err("Failed to claim the audio DMA\n"); 111 return -ENODEV; 112 } 113 114 ret = imx_dma_setup_handlers(iprtd->dma, 115 imx_ssi_dma_callback, 116 snd_imx_dma_err_callback, substream); 117 if (ret) 118 goto out; 119 120 ret = imx_dma_setup_progression_handler(iprtd->dma, 121 imx_ssi_dma_progression); 122 if (ret) { 123 pr_err("Failed to setup the DMA handler\n"); 124 goto out; 125 } 126 127 ret = imx_dma_config_channel(iprtd->dma, 128 IMX_DMA_MEMSIZE_16 | IMX_DMA_TYPE_FIFO, 129 IMX_DMA_MEMSIZE_32 | IMX_DMA_TYPE_LINEAR, 130 dma_params->dma, 1); 131 if (ret < 0) { 132 pr_err("Cannot configure DMA channel: %d\n", ret); 133 goto out; 134 } 135 136 imx_dma_config_burstlen(iprtd->dma, dma_params->burstsize * 2); 137 138 return 0; 139out: 140 imx_dma_free(iprtd->dma); 141 return ret; 142} 143 144static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, 145 struct snd_pcm_hw_params *params) 146{ 147 struct snd_pcm_runtime *runtime = substream->runtime; 148 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 149 int i; 150 unsigned long dma_addr; 151 152 imx_ssi_dma_alloc(substream); 153 154 iprtd->size = params_buffer_bytes(params); 155 iprtd->periods = params_periods(params); 156 iprtd->period = params_period_bytes(params); 157 iprtd->offset = 0; 158 iprtd->period_time = HZ / (params_rate(params) / 159 params_period_size(params)); 160 161 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 162 163 if (iprtd->sg_count != iprtd->periods) { 164 kfree(iprtd->sg_list); 165 166 iprtd->sg_list = kcalloc(iprtd->periods + 1, 167 sizeof(struct scatterlist), GFP_KERNEL); 168 if (!iprtd->sg_list) 169 return -ENOMEM; 170 iprtd->sg_count = iprtd->periods + 1; 171 } 172 173 sg_init_table(iprtd->sg_list, iprtd->sg_count); 174 dma_addr = runtime->dma_addr; 175 176 for (i = 0; i < iprtd->periods; i++) { 177 iprtd->sg_list[i].page_link = 0; 178 iprtd->sg_list[i].offset = 0; 179 iprtd->sg_list[i].dma_address = dma_addr; 180 iprtd->sg_list[i].length = iprtd->period; 181 dma_addr += iprtd->period; 182 } 183 184 /* close the loop */ 185 iprtd->sg_list[iprtd->sg_count - 1].offset = 0; 186 iprtd->sg_list[iprtd->sg_count - 1].length = 0; 187 iprtd->sg_list[iprtd->sg_count - 1].page_link = 188 ((unsigned long) iprtd->sg_list | 0x01) & ~0x02; 189 return 0; 190} 191 192static int snd_imx_pcm_hw_free(struct snd_pcm_substream *substream) 193{ 194 struct snd_pcm_runtime *runtime = substream->runtime; 195 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 196 197 if (iprtd->dma >= 0) { 198 imx_dma_free(iprtd->dma); 199 iprtd->dma = -EINVAL; 200 } 201 202 kfree(iprtd->sg_list); 203 iprtd->sg_list = NULL; 204 205 return 0; 206} 207 208static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) 209{ 210 struct snd_pcm_runtime *runtime = substream->runtime; 211 struct snd_soc_pcm_runtime *rtd = substream->private_data; 212 struct imx_pcm_dma_params *dma_params; 213 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 214 int err; 215 216 dma_params = snd_soc_dai_get_dma_data(rtd->dai->cpu_dai, substream); 217 218 iprtd->substream = substream; 219 iprtd->buf = (unsigned int *)substream->dma_buffer.area; 220 iprtd->period_cnt = 0; 221 222 pr_debug("%s: buf: %p period: %d periods: %d\n", 223 __func__, iprtd->buf, iprtd->period, iprtd->periods); 224 225 err = imx_dma_setup_sg(iprtd->dma, iprtd->sg_list, iprtd->sg_count, 226 IMX_DMA_LENGTH_LOOP, dma_params->dma_addr, 227 substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? 228 DMA_MODE_WRITE : DMA_MODE_READ); 229 if (err) 230 return err; 231 232 return 0; 233} 234 235static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 236{ 237 struct snd_pcm_runtime *runtime = substream->runtime; 238 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 239 240 switch (cmd) { 241 case SNDRV_PCM_TRIGGER_START: 242 case SNDRV_PCM_TRIGGER_RESUME: 243 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 244 imx_dma_enable(iprtd->dma); 245 246 break; 247 248 case SNDRV_PCM_TRIGGER_STOP: 249 case SNDRV_PCM_TRIGGER_SUSPEND: 250 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 251 imx_dma_disable(iprtd->dma); 252 253 break; 254 default: 255 return -EINVAL; 256 } 257 258 return 0; 259} 260 261static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) 262{ 263 struct snd_pcm_runtime *runtime = substream->runtime; 264 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 265 266 return bytes_to_frames(substream->runtime, iprtd->offset); 267} 268 269static struct snd_pcm_hardware snd_imx_hardware = { 270 .info = SNDRV_PCM_INFO_INTERLEAVED | 271 SNDRV_PCM_INFO_BLOCK_TRANSFER | 272 SNDRV_PCM_INFO_MMAP | 273 SNDRV_PCM_INFO_MMAP_VALID | 274 SNDRV_PCM_INFO_PAUSE | 275 SNDRV_PCM_INFO_RESUME, 276 .formats = SNDRV_PCM_FMTBIT_S16_LE, 277 .rate_min = 8000, 278 .channels_min = 2, 279 .channels_max = 2, 280 .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, 281 .period_bytes_min = 128, 282 .period_bytes_max = 16 * 1024, 283 .periods_min = 2, 284 .periods_max = 255, 285 .fifo_size = 0, 286}; 287 288static int snd_imx_open(struct snd_pcm_substream *substream) 289{ 290 struct snd_pcm_runtime *runtime = substream->runtime; 291 struct imx_pcm_runtime_data *iprtd; 292 int ret; 293 294 iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); 295 if (iprtd == NULL) 296 return -ENOMEM; 297 runtime->private_data = iprtd; 298 299 ret = snd_pcm_hw_constraint_integer(substream->runtime, 300 SNDRV_PCM_HW_PARAM_PERIODS); 301 if (ret < 0) { 302 kfree(iprtd); 303 return ret; 304 } 305 306 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); 307 return 0; 308} 309 310static struct snd_pcm_ops imx_pcm_ops = { 311 .open = snd_imx_open, 312 .ioctl = snd_pcm_lib_ioctl, 313 .hw_params = snd_imx_pcm_hw_params, 314 .hw_free = snd_imx_pcm_hw_free, 315 .prepare = snd_imx_pcm_prepare, 316 .trigger = snd_imx_pcm_trigger, 317 .pointer = snd_imx_pcm_pointer, 318 .mmap = snd_imx_pcm_mmap, 319}; 320 321static struct snd_soc_platform imx_soc_platform_dma = { 322 .name = "imx-audio", 323 .pcm_ops = &imx_pcm_ops, 324 .pcm_new = imx_pcm_new, 325 .pcm_free = imx_pcm_free, 326}; 327 328struct snd_soc_platform *imx_ssi_dma_mx2_init(struct platform_device *pdev, 329 struct imx_ssi *ssi) 330{ 331 ssi->dma_params_tx.burstsize = DMA_TXFIFO_BURST; 332 ssi->dma_params_rx.burstsize = DMA_RXFIFO_BURST; 333 334 return &imx_soc_platform_dma; 335} 336