Blame view

sound/sh/aica.c 18 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>
6cbbfe1c8   Takashi Iwai   ALSA: Include lin...
38
  #include <linux/io.h>
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
39
40
41
42
43
  #include <sound/core.h>
  #include <sound/control.h>
  #include <sound/pcm.h>
  #include <sound/initval.h>
  #include <sound/info.h>
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
44
  #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
  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.");
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
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
  /* 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...
93
94
95
96
  		if (time_count > 0x10000) {
  			snd_printk
  			    ("WARNING: G2 FIFO appears to be blocked.
  ");
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
97
98
99
100
101
102
103
104
105
  			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...
106
  	unsigned long flags;
5e246b850   Takashi Iwai   ALSA: Kill snd_as...
107
108
  	if (snd_BUG_ON(length % 4))
  		return;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
109
110
111
  	for (i = 0; i < length; i++) {
  		if (!(i % 8))
  			spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
112
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
113
  		writel(what, toi + SPU_MEMORY_BASE);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
114
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
115
116
117
118
119
120
121
  		toi++;
  	}
  }
  
  /* spu_memload - write to SPU address space */
  static void spu_memload(u32 toi, void *from, int length)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
122
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
123
124
125
126
127
128
129
130
131
132
  	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...
133
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
134
  		writel(val, to);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
135
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
136
137
138
139
140
141
142
143
144
  		froml++;
  		to++;
  	}
  }
  
  /* spu_disable - set spu registers to stop sound output */
  static void spu_disable(void)
  {
  	int i;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
145
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
146
147
148
149
150
  	u32 regval;
  	spu_write_wait();
  	regval = readl(ARM_RESET_REGISTER);
  	regval |= 1;
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
151
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
152
  	writel(regval, ARM_RESET_REGISTER);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
153
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
154
155
156
157
158
  	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...
159
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
160
  		writel(regval, SPU_REGISTER_BASE + (i * 0x80));
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
161
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
162
163
164
165
166
167
  	}
  }
  
  /* spu_enable - set spu registers to enable sound output */
  static void spu_enable(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
168
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
169
170
171
  	u32 regval = readl(ARM_RESET_REGISTER);
  	regval &= ~1;
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
172
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
173
  	writel(regval, ARM_RESET_REGISTER);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
174
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
175
176
177
178
179
180
181
182
  }
  
  /* 
   * 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...
183
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
184
185
186
  	spu_disable();
  	spu_memset(0, 0, 0x200000 / 4);
  	/* Put ARM7 in endless loop */
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
187
  	local_irq_save(flags);
48ccb2cee   Paul Mundt   sound: sh: ctrl_i...
188
  	__raw_writel(0xea000002, SPU_MEMORY_BASE);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
189
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
190
191
192
193
194
195
  	spu_enable();
  }
  
  /* aica_chn_start - write to spu to start playback */
  static void aica_chn_start(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
196
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
197
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
198
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
199
  	writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
200
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
201
202
203
204
205
  }
  
  /* aica_chn_halt - write to spu to halt playback */
  static void aica_chn_halt(void)
  {
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
206
  	unsigned long flags;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
207
  	spu_write_wait();
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
208
  	local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
209
  	writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
210
  	local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  }
  
  /* 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...
237
  	unsigned long flags;
ceac4bf34   Takashi Iwai   [ALSA] Dreamcast ...
238
  	err = 0;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
239
240
241
242
243
  	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...
244
  		local_irq_save(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
245
  		err = dma_xfer(AICA_DMA_CHANNEL,
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
246
247
248
249
250
  			       (unsigned long) (runtime->dma_area +
  						(AICA_BUFFER_SIZE * q) /
  						channels +
  						AICA_PERIOD_SIZE *
  						period_offset),
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
251
252
253
  			       AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
  			       AICA_PERIOD_SIZE * period_offset,
  			       buffer_size / channels, AICA_DMA_MODE);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
254
255
  		if (unlikely(err < 0)) {
  			local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
256
  			break;
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
257
  		}
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
258
  		dma_wait_for_completion(AICA_DMA_CHANNEL);
44e0b6821   Adrian McMenamin   [ALSA] protect Dr...
259
  		local_irq_restore(flags);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
260
261
262
263
264
265
266
  	}
  	return err;
  }
  
  static void startup_aica(struct snd_card_aica *dreamcastcard)
  {
  	spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
267
  		    dreamcastcard->channel, sizeof(struct aica_channel));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
268
269
270
271
272
273
  	aica_chn_start();
  }
  
  static void run_spu_dma(struct work_struct *work)
  {
  	int buffer_size;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
274
275
  	struct snd_pcm_runtime *runtime;
  	struct snd_card_aica *dreamcastcard;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
276
277
278
  	dreamcastcard =
  	    container_of(work, struct snd_card_aica, spu_dma_work);
  	runtime = dreamcastcard->substream->runtime;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
279
  	if (unlikely(dreamcastcard->dma_check == 0)) {
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
280
281
  		buffer_size =
  		    frames_to_bytes(runtime, runtime->buffer_size);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
282
283
  		if (runtime->channels > 1)
  			dreamcastcard->channel->flags |= 0x01;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
284
285
  		aica_dma_transfer(runtime->channels, buffer_size,
  				  dreamcastcard->substream);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
286
287
288
289
290
291
292
  		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...
293
  				  dreamcastcard->substream);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
294
295
296
  		snd_pcm_period_elapsed(dreamcastcard->substream);
  		dreamcastcard->clicks++;
  		if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
297
  			dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
298
299
300
301
302
303
304
305
306
307
308
  		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...
309
  	substream = (struct snd_pcm_substream *) timer_var;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  	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;
43aa56d95   Bhaktipriya Shridhar   ALSA: sh: aica: R...
327
  	schedule_work(&(dreamcastcard->spu_dma_work));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
328
329
330
331
  }
  
  static void spu_begin_dma(struct snd_pcm_substream *substream)
  {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
332
333
334
335
  	struct snd_card_aica *dreamcastcard;
  	struct snd_pcm_runtime *runtime;
  	runtime = substream->runtime;
  	dreamcastcard = substream->pcm->private_data;
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
336
  	/*get the queue to do the work */
43aa56d95   Bhaktipriya Shridhar   ALSA: sh: aica: R...
337
  	schedule_work(&(dreamcastcard->spu_dma_work));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
338
339
340
341
342
  	/* Timer may already be running */
  	if (unlikely(dreamcastcard->timer.data)) {
  		mod_timer(&dreamcastcard->timer, jiffies + 4);
  		return;
  	}
da1a39242   Takashi Iwai   ALSA: aica: Use s...
343
344
345
  	setup_timer(&dreamcastcard->timer, aica_period_elapsed,
  		    (unsigned long) substream);
  	mod_timer(&dreamcastcard->timer, jiffies + 4);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
346
347
348
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
  }
  
  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;
43aa56d95   Bhaktipriya Shridhar   ALSA: sh: aica: R...
381
  	flush_work(&(dreamcastcard->spu_dma_work));
d20b9b20e   Takashi Iwai   [ALSA] aica - fix...
382
383
  	if (dreamcastcard->timer.data)
  		del_timer(&dreamcastcard->timer);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
  	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...
402
403
  	    snd_pcm_lib_malloc_pages(substream,
  				     params_buffer_bytes(hw_params));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  }
  
  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...
420
421
422
423
424
  	switch (cmd) {
  	case SNDRV_PCM_TRIGGER_START:
  		spu_begin_dma(substream);
  		break;
  	case SNDRV_PCM_TRIGGER_STOP:
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
425
426
427
428
429
430
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
  		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...
458
459
  	    snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,
  			&pcm);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
  	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...
478
  #define aica_pcmswitch_info		snd_ctl_boolean_mono_info
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
479
480
481
482
483
484
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
  
  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...
522
  	unsigned int vol;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
523
524
525
  	dreamcastcard = kcontrol->private_data;
  	if (unlikely(!dreamcastcard->channel))
  		return -ETXTBSY;
3b8924677   Takashi Iwai   [ALSA] Check valu...
526
527
528
529
  	vol = ucontrol->value.integer.value[0];
  	if (vol > 0xff)
  		return -EINVAL;
  	if (unlikely(dreamcastcard->channel->vol == vol))
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
530
531
532
533
  		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...
534
  		    dreamcastcard->channel, sizeof(struct aica_channel));
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
535
536
  	return 1;
  }
e74033a85   Bill Pemberton   ALSA: sh: remove ...
537
  static struct snd_kcontrol_new snd_aica_pcmswitch_control = {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
538
539
540
541
542
543
544
  	.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
  	.name = "PCM Playback Switch",
  	.index = 0,
  	.info = aica_pcmswitch_info,
  	.get = aica_pcmswitch_get,
  	.put = aica_pcmswitch_put
  };
e74033a85   Bill Pemberton   ALSA: sh: remove ...
545
  static struct snd_kcontrol_new snd_aica_pcmvolume_control = {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
  	.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...
562
  	/* write firmware into memory */
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
563
564
565
566
567
568
  	spu_disable();
  	spu_memload(0, fw_entry->data, fw_entry->size);
  	spu_enable();
  	release_firmware(fw_entry);
  	return err;
  }
e74033a85   Bill Pemberton   ALSA: sh: remove ...
569
  static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
570
571
572
573
574
575
576
577
578
579
580
581
582
583
  {
  	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;
  }
e74033a85   Bill Pemberton   ALSA: sh: remove ...
584
  static int snd_aica_remove(struct platform_device *devptr)
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
585
586
587
588
589
590
591
  {
  	struct snd_card_aica *dreamcastcard;
  	dreamcastcard = platform_get_drvdata(devptr);
  	if (unlikely(!dreamcastcard))
  		return -ENODEV;
  	snd_card_free(dreamcastcard->card);
  	kfree(dreamcastcard);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
592
593
  	return 0;
  }
e74033a85   Bill Pemberton   ALSA: sh: remove ...
594
  static int snd_aica_probe(struct platform_device *devptr)
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
595
596
597
598
599
600
  {
  	int err;
  	struct snd_card_aica *dreamcastcard;
  	dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
  	if (unlikely(!dreamcastcard))
  		return -ENOMEM;
e7182ac5a   Takashi Iwai   ALSA: sh: Convert...
601
602
  	err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER,
  			   THIS_MODULE, 0, &dreamcastcard->card);
bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
603
  	if (unlikely(err < 0)) {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
604
  		kfree(dreamcastcard);
bd7dd77c2   Takashi Iwai   ALSA: Convert to ...
605
  		return err;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
606
607
608
609
610
  	}
  	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...
611
612
  	/* Prepare to use the queue */
  	INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
613
614
615
616
  	/* Load the PCM 'chip' */
  	err = snd_aicapcmchip(dreamcastcard, 0);
  	if (unlikely(err < 0))
  		goto freedreamcast;
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
617
618
619
620
621
622
623
624
625
626
627
  	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);
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
628
629
630
631
632
633
634
635
636
637
638
639
  	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,
e74033a85   Bill Pemberton   ALSA: sh: remove ...
640
  	.remove = snd_aica_remove,
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
641
  	.driver = {
8bf01d8ab   Takashi Iwai   ALSA: Add missing...
642
  		.name = SND_AICA_DRIVER,
8bf01d8ab   Takashi Iwai   ALSA: Add missing...
643
  	},
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
644
645
646
647
648
649
650
651
652
653
  };
  
  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...
654
  	if (IS_ERR(pd)) {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
655
656
657
658
659
660
661
662
663
  		platform_driver_unregister(&snd_aica_driver);
  		return PTR_ERR(pd);
  	}
  	/* Load the firmware */
  	return load_aica_firmware();
  }
  
  static void __exit aica_exit(void)
  {
198de43d7   Adrian McMenamin   [ALSA] Add ALSA s...
664
665
666
667
668
669
670
671
  	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);