1 /* 2 * imx-pcm-fiq.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 <asm/fiq.h> 31 32 #include <mach/irqs.h> 33 #include <mach/ssi.h> 34 35 #include "imx-ssi.h" 36 37 struct imx_pcm_runtime_data { 38 int period; 39 int periods; 40 unsigned long offset; 41 unsigned long last_offset; 42 unsigned long size; 43 struct hrtimer hrt; 44 int poll_time_ns; 45 struct snd_pcm_substream *substream; 46 atomic_t running; 47 }; 48 49 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt) 50 { 51 struct imx_pcm_runtime_data *iprtd = 52 container_of(hrt, struct imx_pcm_runtime_data, hrt); 53 struct snd_pcm_substream *substream = iprtd->substream; 54 struct snd_pcm_runtime *runtime = substream->runtime; 55 struct pt_regs regs; 56 unsigned long delta; 57 58 if (!atomic_read(&iprtd->running)) 59 return HRTIMER_NORESTART; 60 61 get_fiq_regs(®s); 62 63 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 64 iprtd->offset = regs.ARM_r8 & 0xffff; 65 else 66 iprtd->offset = regs.ARM_r9 & 0xffff; 67 68 /* How much data have we transferred since the last period report? */ 69 if (iprtd->offset >= iprtd->last_offset) 70 delta = iprtd->offset - iprtd->last_offset; 71 else 72 delta = runtime->buffer_size + iprtd->offset 73 - iprtd->last_offset; 74 75 /* If we've transferred at least a period then report it and 76 * reset our poll time */ 77 if (delta >= iprtd->period) { 78 snd_pcm_period_elapsed(substream); 79 iprtd->last_offset = iprtd->offset; 80 } 81 82 hrtimer_forward_now(hrt, ns_to_ktime(iprtd->poll_time_ns)); 83 84 return HRTIMER_RESTART; 85 } 86 87 static struct fiq_handler fh = { 88 .name = DRV_NAME, 89 }; 90 91 static int snd_imx_pcm_hw_params(struct snd_pcm_substream *substream, 92 struct snd_pcm_hw_params *params) 93 { 94 struct snd_pcm_runtime *runtime = substream->runtime; 95 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 96 97 iprtd->size = params_buffer_bytes(params); 98 iprtd->periods = params_periods(params); 99 iprtd->period = params_period_bytes(params) ; 100 iprtd->offset = 0; 101 iprtd->last_offset = 0; 102 iprtd->poll_time_ns = 1000000000 / params_rate(params) * 103 params_period_size(params); 104 snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); 105 106 return 0; 107 } 108 109 static int snd_imx_pcm_prepare(struct snd_pcm_substream *substream) 110 { 111 struct snd_pcm_runtime *runtime = substream->runtime; 112 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 113 struct pt_regs regs; 114 115 get_fiq_regs(®s); 116 if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 117 regs.ARM_r8 = (iprtd->period * iprtd->periods - 1) << 16; 118 else 119 regs.ARM_r9 = (iprtd->period * iprtd->periods - 1) << 16; 120 121 set_fiq_regs(®s); 122 123 return 0; 124 } 125 126 static int fiq_enable; 127 static int imx_pcm_fiq; 128 129 static int snd_imx_pcm_trigger(struct snd_pcm_substream *substream, int cmd) 130 { 131 struct snd_pcm_runtime *runtime = substream->runtime; 132 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 133 134 switch (cmd) { 135 case SNDRV_PCM_TRIGGER_START: 136 case SNDRV_PCM_TRIGGER_RESUME: 137 case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: 138 atomic_set(&iprtd->running, 1); 139 hrtimer_start(&iprtd->hrt, ns_to_ktime(iprtd->poll_time_ns), 140 HRTIMER_MODE_REL); 141 if (++fiq_enable == 1) 142 enable_fiq(imx_pcm_fiq); 143 144 break; 145 146 case SNDRV_PCM_TRIGGER_STOP: 147 case SNDRV_PCM_TRIGGER_SUSPEND: 148 case SNDRV_PCM_TRIGGER_PAUSE_PUSH: 149 atomic_set(&iprtd->running, 0); 150 151 if (--fiq_enable == 0) 152 disable_fiq(imx_pcm_fiq); 153 154 break; 155 default: 156 return -EINVAL; 157 } 158 159 return 0; 160 } 161 162 static snd_pcm_uframes_t snd_imx_pcm_pointer(struct snd_pcm_substream *substream) 163 { 164 struct snd_pcm_runtime *runtime = substream->runtime; 165 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 166 167 return bytes_to_frames(substream->runtime, iprtd->offset); 168 } 169 170 static struct snd_pcm_hardware snd_imx_hardware = { 171 .info = SNDRV_PCM_INFO_INTERLEAVED | 172 SNDRV_PCM_INFO_BLOCK_TRANSFER | 173 SNDRV_PCM_INFO_MMAP | 174 SNDRV_PCM_INFO_MMAP_VALID | 175 SNDRV_PCM_INFO_PAUSE | 176 SNDRV_PCM_INFO_RESUME, 177 .formats = SNDRV_PCM_FMTBIT_S16_LE, 178 .rate_min = 8000, 179 .channels_min = 2, 180 .channels_max = 2, 181 .buffer_bytes_max = IMX_SSI_DMABUF_SIZE, 182 .period_bytes_min = 128, 183 .period_bytes_max = 16 * 1024, 184 .periods_min = 4, 185 .periods_max = 255, 186 .fifo_size = 0, 187 }; 188 189 static int snd_imx_open(struct snd_pcm_substream *substream) 190 { 191 struct snd_pcm_runtime *runtime = substream->runtime; 192 struct imx_pcm_runtime_data *iprtd; 193 int ret; 194 195 iprtd = kzalloc(sizeof(*iprtd), GFP_KERNEL); 196 if (iprtd == NULL) 197 return -ENOMEM; 198 runtime->private_data = iprtd; 199 200 iprtd->substream = substream; 201 202 atomic_set(&iprtd->running, 0); 203 hrtimer_init(&iprtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL); 204 iprtd->hrt.function = snd_hrtimer_callback; 205 206 ret = snd_pcm_hw_constraint_integer(substream->runtime, 207 SNDRV_PCM_HW_PARAM_PERIODS); 208 if (ret < 0) { 209 kfree(iprtd); 210 return ret; 211 } 212 213 snd_soc_set_runtime_hwparams(substream, &snd_imx_hardware); 214 return 0; 215 } 216 217 static int snd_imx_close(struct snd_pcm_substream *substream) 218 { 219 struct snd_pcm_runtime *runtime = substream->runtime; 220 struct imx_pcm_runtime_data *iprtd = runtime->private_data; 221 222 hrtimer_cancel(&iprtd->hrt); 223 224 kfree(iprtd); 225 226 return 0; 227 } 228 229 static struct snd_pcm_ops imx_pcm_ops = { 230 .open = snd_imx_open, 231 .close = snd_imx_close, 232 .ioctl = snd_pcm_lib_ioctl, 233 .hw_params = snd_imx_pcm_hw_params, 234 .prepare = snd_imx_pcm_prepare, 235 .trigger = snd_imx_pcm_trigger, 236 .pointer = snd_imx_pcm_pointer, 237 .mmap = snd_imx_pcm_mmap, 238 }; 239 240 static int ssi_irq = 0; 241 242 static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd) 243 { 244 struct snd_pcm *pcm = rtd->pcm; 245 struct snd_pcm_substream *substream; 246 int ret; 247 248 ret = imx_pcm_new(rtd); 249 if (ret) 250 return ret; 251 252 substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; 253 if (substream) { 254 struct snd_dma_buffer *buf = &substream->dma_buffer; 255 256 imx_ssi_fiq_tx_buffer = (unsigned long)buf->area; 257 } 258 259 substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; 260 if (substream) { 261 struct snd_dma_buffer *buf = &substream->dma_buffer; 262 263 imx_ssi_fiq_rx_buffer = (unsigned long)buf->area; 264 } 265 266 set_fiq_handler(&imx_ssi_fiq_start, 267 &imx_ssi_fiq_end - &imx_ssi_fiq_start); 268 269 return 0; 270 } 271 272 static void imx_pcm_fiq_free(struct snd_pcm *pcm) 273 { 274 mxc_set_irq_fiq(ssi_irq, 0); 275 release_fiq(&fh); 276 imx_pcm_free(pcm); 277 } 278 279 static struct snd_soc_platform_driver imx_soc_platform_fiq = { 280 .ops = &imx_pcm_ops, 281 .pcm_new = imx_pcm_fiq_new, 282 .pcm_free = imx_pcm_fiq_free, 283 }; 284 285 static int __devinit imx_soc_platform_probe(struct platform_device *pdev) 286 { 287 struct imx_ssi *ssi = platform_get_drvdata(pdev); 288 int ret; 289 290 ret = claim_fiq(&fh); 291 if (ret) { 292 dev_err(&pdev->dev, "failed to claim fiq: %d", ret); 293 return ret; 294 } 295 296 mxc_set_irq_fiq(ssi->irq, 1); 297 ssi_irq = ssi->irq; 298 299 imx_pcm_fiq = ssi->irq; 300 301 imx_ssi_fiq_base = (unsigned long)ssi->base; 302 303 ssi->dma_params_tx.burstsize = 4; 304 ssi->dma_params_rx.burstsize = 6; 305 306 ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq); 307 if (ret) 308 goto failed_register; 309 310 return 0; 311 312 failed_register: 313 mxc_set_irq_fiq(ssi_irq, 0); 314 release_fiq(&fh); 315 316 return ret; 317 } 318 319 static int __devexit imx_soc_platform_remove(struct platform_device *pdev) 320 { 321 snd_soc_unregister_platform(&pdev->dev); 322 return 0; 323 } 324 325 static struct platform_driver imx_pcm_driver = { 326 .driver = { 327 .name = "imx-fiq-pcm-audio", 328 .owner = THIS_MODULE, 329 }, 330 331 .probe = imx_soc_platform_probe, 332 .remove = __devexit_p(imx_soc_platform_remove), 333 }; 334 335 module_platform_driver(imx_pcm_driver); 336 337 MODULE_LICENSE("GPL"); 338