Blame view

sound/soc/kirkwood/kirkwood-dma.c 10.8 KB
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
1
2
3
4
  /*
   * kirkwood-dma.c
   *
   * (c) 2010 Arnaud Patard <apatard@mandriva.com>
697378972   Arnaud Patard   ASoC: Change my m...
5
   * (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
   *
   *  This program is free software; you can redistribute  it and/or modify it
   *  under  the terms of  the GNU General  Public License as published by the
   *  Free Software Foundation;  either version 2 of the  License, or (at your
   *  option) any later version.
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/io.h>
  #include <linux/slab.h>
  #include <linux/interrupt.h>
  #include <linux/dma-mapping.h>
  #include <linux/mbus.h>
  #include <sound/soc.h>
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
  #include "kirkwood.h"
  
  #define KIRKWOOD_RATES \
  	(SNDRV_PCM_RATE_44100 | \
  	 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
  #define KIRKWOOD_FORMATS \
  	(SNDRV_PCM_FMTBIT_S16_LE | \
  	 SNDRV_PCM_FMTBIT_S24_LE | \
  	 SNDRV_PCM_FMTBIT_S32_LE)
  
  struct kirkwood_dma_priv {
  	struct snd_pcm_substream *play_stream;
  	struct snd_pcm_substream *rec_stream;
  	struct kirkwood_dma_data *data;
  };
  
  static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
  	.info = (SNDRV_PCM_INFO_INTERLEAVED |
  		 SNDRV_PCM_INFO_MMAP |
  		 SNDRV_PCM_INFO_MMAP_VALID |
  		 SNDRV_PCM_INFO_BLOCK_TRANSFER |
  		 SNDRV_PCM_INFO_PAUSE),
  	.formats		= KIRKWOOD_FORMATS,
  	.rates			= KIRKWOOD_RATES,
  	.rate_min		= 44100,
  	.rate_max		= 96000,
  	.channels_min		= 1,
  	.channels_max		= 2,
  	.buffer_bytes_max	= KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS,
  	.period_bytes_min	= KIRKWOOD_SND_MIN_PERIOD_BYTES,
  	.period_bytes_max	= KIRKWOOD_SND_MAX_PERIOD_BYTES,
  	.periods_min		= KIRKWOOD_SND_MIN_PERIODS,
  	.periods_max		= KIRKWOOD_SND_MAX_PERIODS,
  	.fifo_size		= 0,
  };
  
  static u64 kirkwood_dma_dmamask = 0xFFFFFFFFUL;
  
  static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
  {
  	struct kirkwood_dma_priv *prdata = dev_id;
  	struct kirkwood_dma_data *priv = prdata->data;
  	unsigned long mask, status, cause;
  
  	mask = readl(priv->io + KIRKWOOD_INT_MASK);
  	status = readl(priv->io + KIRKWOOD_INT_CAUSE) & mask;
  
  	cause = readl(priv->io + KIRKWOOD_ERR_CAUSE);
  	if (unlikely(cause)) {
  		printk(KERN_WARNING "%s: got err interrupt 0x%lx
  ",
  				__func__, cause);
  		writel(cause, priv->io + KIRKWOOD_ERR_CAUSE);
  		return IRQ_HANDLED;
  	}
  
  	/* we've enabled only bytes interrupts ... */
  	if (status & ~(KIRKWOOD_INT_CAUSE_PLAY_BYTES | \
  			KIRKWOOD_INT_CAUSE_REC_BYTES)) {
  		printk(KERN_WARNING "%s: unexpected interrupt %lx
  ",
  			__func__, status);
  		return IRQ_NONE;
  	}
  
  	/* ack int */
  	writel(status, priv->io + KIRKWOOD_INT_CAUSE);
  
  	if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
  		snd_pcm_period_elapsed(prdata->play_stream);
  
  	if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
  		snd_pcm_period_elapsed(prdata->rec_stream);
  
  	return IRQ_HANDLED;
  }
63a9332b2   Andrew Lunn   ARM: Orion: Get a...
98
99
100
101
  static void
  kirkwood_dma_conf_mbus_windows(void __iomem *base, int win,
  			       unsigned long dma,
  			       const struct mbus_dram_target_info *dram)
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
102
103
104
105
106
107
108
109
110
  {
  	int i;
  
  	/* First disable and clear windows */
  	writel(0, base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
  	writel(0, base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
  
  	/* try to find matching cs for current dma address */
  	for (i = 0; i < dram->num_cs; i++) {
63a9332b2   Andrew Lunn   ARM: Orion: Get a...
111
  		const struct mbus_dram_window *cs = dram->cs + i;
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
  		if ((cs->base & 0xffff0000) < (dma & 0xffff0000)) {
  			writel(cs->base & 0xffff0000,
  				base + KIRKWOOD_AUDIO_WIN_BASE_REG(win));
  			writel(((cs->size - 1) & 0xffff0000) |
  				(cs->mbus_attr << 8) |
  				(dram->mbus_dram_target_id << 4) | 1,
  				base + KIRKWOOD_AUDIO_WIN_CTRL_REG(win));
  		}
  	}
  }
  
  static int kirkwood_dma_open(struct snd_pcm_substream *substream)
  {
  	int err;
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
128
129
  	struct snd_soc_platform *platform = soc_runtime->platform;
  	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
130
  	struct kirkwood_dma_data *priv;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
131
  	struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
63a9332b2   Andrew Lunn   ARM: Orion: Get a...
132
  	const struct mbus_dram_target_info *dram;
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
133
134
135
136
  	unsigned long addr;
  
  	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
  	snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
25985edce   Lucas De Marchi   Fix common misspe...
137
  	/* Ensure that all constraints linked to dma burst are fulfilled */
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
  	err = snd_pcm_hw_constraint_minmax(runtime,
  			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
  			priv->burst * 2,
  			KIRKWOOD_AUDIO_BUF_MAX-1);
  	if (err < 0)
  		return err;
  
  	err = snd_pcm_hw_constraint_step(runtime, 0,
  			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
  			priv->burst);
  	if (err < 0)
  		return err;
  
  	err = snd_pcm_hw_constraint_step(substream->runtime, 0,
  			 SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
  			 priv->burst);
  	if (err < 0)
  		return err;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
156
  	if (prdata == NULL) {
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
157
158
159
160
161
162
163
164
165
166
167
168
  		prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL);
  		if (prdata == NULL)
  			return -ENOMEM;
  
  		prdata->data = priv;
  
  		err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
  				  "kirkwood-i2s", prdata);
  		if (err) {
  			kfree(prdata);
  			return -EBUSY;
  		}
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
169
  		snd_soc_platform_set_drvdata(platform, prdata);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
170
171
172
  
  		/*
  		 * Enable Error interrupts. We're only ack'ing them but
25985edce   Lucas De Marchi   Fix common misspe...
173
  		 * it's useful for diagnostics
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
174
175
176
  		 */
  		writel((unsigned long)-1, priv->io + KIRKWOOD_ERR_MASK);
  	}
63a9332b2   Andrew Lunn   ARM: Orion: Get a...
177
  	dram = mv_mbus_dram_info();
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
178
179
180
181
  	addr = virt_to_phys(substream->dma_buffer.area);
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		prdata->play_stream = substream;
  		kirkwood_dma_conf_mbus_windows(priv->io,
63a9332b2   Andrew Lunn   ARM: Orion: Get a...
182
  			KIRKWOOD_PLAYBACK_WIN, addr, dram);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
183
184
185
  	} else {
  		prdata->rec_stream = substream;
  		kirkwood_dma_conf_mbus_windows(priv->io,
63a9332b2   Andrew Lunn   ARM: Orion: Get a...
186
  			KIRKWOOD_RECORD_WIN, addr, dram);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
187
188
189
190
191
192
193
194
  	}
  
  	return 0;
  }
  
  static int kirkwood_dma_close(struct snd_pcm_substream *substream)
  {
  	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
195
196
197
  	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
  	struct snd_soc_platform *platform = soc_runtime->platform;
  	struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  	struct kirkwood_dma_data *priv;
  
  	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
  
  	if (!prdata || !priv)
  		return 0;
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		prdata->play_stream = NULL;
  	else
  		prdata->rec_stream = NULL;
  
  	if (!prdata->play_stream && !prdata->rec_stream) {
  		writel(0, priv->io + KIRKWOOD_ERR_MASK);
  		free_irq(priv->irq, prdata);
  		kfree(prdata);
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
214
  		snd_soc_platform_set_drvdata(platform, NULL);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
  	}
  
  	return 0;
  }
  
  static int kirkwood_dma_hw_params(struct snd_pcm_substream *substream,
  				struct snd_pcm_hw_params *params)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  
  	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
  	runtime->dma_bytes = params_buffer_bytes(params);
  
  	return 0;
  }
  
  static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
  {
  	snd_pcm_set_runtime_buffer(substream, NULL);
  	return 0;
  }
  
  static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
  {
  	struct snd_pcm_runtime *runtime = substream->runtime;
  	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
241
  	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  	struct kirkwood_dma_data *priv;
  	unsigned long size, count;
  
  	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
  
  	/* compute buffer size in term of "words" as requested in specs */
  	size = frames_to_bytes(runtime, runtime->buffer_size);
  	size = (size>>2)-1;
  	count = snd_pcm_lib_period_bytes(substream);
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
  		writel(count, priv->io + KIRKWOOD_PLAY_BYTE_INT_COUNT);
  		writel(runtime->dma_addr, priv->io + KIRKWOOD_PLAY_BUF_ADDR);
  		writel(size, priv->io + KIRKWOOD_PLAY_BUF_SIZE);
  	} else {
  		writel(count, priv->io + KIRKWOOD_REC_BYTE_INT_COUNT);
  		writel(runtime->dma_addr, priv->io + KIRKWOOD_REC_BUF_ADDR);
  		writel(size, priv->io + KIRKWOOD_REC_BUF_SIZE);
  	}
  
  
  	return 0;
  }
  
  static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
  						*substream)
  {
  	struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
270
  	struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
  	struct kirkwood_dma_data *priv;
  	snd_pcm_uframes_t count;
  
  	priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
  
  	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
  		count = bytes_to_frames(substream->runtime,
  			readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
  	else
  		count = bytes_to_frames(substream->runtime,
  			readl(priv->io + KIRKWOOD_REC_BYTE_COUNT));
  
  	return count;
  }
  
  struct snd_pcm_ops kirkwood_dma_ops = {
  	.open =		kirkwood_dma_open,
  	.close =        kirkwood_dma_close,
  	.ioctl =	snd_pcm_lib_ioctl,
  	.hw_params =	kirkwood_dma_hw_params,
  	.hw_free =      kirkwood_dma_hw_free,
  	.prepare =      kirkwood_dma_prepare,
  	.pointer =	kirkwood_dma_pointer,
  };
  
  static int kirkwood_dma_preallocate_dma_buffer(struct snd_pcm *pcm,
  		int stream)
  {
  	struct snd_pcm_substream *substream = pcm->streams[stream].substream;
  	struct snd_dma_buffer *buf = &substream->dma_buffer;
  	size_t size = kirkwood_dma_snd_hw.buffer_bytes_max;
  
  	buf->dev.type = SNDRV_DMA_TYPE_DEV;
  	buf->dev.dev = pcm->card->dev;
  	buf->area = dma_alloc_coherent(pcm->card->dev, size,
  			&buf->addr, GFP_KERNEL);
  	if (!buf->area)
  		return -ENOMEM;
  	buf->bytes = size;
  	buf->private_data = NULL;
  
  	return 0;
  }
552d1ef6b   Liam Girdwood   ASoC: core - Opti...
314
  static int kirkwood_dma_new(struct snd_soc_pcm_runtime *rtd)
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
315
  {
552d1ef6b   Liam Girdwood   ASoC: core - Opti...
316
  	struct snd_card *card = rtd->card->snd_card;
552d1ef6b   Liam Girdwood   ASoC: core - Opti...
317
  	struct snd_pcm *pcm = rtd->pcm;
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
318
319
320
321
322
323
  	int ret;
  
  	if (!card->dev->dma_mask)
  		card->dev->dma_mask = &kirkwood_dma_dmamask;
  	if (!card->dev->coherent_dma_mask)
  		card->dev->coherent_dma_mask = 0xffffffff;
25e9e7565   Joachim Eastwood   ASoC: check for s...
324
  	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
325
326
327
328
329
  		ret = kirkwood_dma_preallocate_dma_buffer(pcm,
  				SNDRV_PCM_STREAM_PLAYBACK);
  		if (ret)
  			return ret;
  	}
25e9e7565   Joachim Eastwood   ASoC: check for s...
330
  	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  		ret = kirkwood_dma_preallocate_dma_buffer(pcm,
  				SNDRV_PCM_STREAM_CAPTURE);
  		if (ret)
  			return ret;
  	}
  
  	return 0;
  }
  
  static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
  {
  	struct snd_pcm_substream *substream;
  	struct snd_dma_buffer *buf;
  	int stream;
  
  	for (stream = 0; stream < 2; stream++) {
  		substream = pcm->streams[stream].substream;
  		if (!substream)
  			continue;
  		buf = &substream->dma_buffer;
  		if (!buf->area)
  			continue;
  
  		dma_free_coherent(pcm->card->dev, buf->bytes,
  				buf->area, buf->addr);
  		buf->area = NULL;
  	}
  }
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
359
360
  static struct snd_soc_platform_driver kirkwood_soc_platform = {
  	.ops		= &kirkwood_dma_ops,
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
361
362
363
  	.pcm_new	= kirkwood_dma_new,
  	.pcm_free	= kirkwood_dma_free_dma_buffers,
  };
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
364

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
365
  static int __devinit kirkwood_soc_platform_probe(struct platform_device *pdev)
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
366
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
367
  	return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
368
  }
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
369

f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
370
  static int __devexit kirkwood_soc_platform_remove(struct platform_device *pdev)
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
371
  {
f0fba2ad1   Liam Girdwood   ASoC: multi-compo...
372
373
374
375
376
377
378
379
380
381
382
383
384
  	snd_soc_unregister_platform(&pdev->dev);
  	return 0;
  }
  
  static struct platform_driver kirkwood_pcm_driver = {
  	.driver = {
  			.name = "kirkwood-pcm-audio",
  			.owner = THIS_MODULE,
  	},
  
  	.probe = kirkwood_soc_platform_probe,
  	.remove = __devexit_p(kirkwood_soc_platform_remove),
  };
41b102250   Axel Lin   ASoC: Convert kir...
385
  module_platform_driver(kirkwood_pcm_driver);
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
386

697378972   Arnaud Patard   ASoC: Change my m...
387
  MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
f9b95980f   apatard@mandriva.com   ASoC: kirkwood: A...
388
389
  MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
  MODULE_LICENSE("GPL");
3ba31051f   Arnaud Patard   ASoC: kirkwood: a...
390
  MODULE_ALIAS("platform:kirkwood-pcm-audio");