Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
kirkwood-dma.c
Go to the documentation of this file.
1 /*
2  * kirkwood-dma.c
3  *
4  * (c) 2010 Arnaud Patard <[email protected]>
5  * (c) 2010 Arnaud Patard <[email protected]>
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 
36 };
37 
38 static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
46  .rate_min = 44100,
47  .rate_max = 96000,
48  .channels_min = 1,
49  .channels_max = 2,
54  .periods_max = KIRKWOOD_SND_MAX_PERIODS,
55  .fifo_size = 0,
56 };
57 
58 static u64 kirkwood_dma_dmamask = DMA_BIT_MASK(32);
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  }
75 
76  /* we've enabled only bytes interrupts ... */
77  if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
79  printk(KERN_WARNING "%s: unexpected interrupt %lx\n",
80  __func__, status);
81  return IRQ_NONE;
82  }
83 
84  /* ack int */
85  writel(status, priv->io + KIRKWOOD_INT_CAUSE);
86 
87  if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
89 
90  if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
92 
93  return IRQ_HANDLED;
94 }
95 
96 static void
97 kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
98  unsigned long dma,
99  const 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  const 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  const struct mbus_dram_target_info *dram;
131  unsigned long addr;
132 
133  priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
134  snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
135 
136  /* Ensure that all constraints linked to dma burst are fulfilled */
137  err = snd_pcm_hw_constraint_minmax(runtime,
139  priv->burst * 2,
141  if (err < 0)
142  return err;
143 
144  err = snd_pcm_hw_constraint_step(runtime, 0,
146  priv->burst);
147  if (err < 0)
148  return err;
149 
150  err = snd_pcm_hw_constraint_step(substream->runtime, 0,
152  priv->burst);
153  if (err < 0)
154  return err;
155 
156  if (prdata == NULL) {
157  prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL);
158  if (prdata == NULL)
159  return -ENOMEM;
160 
161  prdata->data = priv;
162 
163  err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
164  "kirkwood-i2s", prdata);
165  if (err) {
166  kfree(prdata);
167  return -EBUSY;
168  }
169 
170  snd_soc_platform_set_drvdata(platform, prdata);
171 
172  /*
173  * Enable Error interrupts. We're only ack'ing them but
174  * it's useful for diagnostics
175  */
176  writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK);
177  }
178 
179  dram = mv_mbus_dram_info();
180  addr = substream->dma_buffer.addr;
181  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
182  prdata->play_stream = substream;
183  kirkwood_dma_conf_mbus_windows(priv->io,
184  KIRKWOOD_PLAYBACK_WIN, addr, dram);
185  } else {
186  prdata->rec_stream = substream;
187  kirkwood_dma_conf_mbus_windows(priv->io,
188  KIRKWOOD_RECORD_WIN, addr, dram);
189  }
190 
191  return 0;
192 }
193 
194 static int kirkwood_dma_close(struct snd_pcm_substream *substream)
195 {
196  struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
197  struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
198  struct snd_soc_platform *platform = soc_runtime->platform;
199  struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
200  struct kirkwood_dma_data *priv;
201 
202  priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
203 
204  if (!prdata || !priv)
205  return 0;
206 
207  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
208  prdata->play_stream = NULL;
209  else
210  prdata->rec_stream = NULL;
211 
212  if (!prdata->play_stream && !prdata->rec_stream) {
213  writel(0, priv->io + KIRKWOOD_ERR_MASK);
214  free_irq(priv->irq, prdata);
215  kfree(prdata);
216  snd_soc_platform_set_drvdata(platform, NULL);
217  }
218 
219  return 0;
220 }
221 
222 static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
223  struct snd_pcm_hw_params *params)
224 {
225  struct snd_pcm_runtime *runtime = substream->runtime;
226 
227  snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
228  runtime->dma_bytes = params_buffer_bytes(params);
229 
230  return 0;
231 }
232 
233 static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
234 {
235  snd_pcm_set_runtime_buffer(substream, NULL);
236  return 0;
237 }
238 
239 static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
240 {
241  struct snd_pcm_runtime *runtime = substream->runtime;
242  struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
243  struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
244  struct kirkwood_dma_data *priv;
245  unsigned long size, count;
246 
247  priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
248 
249  /* compute buffer size in term of "words" as requested in specs */
250  size = frames_to_bytes(runtime, runtime->buffer_size);
251  size = (size>>2)-1;
252  count = snd_pcm_lib_period_bytes(substream);
253 
254  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
255  writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
256  writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
257  writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
258  } else {
259  writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
260  writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
261  writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
262  }
263 
264 
265  return 0;
266 }
267 
268 static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
269  *substream)
270 {
271  struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
272  struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
273  struct kirkwood_dma_data *priv;
275 
276  priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
277 
278  if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
279  count = bytes_to_frames(substream->runtime,
281  else
282  count = bytes_to_frames(substream->runtime,
283  readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
284 
285  return count;
286 }
287 
289  .open = kirkwood_dma_open,
290  .close = kirkwood_dma_close,
291  .ioctl = snd_pcm_lib_ioctl,
292  .hw_params = kirkwood_dma_hw_params,
293  .hw_free = kirkwood_dma_hw_free,
294  .prepare = kirkwood_dma_prepare,
295  .pointer = kirkwood_dma_pointer,
296 };
297 
298 static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
299  int stream)
300 {
301  struct snd_pcm_substream *substream = pcm->streams[stream].substream;
302  struct snd_dma_buffer *buf = &substream->dma_buffer;
303  size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
304 
305  buf->dev.type = SNDRV_DMA_TYPE_DEV;
306  buf->dev.dev = pcm->card->dev;
307  buf->area = dma_alloc_coherent(pcm->card->dev, size,
308  &buf->addr, GFP_KERNEL);
309  if (!buf->area)
310  return -ENOMEM;
311  buf->bytes = size;
312  buf->private_data = NULL;
313 
314  return 0;
315 }
316 
317 static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
318 {
319  struct snd_card *card = rtd->card->snd_card;
320  struct snd_pcm *pcm = rtd->pcm;
321  int ret;
322 
323  if (!card->dev->dma_mask)
324  card->dev->dma_mask = &kirkwood_dma_dmamask;
325  if (!card->dev->coherent_dma_mask)
326  card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
327 
328  if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
329  ret = kirkwood_dma_preallocate_dma_buffer(pcm,
331  if (ret)
332  return ret;
333  }
334 
335  if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
336  ret = kirkwood_dma_preallocate_dma_buffer(pcm,
338  if (ret)
339  return ret;
340  }
341 
342  return 0;
343 }
344 
345 static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
346 {
347  struct snd_pcm_substream *substream;
348  struct snd_dma_buffer *buf;
349  int stream;
350 
351  for (stream = 0; stream < 2; stream++) {
352  substream = pcm->streams[stream].substream;
353  if (!substream)
354  continue;
355  buf = &substream->dma_buffer;
356  if (!buf->area)
357  continue;
358 
359  dma_free_coherent(pcm->card->dev, buf->bytes,
360  buf->area, buf->addr);
361  buf->area = NULL;
362  }
363 }
364 
365 static struct snd_soc_platform_driver kirkwood_soc_platform = {
366  .ops = &kirkwood_dma_ops,
367  .pcm_new = kirkwood_dma_new,
368  .pcm_free = kirkwood_dma_free_dma_buffers,
369 };
370 
371 static int __devinit kirkwood_soc_platform_probe(struct platform_device *pdev)
372 {
373  return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
374 }
375 
376 static int __devexit kirkwood_soc_platform_remove(struct platform_device *pdev)
377 {
379  return 0;
380 }
381 
382 static struct platform_driver kirkwood_pcm_driver = {
383  .driver = {
384  .name = "kirkwood-pcm-audio",
385  .owner = THIS_MODULE,
386  },
387 
388  .probe = kirkwood_soc_platform_probe,
389  .remove = __devexit_p(kirkwood_soc_platform_remove),
390 };
391 
392 module_platform_driver(kirkwood_pcm_driver);
393 
394 MODULE_AUTHOR("Arnaud Patard <[email protected]>");
395 MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
396 MODULE_LICENSE("GPL");
397 MODULE_ALIAS("platform:kirkwood-pcm-audio");