Blame view

sound/sh/aica.c 18.5 KB
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  /*
  * This code is licenced under 
  * the General Public Licence
  * version 2
  *
  * Copyright Adrian McMenamin 2005, 2006, 2007
  * <adrian@mcmen.demon.co.uk>
  * Requires firmware (BSD licenced) available from:
  * http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
  * or the maintainer
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as published by
  * the Free Software Foundation.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  *
  */
  
  #include <linux/init.h>
  #include <linux/jiffies.h>
  #include <linux/slab.h>
  #include <linux/time.h>
  #include <linux/wait.h>
65a772172   Paul Gortmaker   sound: fix driver...
32
  #include <linux/module.h>
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
33
34
35
36
37
  #include <linux/platform_device.h>
  #include <linux/firmware.h>
  #include <linux/timer.h>
  #include <linux/delay.h>
  #include <linux/workqueue.h>
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
38
39
40
41
42
43
44
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/pcm.h>
  #include <sound/initval.h>
  #include <sound/info.h>
  #include <asm/io.h>
  #include <asm/dma.h>
f15cbe6f1   Paul Mundt   sh: migrate to ar...
45
  #include <mach/sysasic.h>
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
46
47
48
49
50
51
  #include "aica.h"
  
  MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
  MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
  MODULE_LICENSE("GPL");
  MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
f37325a95   Ben Hutchings   ALSA: snd-aica: d...
52
  MODULE_FIRMWARE("aica_firmware.bin");
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
53
54
55
56
57
  
  /* module parameters */
  #define CARD_NAME "AICA"
  static int index = -1;
  static char *id;
a67ff6a54   Rusty Russell   ALSA: module_para...
58
  static bool enable = 1;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
59
60
61
62
63
64
65
66
  module_param(index, int, 0444);
  MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
  module_param(id, charp, 0444);
  MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
  module_param(enable, bool, 0644);
  MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
  
  /* Use workqueue */
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
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
  static struct workqueue_struct *aica_queue;
  
  /* Simple platform device */
  static struct platform_device *pd;
  static struct resource aica_memory_space[2] = {
  	{
  	 .name = "AICA ARM CONTROL",
  	 .start = ARM_RESET_REGISTER,
  	 .flags = IORESOURCE_MEM,
  	 .end = ARM_RESET_REGISTER + 3,
  	 },
  	{
  	 .name = "AICA Sound RAM",
  	 .start = SPU_MEMORY_BASE,
  	 .flags = IORESOURCE_MEM,
  	 .end = SPU_MEMORY_BASE + 0x200000 - 1,
  	 },
  };
  
  /* SPU specific functions */
  /* spu_write_wait - wait for G2-SH FIFO to clear */
  static void spu_write_wait(void)
  {
  	int time_count;
  	time_count = 0;
  	while (1) {
  		if (!(readl(G2_FIFO) & 0x11))
  			break;
  		/* To ensure hardware failure doesn't wedge kernel */
  		time_count++;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
97
98
99
100
  		if (time_count > 0x10000) {
  			snd_printk
  			    ("WARNING: G2 FIFO appears to be blocked.
  ");
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
101
102
103
104
105
106
107
108
109
  			break;
  		}
  	}
  }
  
  /* spu_memset - write to memory in SPU address space */
  static void spu_memset(u32 toi, u32 what, int length)
  {
  	int i;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
110
  	unsigned long flags;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
111
112
  	if (snd_BUG_ON(length % 4))
  		return;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
113
114
115
  	for (i = 0; i < length; i++) {
  		if (!(i % 8))
  			spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
116
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
117
  		writel(what, toi + SPU_MEMORY_BASE);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
118
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
119
120
121
122
123
124
125
  		toi++;
  	}
  }
  
  /* spu_memload - write to SPU address space */
  static void spu_memload(u32 toi, void *from, int length)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
126
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
127
128
129
130
131
132
133
134
135
136
  	u32 *froml = from;
  	u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
  	int i;
  	u32 val;
  	length = DIV_ROUND_UP(length, 4);
  	spu_write_wait();
  	for (i = 0; i < length; i++) {
  		if (!(i % 8))
  			spu_write_wait();
  		val = *froml;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
137
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
138
  		writel(val, to);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
139
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
140
141
142
143
144
145
146
147
148
  		froml++;
  		to++;
  	}
  }
  
  /* spu_disable - set spu registers to stop sound output */
  static void spu_disable(void)
  {
  	int i;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
149
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
150
151
152
153
154
  	u32 regval;
  	spu_write_wait();
  	regval = readl(ARM_RESET_REGISTER);
  	regval |= 1;
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
155
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
156
  	writel(regval, ARM_RESET_REGISTER);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
157
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
158
159
160
161
162
  	for (i = 0; i < 64; i++) {
  		spu_write_wait();
  		regval = readl(SPU_REGISTER_BASE + (i * 0x80));
  		regval = (regval & ~0x4000) | 0x8000;
  		spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
163
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
164
  		writel(regval, SPU_REGISTER_BASE + (i * 0x80));
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
165
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
166
167
168
169
170
171
  	}
  }
  
  /* spu_enable - set spu registers to enable sound output */
  static void spu_enable(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
172
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
173
174
175
  	u32 regval = readl(ARM_RESET_REGISTER);
  	regval &= ~1;
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
176
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
177
  	writel(regval, ARM_RESET_REGISTER);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
178
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
179
180
181
182
183
184
185
186
  }
  
  /* 
   * Halt the sound processor, clear the memory,
   * load some default ARM7 code, and then restart ARM7
  */
  static void spu_reset(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
187
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
188
189
190
  	spu_disable();
  	spu_memset(0, 0, 0x200000 / 4);
  	/* Put ARM7 in endless loop */
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
191
  	local_irq_save(flags);
48ccb2cee   Paul Mundt   sound: sh: ctrl_i...
192
  	__raw_writel(0xea000002, SPU_MEMORY_BASE);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
193
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
194
195
196
197
198
199
  	spu_enable();
  }
  
  /* aica_chn_start - write to spu to start playback */
  static void aica_chn_start(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
200
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
201
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
202
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
203
  	writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
204
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
205
206
207
208
209
  }
  
  /* aica_chn_halt - write to spu to halt playback */
  static void aica_chn_halt(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
210
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
211
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
212
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
213
  	writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
214
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
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
  }
  
  /* ALSA code below */
  static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
  	.info = (SNDRV_PCM_INFO_NONINTERLEAVED),
  	.formats =
  	    (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
  	     SNDRV_PCM_FMTBIT_IMA_ADPCM),
  	.rates = SNDRV_PCM_RATE_8000_48000,
  	.rate_min = 8000,
  	.rate_max = 48000,
  	.channels_min = 1,
  	.channels_max = 2,
  	.buffer_bytes_max = AICA_BUFFER_SIZE,
  	.period_bytes_min = AICA_PERIOD_SIZE,
  	.period_bytes_max = AICA_PERIOD_SIZE,
  	.periods_min = AICA_PERIOD_NUMBER,
  	.periods_max = AICA_PERIOD_NUMBER,
  };
  
  static int aica_dma_transfer(int channels, int buffer_size,
  			     struct snd_pcm_substream *substream)
  {
  	int q, err, period_offset;
  	struct snd_card_aica *dreamcastcard;
  	struct snd_pcm_runtime *runtime;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
241
  	unsigned long flags;
ceac4bf34   Takashi Iwai   [ALSA] Dreamcast ...
242
  	err = 0;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
243
244
245
246
247
  	dreamcastcard = substream->pcm->private_data;
  	period_offset = dreamcastcard->clicks;
  	period_offset %= (AICA_PERIOD_NUMBER / channels);
  	runtime = substream->runtime;
  	for (q = 0; q < channels; q++) {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
248
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
249
  		err = dma_xfer(AICA_DMA_CHANNEL,
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
250
251
252
253
254
  			       (unsigned long) (runtime->dma_area +
  						(AICA_BUFFER_SIZE * q) /
  						channels +
  						AICA_PERIOD_SIZE *
  						period_offset),
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
255
256
257
  			       AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
  			       AICA_PERIOD_SIZE * period_offset,
  			       buffer_size / channels, AICA_DMA_MODE);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
258
259
  		if (unlikely(err < 0)) {
  			local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
260
  			break;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
261
  		}
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
262
  		dma_wait_for_completion(AICA_DMA_CHANNEL);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
263
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
264
265
266
267
268
269
270
  	}
  	return err;
  }
  
  static void startup_aica(struct snd_card_aica *dreamcastcard)
  {
  	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
271
  		    dreamcastcard->channel, sizeof(struct aica_channel));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
272
273
274
275
276
277
  	aica_chn_start();
  }
  
  static void run_spu_dma(struct work_struct *work)
  {
  	int buffer_size;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
278
279
  	struct snd_pcm_runtime *runtime;
  	struct snd_card_aica *dreamcastcard;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
280
281
282
  	dreamcastcard =
  	    container_of(work, struct snd_card_aica, spu_dma_work);
  	runtime = dreamcastcard->substream->runtime;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
283
  	if (unlikely(dreamcastcard->dma_check == 0)) {
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
284
285
  		buffer_size =
  		    frames_to_bytes(runtime, runtime->buffer_size);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
286
287
  		if (runtime->channels > 1)
  			dreamcastcard->channel->flags |= 0x01;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
288
289
  		aica_dma_transfer(runtime->channels, buffer_size,
  				  dreamcastcard->substream);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
290
291
292
293
294
295
296
  		startup_aica(dreamcastcard);
  		dreamcastcard->clicks =
  		    buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
  		return;
  	} else {
  		aica_dma_transfer(runtime->channels,
  				  AICA_PERIOD_SIZE * runtime->channels,
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
297
  				  dreamcastcard->substream);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
298
299
300
  		snd_pcm_period_elapsed(dreamcastcard->substream);
  		dreamcastcard->clicks++;
  		if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
301
  			dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
302
303
304
305
306
307
308
309
310
311
312
  		mod_timer(&dreamcastcard->timer, jiffies + 1);
  	}
  }
  
  static void aica_period_elapsed(unsigned long timer_var)
  {
  	/*timer function - so cannot sleep */
  	int play_period;
  	struct snd_pcm_runtime *runtime;
  	struct snd_pcm_substream *substream;
  	struct snd_card_aica *dreamcastcard;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
313
  	substream = (struct snd_pcm_substream *) timer_var;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
  	runtime = substream->runtime;
  	dreamcastcard = substream->pcm->private_data;
  	/* Have we played out an additional period? */
  	play_period =
  	    frames_to_bytes(runtime,
  			    readl
  			    (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
  	    AICA_PERIOD_SIZE;
  	if (play_period == dreamcastcard->current_period) {
  		/* reschedule the timer */
  		mod_timer(&(dreamcastcard->timer), jiffies + 1);
  		return;
  	}
  	if (runtime->channels > 1)
  		dreamcastcard->current_period = play_period;
  	if (unlikely(dreamcastcard->dma_check == 0))
  		dreamcastcard->dma_check = 1;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
331
  	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
332
333
334
335
  }
  
  static void spu_begin_dma(struct snd_pcm_substream *substream)
  {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
336
337
338
339
  	struct snd_card_aica *dreamcastcard;
  	struct snd_pcm_runtime *runtime;
  	runtime = substream->runtime;
  	dreamcastcard = substream->pcm->private_data;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
340
341
  	/*get the queue to do the work */
  	queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
342
343
344
345
346
347
  	/* Timer may already be running */
  	if (unlikely(dreamcastcard->timer.data)) {
  		mod_timer(&dreamcastcard->timer, jiffies + 4);
  		return;
  	}
  	init_timer(&(dreamcastcard->timer));
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
348
  	dreamcastcard->timer.data = (unsigned long) substream;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
  	dreamcastcard->timer.function = aica_period_elapsed;
  	dreamcastcard->timer.expires = jiffies + 4;
  	add_timer(&(dreamcastcard->timer));
  }
  
  static int snd_aicapcm_pcm_open(struct snd_pcm_substream
  				*substream)
  {
  	struct snd_pcm_runtime *runtime;
  	struct aica_channel *channel;
  	struct snd_card_aica *dreamcastcard;
  	if (!enable)
  		return -ENOENT;
  	dreamcastcard = substream->pcm->private_data;
  	channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
  	if (!channel)
  		return -ENOMEM;
  	/* set defaults for channel */
  	channel->sfmt = SM_8BIT;
  	channel->cmd = AICA_CMD_START;
  	channel->vol = dreamcastcard->master_volume;
  	channel->pan = 0x80;
  	channel->pos = 0;
  	channel->flags = 0;	/* default to mono */
  	dreamcastcard->channel = channel;
  	runtime = substream->runtime;
  	runtime->hw = snd_pcm_aica_playback_hw;
  	spu_enable();
  	dreamcastcard->clicks = 0;
  	dreamcastcard->current_period = 0;
  	dreamcastcard->dma_check = 0;
  	return 0;
  }
  
  static int snd_aicapcm_pcm_close(struct snd_pcm_substream
  				 *substream)
  {
  	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
387
388
389
  	flush_workqueue(aica_queue);
  	if (dreamcastcard->timer.data)
  		del_timer(&dreamcastcard->timer);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  	kfree(dreamcastcard->channel);
  	spu_disable();
  	return 0;
  }
  
  static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
  				   *substream)
  {
  	/* Free the DMA buffer */
  	return snd_pcm_lib_free_pages(substream);
  }
  
  static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
  				     *substream, struct snd_pcm_hw_params
  				     *hw_params)
  {
  	/* Allocate a DMA buffer using ALSA built-ins */
  	return
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
408
409
  	    snd_pcm_lib_malloc_pages(substream,
  				     params_buffer_bytes(hw_params));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  }
  
  static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
  				   *substream)
  {
  	struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
  	if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
  		dreamcastcard->channel->sfmt = SM_16BIT;
  	dreamcastcard->channel->freq = substream->runtime->rate;
  	dreamcastcard->substream = substream;
  	return 0;
  }
  
  static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
  				   *substream, int cmd)
  {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
426
427
428
429
430
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  		spu_begin_dma(substream);
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
  		aica_chn_halt();
  		break;
  	default:
  		return -EINVAL;
  	}
  	return 0;
  }
  
  static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
  					     *substream)
  {
  	return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
  }
  
  static struct snd_pcm_ops snd_aicapcm_playback_ops = {
  	.open = snd_aicapcm_pcm_open,
  	.close = snd_aicapcm_pcm_close,
  	.ioctl = snd_pcm_lib_ioctl,
  	.hw_params = snd_aicapcm_pcm_hw_params,
  	.hw_free = snd_aicapcm_pcm_hw_free,
  	.prepare = snd_aicapcm_pcm_prepare,
  	.trigger = snd_aicapcm_pcm_trigger,
  	.pointer = snd_aicapcm_pcm_pointer,
  };
  
  /* TO DO: set up to handle more than one pcm instance */
  static int __init snd_aicapcmchip(struct snd_card_aica
  				  *dreamcastcard, int pcm_index)
  {
  	struct snd_pcm *pcm;
  	int err;
  	/* AICA has no capture ability */
  	err =
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
464
465
  	    snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
  			&pcm);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
  	if (unlikely(err < 0))
  		return err;
  	pcm->private_data = dreamcastcard;
  	strcpy(pcm->name, "AICA PCM");
  	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
  			&snd_aicapcm_playback_ops);
  	/* Allocate the DMA buffers */
  	err =
  	    snd_pcm_lib_preallocate_pages_for_all(pcm,
  						  SNDRV_DMA_TYPE_CONTINUOUS,
  						  snd_dma_continuous_data
  						  (GFP_KERNEL),
  						  AICA_BUFFER_SIZE,
  						  AICA_BUFFER_SIZE);
  	return err;
  }
  
  /* Mixer controls */
a5ce88909   Takashi Iwai   [ALSA] Clean up w...
484
  #define aica_pcmswitch_info		snd_ctl_boolean_mono_info
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
  
  static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_value *ucontrol)
  {
  	ucontrol->value.integer.value[0] = 1;	/* TO DO: Fix me */
  	return 0;
  }
  
  static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_value *ucontrol)
  {
  	if (ucontrol->value.integer.value[0] == 1)
  		return 0;	/* TO DO: Fix me */
  	else
  		aica_chn_halt();
  	return 0;
  }
  
  static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
  			       struct snd_ctl_elem_info *uinfo)
  {
  	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
  	uinfo->count = 1;
  	uinfo->value.integer.min = 0;
  	uinfo->value.integer.max = 0xFF;
  	return 0;
  }
  
  static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_value *ucontrol)
  {
  	struct snd_card_aica *dreamcastcard;
  	dreamcastcard = kcontrol->private_data;
  	if (unlikely(!dreamcastcard->channel))
  		return -ETXTBSY;	/* we've not yet been set up */
  	ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
  	return 0;
  }
  
  static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
  			      struct snd_ctl_elem_value *ucontrol)
  {
  	struct snd_card_aica *dreamcastcard;
3b8924677   Takashi Iwai   [ALSA] Check valu...
528
  	unsigned int vol;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
529
530
531
  	dreamcastcard = kcontrol->private_data;
  	if (unlikely(!dreamcastcard->channel))
  		return -ETXTBSY;
3b8924677   Takashi Iwai   [ALSA] Check valu...
532
533
534
535
  	vol = ucontrol->value.integer.value[0];
  	if (vol > 0xff)
  		return -EINVAL;
  	if (unlikely(dreamcastcard->channel->vol == vol))
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
536
537
538
539
  		return 0;
  	dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
  	dreamcastcard->master_volume = ucontrol->value.integer.value[0];
  	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
540
  		    dreamcastcard->channel, sizeof(struct aica_channel));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  	return 1;
  }
  
  static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "PCM Playback Switch",
  	.index = 0,
  	.info = aica_pcmswitch_info,
  	.get = aica_pcmswitch_get,
  	.put = aica_pcmswitch_put
  };
  
  static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "PCM Playback Volume",
  	.index = 0,
  	.info = aica_pcmvolume_info,
  	.get = aica_pcmvolume_get,
  	.put = aica_pcmvolume_put
  };
  
  static int load_aica_firmware(void)
  {
  	int err;
  	const struct firmware *fw_entry;
  	spu_reset();
  	err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
  	if (unlikely(err))
  		return err;
877d03105   Nick Andrew   trivial: Fix miss...
570
  	/* write firmware into memory */
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
  	spu_disable();
  	spu_memload(0, fw_entry->data, fw_entry->size);
  	spu_enable();
  	release_firmware(fw_entry);
  	return err;
  }
  
  static int __devinit add_aicamixer_controls(struct snd_card_aica
  					    *dreamcastcard)
  {
  	int err;
  	err = snd_ctl_add
  	    (dreamcastcard->card,
  	     snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
  	if (unlikely(err < 0))
  		return err;
  	err = snd_ctl_add
  	    (dreamcastcard->card,
  	     snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
  	if (unlikely(err < 0))
  		return err;
  	return 0;
  }
418c8f899   Uwe Kleine-König   ALSA: Fix section...
594
  static int __devexit snd_aica_remove(struct platform_device *devptr)
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
595
596
597
598
599
600
601
602
603
604
  {
  	struct snd_card_aica *dreamcastcard;
  	dreamcastcard = platform_get_drvdata(devptr);
  	if (unlikely(!dreamcastcard))
  		return -ENODEV;
  	snd_card_free(dreamcastcard->card);
  	kfree(dreamcastcard);
  	platform_set_drvdata(devptr, NULL);
  	return 0;
  }
418c8f899   Uwe Kleine-König   ALSA: Fix section...
605
  static int __devinit snd_aica_probe(struct platform_device *devptr)
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
606
607
608
609
610
611
  {
  	int err;
  	struct snd_card_aica *dreamcastcard;
  	dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
  	if (unlikely(!dreamcastcard))
  		return -ENOMEM;
bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
612
613
614
  	err = snd_card_create(index, SND_AICA_DRIVER, THIS_MODULE, 0,
  			      &dreamcastcard->card);
  	if (unlikely(err < 0)) {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
615
  		kfree(dreamcastcard);
bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
616
  		return err;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
617
618
619
620
621
  	}
  	strcpy(dreamcastcard->card->driver, "snd_aica");
  	strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
  	strcpy(dreamcastcard->card->longname,
  	       "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
622
623
  	/* Prepare to use the queue */
  	INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
  	/* Load the PCM 'chip' */
  	err = snd_aicapcmchip(dreamcastcard, 0);
  	if (unlikely(err < 0))
  		goto freedreamcast;
  	snd_card_set_dev(dreamcastcard->card, &devptr->dev);
  	dreamcastcard->timer.data = 0;
  	dreamcastcard->channel = NULL;
  	/* Add basic controls */
  	err = add_aicamixer_controls(dreamcastcard);
  	if (unlikely(err < 0))
  		goto freedreamcast;
  	/* Register the card with ALSA subsystem */
  	err = snd_card_register(dreamcastcard->card);
  	if (unlikely(err < 0))
  		goto freedreamcast;
  	platform_set_drvdata(devptr, dreamcastcard);
  	aica_queue = create_workqueue(CARD_NAME);
  	if (unlikely(!aica_queue))
  		goto freedreamcast;
  	snd_printk
  	    ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor
  ");
  	return 0;
        freedreamcast:
  	snd_card_free(dreamcastcard->card);
  	kfree(dreamcastcard);
  	return err;
  }
  
  static struct platform_driver snd_aica_driver = {
  	.probe = snd_aica_probe,
418c8f899   Uwe Kleine-König   ALSA: Fix section...
655
  	.remove = __devexit_p(snd_aica_remove),
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
656
657
658
659
660
661
662
663
664
665
666
667
  	.driver = {
  		   .name = SND_AICA_DRIVER},
  };
  
  static int __init aica_init(void)
  {
  	int err;
  	err = platform_driver_register(&snd_aica_driver);
  	if (unlikely(err < 0))
  		return err;
  	pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
  					     aica_memory_space, 2);
801678c5a   Hirofumi Nakagawa   Remove duplicated...
668
  	if (IS_ERR(pd)) {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
669
670
671
672
673
674
675
676
677
  		platform_driver_unregister(&snd_aica_driver);
  		return PTR_ERR(pd);
  	}
  	/* Load the firmware */
  	return load_aica_firmware();
  }
  
  static void __exit aica_exit(void)
  {
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
678
679
680
681
  	/* Destroy the aica kernel thread            *
  	 * being extra cautious to check if it exists*/
  	if (likely(aica_queue))
  		destroy_workqueue(aica_queue);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
682
683
684
685
686
687
688
689
  	platform_device_unregister(pd);
  	platform_driver_unregister(&snd_aica_driver);
  	/* Kill any sound still playing and reset ARM7 to safe state */
  	spu_reset();
  }
  
  module_init(aica_init);
  module_exit(aica_exit);