~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/sound/soc/fsl/imx-pcm-fiq.c

Version: ~ [ linux-5.2-rc4 ] ~ [ linux-5.1.9 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.50 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.125 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.181 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.181 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.68 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp