aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorAdrian McMenamin <adrian@mcmen.demon.co.uk>2007-05-18 08:26:59 -0400
committerJaroslav Kysela <perex@suse.cz>2007-07-20 05:11:17 -0400
commit198de43d758ca2700e2b52b49c0b189b4931466c (patch)
tree4c783082bc682c6a3eaa9734563421bf42ba82eb /sound
parentaef3b06ac69783d6a6d1e4357c62bab46dd16141 (diff)
[ALSA] Add ALSA support for the SEGA Dreamcast PCM device
ALSA support for the SEGA Dreamcast Yamaha AICA sound device (pcm) This patch adds ALSA sound support for pcm playback on two channels on the SEGA Dreamcast built-in sound device (the Yamaha AICA) Add driver for the AICA sound device built into the SEGA Dreamcast Hook it all up with the build system. Signed-off-by: Adrian McMenamin <adrian@mcmen.demon.co.uk> Signed-off-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: Jaroslav Kysela <perex@suse.cz>
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig2
-rw-r--r--sound/Makefile2
-rw-r--r--sound/sh/Kconfig14
-rw-r--r--sound/sh/Makefile8
-rw-r--r--sound/sh/aica.c675
-rw-r--r--sound/sh/aica.h80
6 files changed, 780 insertions, 1 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index 9ea473823418..e48b9b37d228 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -65,6 +65,8 @@ source "sound/arm/Kconfig"
65 65
66source "sound/mips/Kconfig" 66source "sound/mips/Kconfig"
67 67
68source "sound/sh/Kconfig"
69
68# the following will depend on the order of config. 70# the following will depend on the order of config.
69# here assuming USB is defined before ALSA 71# here assuming USB is defined before ALSA
70source "sound/usb/Kconfig" 72source "sound/usb/Kconfig"
diff --git a/sound/Makefile b/sound/Makefile
index b7c7fb7c24c8..3ead922bd9c6 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -5,7 +5,7 @@ obj-$(CONFIG_SOUND) += soundcore.o
5obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o 5obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
6obj-$(CONFIG_SOUND_PRIME) += oss/ 6obj-$(CONFIG_SOUND_PRIME) += oss/
7obj-$(CONFIG_DMASOUND) += oss/ 7obj-$(CONFIG_DMASOUND) += oss/
8obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/ 8obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
9obj-$(CONFIG_SND_AOA) += aoa/ 9obj-$(CONFIG_SND_AOA) += aoa/
10 10
11# This one must be compilable even if sound is configured out 11# This one must be compilable even if sound is configured out
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
new file mode 100644
index 000000000000..b7e08ef22a94
--- /dev/null
+++ b/sound/sh/Kconfig
@@ -0,0 +1,14 @@
1# ALSA SH drivers
2
3menu "SUPERH devices"
4 depends on SND!=n && SUPERH
5
6config SND_AICA
7 tristate "Dreamcast Yamaha AICA sound"
8 depends on SH_DREAMCAST && SND
9 select SND_PCM
10 help
11 ALSA Sound driver for the SEGA Dreamcast console.
12
13endmenu
14
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
new file mode 100644
index 000000000000..8fdcb6e26f00
--- /dev/null
+++ b/sound/sh/Makefile
@@ -0,0 +1,8 @@
1#
2# Makefile for ALSA
3#
4
5snd-aica-objs := aica.o
6
7# Toplevel Module Dependency
8obj-$(CONFIG_SND_AICA) += snd-aica.o
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
new file mode 100644
index 000000000000..97bb86a58622
--- /dev/null
+++ b/sound/sh/aica.c
@@ -0,0 +1,675 @@
1/*
2* This code is licenced under
3* the General Public Licence
4* version 2
5*
6* Copyright Adrian McMenamin 2005, 2006, 2007
7* <adrian@mcmen.demon.co.uk>
8* Requires firmware (BSD licenced) available from:
9* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/
10* or the maintainer
11*
12* This program is free software; you can redistribute it and/or modify
13* it under the terms of version 2 of the GNU General Public License as published by
14* the Free Software Foundation.
15*
16* This program is distributed in the hope that it will be useful,
17* but WITHOUT ANY WARRANTY; without even the implied warranty of
18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19* GNU General Public License for more details.
20*
21* You should have received a copy of the GNU General Public License
22* along with this program; if not, write to the Free Software
23* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24*
25*/
26
27#include <linux/init.h>
28#include <linux/jiffies.h>
29#include <linux/slab.h>
30#include <linux/time.h>
31#include <linux/wait.h>
32#include <linux/moduleparam.h>
33#include <linux/platform_device.h>
34#include <linux/firmware.h>
35#include <linux/timer.h>
36#include <linux/delay.h>
37#include <linux/workqueue.h>
38#include <sound/driver.h>
39#include <sound/core.h>
40#include <sound/control.h>
41#include <sound/pcm.h>
42#include <sound/initval.h>
43#include <sound/info.h>
44#include <asm/io.h>
45#include <asm/dma.h>
46#include <asm/dreamcast/sysasic.h>
47#include "aica.h"
48
49MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
50MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
51MODULE_LICENSE("GPL");
52MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
53
54/* module parameters */
55#define CARD_NAME "AICA"
56static int index = -1;
57static char *id;
58static int enable = 1;
59module_param(index, int, 0444);
60MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
61module_param(id, charp, 0444);
62MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
63module_param(enable, bool, 0644);
64MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
65
66/* Use workqueue */
67
68static struct spu_work_holder {
69 struct work_struct spu_dma_work;
70 void *sspointer;
71} spu_working;
72
73static struct workqueue_struct *aica_queue;
74
75/* Simple platform device */
76static struct platform_device *pd;
77static struct resource aica_memory_space[2] = {
78 {
79 .name = "AICA ARM CONTROL",
80 .start = ARM_RESET_REGISTER,
81 .flags = IORESOURCE_MEM,
82 .end = ARM_RESET_REGISTER + 3,
83 },
84 {
85 .name = "AICA Sound RAM",
86 .start = SPU_MEMORY_BASE,
87 .flags = IORESOURCE_MEM,
88 .end = SPU_MEMORY_BASE + 0x200000 - 1,
89 },
90};
91
92/* SPU specific functions */
93/* spu_write_wait - wait for G2-SH FIFO to clear */
94static void spu_write_wait(void)
95{
96 int time_count;
97 time_count = 0;
98 while (1) {
99 if (!(readl(G2_FIFO) & 0x11))
100 break;
101 /* To ensure hardware failure doesn't wedge kernel */
102 time_count++;
103 if (time_count > 0x10000)
104 {
105 snd_printk("WARNING: G2 FIFO appears to be blocked.\n");
106 break;
107 }
108 }
109}
110
111/* spu_memset - write to memory in SPU address space */
112static void spu_memset(u32 toi, u32 what, int length)
113{
114 int i;
115 snd_assert(length % 4 == 0, return);
116 for (i = 0; i < length; i++) {
117 if (!(i % 8))
118 spu_write_wait();
119 writel(what, toi + SPU_MEMORY_BASE);
120 toi++;
121 }
122}
123
124/* spu_memload - write to SPU address space */
125static void spu_memload(u32 toi, void *from, int length)
126{
127 u32 *froml = from;
128 u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);
129 int i;
130 u32 val;
131 length = DIV_ROUND_UP(length, 4);
132 spu_write_wait();
133 for (i = 0; i < length; i++) {
134 if (!(i % 8))
135 spu_write_wait();
136 val = *froml;
137 writel(val, to);
138 froml++;
139 to++;
140 }
141}
142
143/* spu_disable - set spu registers to stop sound output */
144static void spu_disable(void)
145{
146 int i;
147 u32 regval;
148 spu_write_wait();
149 regval = readl(ARM_RESET_REGISTER);
150 regval |= 1;
151 spu_write_wait();
152 writel(regval, ARM_RESET_REGISTER);
153 for (i = 0; i < 64; i++) {
154 spu_write_wait();
155 regval = readl(SPU_REGISTER_BASE + (i * 0x80));
156 regval = (regval & ~0x4000) | 0x8000;
157 spu_write_wait();
158 writel(regval, SPU_REGISTER_BASE + (i * 0x80));
159 }
160}
161
162/* spu_enable - set spu registers to enable sound output */
163static void spu_enable(void)
164{
165 u32 regval = readl(ARM_RESET_REGISTER);
166 regval &= ~1;
167 spu_write_wait();
168 writel(regval, ARM_RESET_REGISTER);
169}
170
171/*
172 * Halt the sound processor, clear the memory,
173 * load some default ARM7 code, and then restart ARM7
174*/
175static void spu_reset(void)
176{
177 spu_disable();
178 spu_memset(0, 0, 0x200000 / 4);
179 /* Put ARM7 in endless loop */
180 ctrl_outl(0xea000002, SPU_MEMORY_BASE);
181 spu_enable();
182}
183
184/* aica_chn_start - write to spu to start playback */
185static void aica_chn_start(void)
186{
187 spu_write_wait();
188 writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);
189}
190
191/* aica_chn_halt - write to spu to halt playback */
192static void aica_chn_halt(void)
193{
194 spu_write_wait();
195 writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);
196}
197
198/* ALSA code below */
199static struct snd_pcm_hardware snd_pcm_aica_playback_hw = {
200 .info = (SNDRV_PCM_INFO_NONINTERLEAVED),
201 .formats =
202 (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
203 SNDRV_PCM_FMTBIT_IMA_ADPCM),
204 .rates = SNDRV_PCM_RATE_8000_48000,
205 .rate_min = 8000,
206 .rate_max = 48000,
207 .channels_min = 1,
208 .channels_max = 2,
209 .buffer_bytes_max = AICA_BUFFER_SIZE,
210 .period_bytes_min = AICA_PERIOD_SIZE,
211 .period_bytes_max = AICA_PERIOD_SIZE,
212 .periods_min = AICA_PERIOD_NUMBER,
213 .periods_max = AICA_PERIOD_NUMBER,
214};
215
216static int aica_dma_transfer(int channels, int buffer_size,
217 struct snd_pcm_substream *substream)
218{
219 int q, err, period_offset;
220 struct snd_card_aica *dreamcastcard;
221 struct snd_pcm_runtime *runtime;
222 err = 0;
223 dreamcastcard = substream->pcm->private_data;
224 period_offset = dreamcastcard->clicks;
225 period_offset %= (AICA_PERIOD_NUMBER / channels);
226 runtime = substream->runtime;
227 for (q = 0; q < channels; q++) {
228 err = dma_xfer(AICA_DMA_CHANNEL,
229 (unsigned long)(runtime->dma_area +
230 (AICA_BUFFER_SIZE * q) /
231 channels +
232 AICA_PERIOD_SIZE *
233 period_offset),
234 AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +
235 AICA_PERIOD_SIZE * period_offset,
236 buffer_size / channels, AICA_DMA_MODE);
237 if (unlikely(err < 0))
238 break;
239 dma_wait_for_completion(AICA_DMA_CHANNEL);
240 }
241 return err;
242}
243
244static void startup_aica(struct snd_card_aica *dreamcastcard)
245{
246 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
247 dreamcastcard->channel,
248 sizeof(struct aica_channel));
249 aica_chn_start();
250}
251
252static void run_spu_dma(struct work_struct *work)
253{
254 int buffer_size;
255 struct snd_pcm_substream *substream;
256 struct snd_pcm_runtime *runtime;
257 struct snd_card_aica *dreamcastcard;
258 struct spu_work_holder *holder = container_of(work, struct spu_work_holder, spu_dma_work);
259 substream = holder-> sspointer;
260 dreamcastcard = substream->pcm->private_data;
261 runtime = substream->runtime;
262 if (unlikely(dreamcastcard->dma_check == 0)) {
263 buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
264 if (runtime->channels > 1)
265 dreamcastcard->channel->flags |= 0x01;
266 aica_dma_transfer(runtime->channels, buffer_size, substream);
267 startup_aica(dreamcastcard);
268 dreamcastcard->clicks =
269 buffer_size / (AICA_PERIOD_SIZE * runtime->channels);
270 return;
271 } else {
272 aica_dma_transfer(runtime->channels,
273 AICA_PERIOD_SIZE * runtime->channels,
274 substream);
275 snd_pcm_period_elapsed(dreamcastcard->substream);
276 dreamcastcard->clicks++;
277 if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))
278 {
279 dreamcastcard->clicks %= AICA_PERIOD_NUMBER;
280 }
281 mod_timer(&dreamcastcard->timer, jiffies + 1);
282 }
283}
284
285static void aica_period_elapsed(unsigned long timer_var)
286{
287 /*timer function - so cannot sleep */
288 int play_period;
289 struct snd_pcm_runtime *runtime;
290 struct snd_pcm_substream *substream;
291 struct snd_card_aica *dreamcastcard;
292 substream = (struct snd_pcm_substream *)timer_var;
293 runtime = substream->runtime;
294 dreamcastcard = substream->pcm->private_data;
295 /* Have we played out an additional period? */
296 play_period =
297 frames_to_bytes(runtime,
298 readl
299 (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /
300 AICA_PERIOD_SIZE;
301 if (play_period == dreamcastcard->current_period) {
302 /* reschedule the timer */
303 mod_timer(&(dreamcastcard->timer), jiffies + 1);
304 return;
305 }
306 if (runtime->channels > 1)
307 dreamcastcard->current_period = play_period;
308 if (unlikely(dreamcastcard->dma_check == 0))
309 dreamcastcard->dma_check = 1;
310 queue_work(aica_queue, &(spu_working.spu_dma_work));
311}
312
313static void spu_begin_dma(struct snd_pcm_substream *substream)
314{
315 /* Must be atomic */
316 struct snd_card_aica *dreamcastcard;
317 struct snd_pcm_runtime *runtime;
318 runtime = substream->runtime;
319 dreamcastcard = substream->pcm->private_data;
320 /* Use queue to do the heavy lifting */
321 spu_working.sspointer = substream;
322 INIT_WORK(&(spu_working.spu_dma_work), run_spu_dma);
323 queue_work(aica_queue, &(spu_working.spu_dma_work));
324 /* Timer may already be running */
325 if (unlikely(dreamcastcard->timer.data)) {
326 mod_timer(&dreamcastcard->timer, jiffies + 4);
327 return;
328 }
329 init_timer(&(dreamcastcard->timer));
330 dreamcastcard->timer.data = (unsigned long)substream;
331 dreamcastcard->timer.function = aica_period_elapsed;
332 dreamcastcard->timer.expires = jiffies + 4;
333 add_timer(&(dreamcastcard->timer));
334}
335
336static int snd_aicapcm_pcm_open(struct snd_pcm_substream
337 *substream)
338{
339 struct snd_pcm_runtime *runtime;
340 struct aica_channel *channel;
341 struct snd_card_aica *dreamcastcard;
342 if (!enable)
343 return -ENOENT;
344 dreamcastcard = substream->pcm->private_data;
345 channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);
346 if (!channel)
347 return -ENOMEM;
348 /* set defaults for channel */
349 channel->sfmt = SM_8BIT;
350 channel->cmd = AICA_CMD_START;
351 channel->vol = dreamcastcard->master_volume;
352 channel->pan = 0x80;
353 channel->pos = 0;
354 channel->flags = 0; /* default to mono */
355 dreamcastcard->channel = channel;
356 runtime = substream->runtime;
357 runtime->hw = snd_pcm_aica_playback_hw;
358 spu_enable();
359 dreamcastcard->clicks = 0;
360 dreamcastcard->current_period = 0;
361 dreamcastcard->dma_check = 0;
362 return 0;
363}
364
365static int snd_aicapcm_pcm_close(struct snd_pcm_substream
366 *substream)
367{
368 struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
369 del_timer(&dreamcastcard->timer);
370 kfree(dreamcastcard->channel);
371 spu_disable();
372 return 0;
373}
374
375static int snd_aicapcm_pcm_hw_free(struct snd_pcm_substream
376 *substream)
377{
378 /* Free the DMA buffer */
379 return snd_pcm_lib_free_pages(substream);
380}
381
382static int snd_aicapcm_pcm_hw_params(struct snd_pcm_substream
383 *substream, struct snd_pcm_hw_params
384 *hw_params)
385{
386 /* Allocate a DMA buffer using ALSA built-ins */
387 return
388 snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));
389}
390
391static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream
392 *substream)
393{
394 struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
395 if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)
396 dreamcastcard->channel->sfmt = SM_16BIT;
397 dreamcastcard->channel->freq = substream->runtime->rate;
398 dreamcastcard->substream = substream;
399 return 0;
400}
401
402static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream
403 *substream, int cmd)
404{
405 struct snd_card_aica *dreamcastcard;
406 switch (cmd) {
407 case SNDRV_PCM_TRIGGER_START:
408 spu_begin_dma(substream);
409 break;
410 case SNDRV_PCM_TRIGGER_STOP:
411 dreamcastcard = substream->pcm->private_data;
412 if (dreamcastcard->timer.data)
413 del_timer(&dreamcastcard->timer);
414 aica_chn_halt();
415 break;
416 default:
417 return -EINVAL;
418 }
419 return 0;
420}
421
422static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
423 *substream)
424{
425 return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
426}
427
428static struct snd_pcm_ops snd_aicapcm_playback_ops = {
429 .open = snd_aicapcm_pcm_open,
430 .close = snd_aicapcm_pcm_close,
431 .ioctl = snd_pcm_lib_ioctl,
432 .hw_params = snd_aicapcm_pcm_hw_params,
433 .hw_free = snd_aicapcm_pcm_hw_free,
434 .prepare = snd_aicapcm_pcm_prepare,
435 .trigger = snd_aicapcm_pcm_trigger,
436 .pointer = snd_aicapcm_pcm_pointer,
437};
438
439/* TO DO: set up to handle more than one pcm instance */
440static int __init snd_aicapcmchip(struct snd_card_aica
441 *dreamcastcard, int pcm_index)
442{
443 struct snd_pcm *pcm;
444 int err;
445 /* AICA has no capture ability */
446 err =
447 snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0, &pcm);
448 if (unlikely(err < 0))
449 return err;
450 pcm->private_data = dreamcastcard;
451 strcpy(pcm->name, "AICA PCM");
452 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
453 &snd_aicapcm_playback_ops);
454 /* Allocate the DMA buffers */
455 err =
456 snd_pcm_lib_preallocate_pages_for_all(pcm,
457 SNDRV_DMA_TYPE_CONTINUOUS,
458 snd_dma_continuous_data
459 (GFP_KERNEL),
460 AICA_BUFFER_SIZE,
461 AICA_BUFFER_SIZE);
462 return err;
463}
464
465/* Mixer controls */
466static int aica_pcmswitch_info(struct snd_kcontrol *kcontrol,
467 struct snd_ctl_elem_info *uinfo)
468{
469 uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
470 uinfo->count = 1;
471 uinfo->value.integer.min = 0;
472 uinfo->value.integer.max = 1;
473 return 0;
474}
475
476static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,
477 struct snd_ctl_elem_value *ucontrol)
478{
479 ucontrol->value.integer.value[0] = 1; /* TO DO: Fix me */
480 return 0;
481}
482
483static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,
484 struct snd_ctl_elem_value *ucontrol)
485{
486 if (ucontrol->value.integer.value[0] == 1)
487 return 0; /* TO DO: Fix me */
488 else
489 aica_chn_halt();
490 return 0;
491}
492
493static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,
494 struct snd_ctl_elem_info *uinfo)
495{
496 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
497 uinfo->count = 1;
498 uinfo->value.integer.min = 0;
499 uinfo->value.integer.max = 0xFF;
500 return 0;
501}
502
503static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,
504 struct snd_ctl_elem_value *ucontrol)
505{
506 struct snd_card_aica *dreamcastcard;
507 dreamcastcard = kcontrol->private_data;
508 if (unlikely(!dreamcastcard->channel))
509 return -ETXTBSY; /* we've not yet been set up */
510 ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;
511 return 0;
512}
513
514static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
515 struct snd_ctl_elem_value *ucontrol)
516{
517 struct snd_card_aica *dreamcastcard;
518 dreamcastcard = kcontrol->private_data;
519 if (unlikely(!dreamcastcard->channel))
520 return -ETXTBSY;
521 if (unlikely(dreamcastcard->channel->vol ==
522 ucontrol->value.integer.value[0]))
523 return 0;
524 dreamcastcard->channel->vol = ucontrol->value.integer.value[0];
525 dreamcastcard->master_volume = ucontrol->value.integer.value[0];
526 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,
527 dreamcastcard->channel,
528 sizeof(struct aica_channel));
529
530 return 1;
531}
532
533static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
534 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
535 .name = "PCM Playback Switch",
536 .index = 0,
537 .info = aica_pcmswitch_info,
538 .get = aica_pcmswitch_get,
539 .put = aica_pcmswitch_put
540};
541
542static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
543 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
544 .name = "PCM Playback Volume",
545 .index = 0,
546 .info = aica_pcmvolume_info,
547 .get = aica_pcmvolume_get,
548 .put = aica_pcmvolume_put
549};
550
551static int load_aica_firmware(void)
552{
553 int err;
554 const struct firmware *fw_entry;
555 spu_reset();
556 err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
557 if (unlikely(err))
558 return err;
559 /* write firware into memory */
560 spu_disable();
561 spu_memload(0, fw_entry->data, fw_entry->size);
562 spu_enable();
563 release_firmware(fw_entry);
564 return err;
565}
566
567static int __devinit add_aicamixer_controls(struct snd_card_aica
568 *dreamcastcard)
569{
570 int err;
571 err = snd_ctl_add
572 (dreamcastcard->card,
573 snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));
574 if (unlikely(err < 0))
575 return err;
576 err = snd_ctl_add
577 (dreamcastcard->card,
578 snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));
579 if (unlikely(err < 0))
580 return err;
581 return 0;
582}
583
584static int snd_aica_remove(struct platform_device *devptr)
585{
586 struct snd_card_aica *dreamcastcard;
587 dreamcastcard = platform_get_drvdata(devptr);
588 if (unlikely(!dreamcastcard))
589 return -ENODEV;
590 snd_card_free(dreamcastcard->card);
591 kfree(dreamcastcard);
592 platform_set_drvdata(devptr, NULL);
593 return 0;
594}
595
596static int __init snd_aica_probe(struct platform_device *devptr)
597{
598 int err;
599 struct snd_card_aica *dreamcastcard;
600 dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL);
601 if (unlikely(!dreamcastcard))
602 return -ENOMEM;
603 dreamcastcard->card =
604 snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
605 if (unlikely(!dreamcastcard->card)) {
606 kfree(dreamcastcard);
607 return -ENODEV;
608 }
609 strcpy(dreamcastcard->card->driver, "snd_aica");
610 strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
611 strcpy(dreamcastcard->card->longname,
612 "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");
613 /* Load the PCM 'chip' */
614 err = snd_aicapcmchip(dreamcastcard, 0);
615 if (unlikely(err < 0))
616 goto freedreamcast;
617 snd_card_set_dev(dreamcastcard->card, &devptr->dev);
618 dreamcastcard->timer.data = 0;
619 dreamcastcard->channel = NULL;
620 /* Add basic controls */
621 err = add_aicamixer_controls(dreamcastcard);
622 if (unlikely(err < 0))
623 goto freedreamcast;
624 /* Register the card with ALSA subsystem */
625 err = snd_card_register(dreamcastcard->card);
626 if (unlikely(err < 0))
627 goto freedreamcast;
628 platform_set_drvdata(devptr, dreamcastcard);
629 aica_queue = create_workqueue(CARD_NAME);
630 if (unlikely(!aica_queue))
631 goto freedreamcast;
632 snd_printk
633 ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
634 return 0;
635 freedreamcast:
636 snd_card_free(dreamcastcard->card);
637 kfree(dreamcastcard);
638 return err;
639}
640
641static struct platform_driver snd_aica_driver = {
642 .probe = snd_aica_probe,
643 .remove = snd_aica_remove,
644 .driver = {
645 .name = SND_AICA_DRIVER},
646};
647
648static int __init aica_init(void)
649{
650 int err;
651 err = platform_driver_register(&snd_aica_driver);
652 if (unlikely(err < 0))
653 return err;
654 pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
655 aica_memory_space, 2);
656 if (unlikely(IS_ERR(pd))) {
657 platform_driver_unregister(&snd_aica_driver);
658 return PTR_ERR(pd);
659 }
660 /* Load the firmware */
661 return load_aica_firmware();
662}
663
664static void __exit aica_exit(void)
665{
666 /* Destroy the aica kernel thread */
667 destroy_workqueue(aica_queue);
668 platform_device_unregister(pd);
669 platform_driver_unregister(&snd_aica_driver);
670 /* Kill any sound still playing and reset ARM7 to safe state */
671 spu_reset();
672}
673
674module_init(aica_init);
675module_exit(aica_exit);
diff --git a/sound/sh/aica.h b/sound/sh/aica.h
new file mode 100644
index 000000000000..0603b5b0376b
--- /dev/null
+++ b/sound/sh/aica.h
@@ -0,0 +1,80 @@
1/* aica.h
2 * Header file for ALSA driver for
3 * Sega Dreamcast Yamaha AICA sound
4 * Copyright Adrian McMenamin
5 * <adrian@mcmen.demon.co.uk>
6 * 2006
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as published by
10 * the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 *
21 */
22
23/* SPU memory and register constants etc */
24#define G2_FIFO 0xa05f688c
25#define SPU_MEMORY_BASE 0xA0800000
26#define ARM_RESET_REGISTER 0xA0702C00
27#define SPU_REGISTER_BASE 0xA0700000
28
29/* AICA channels stuff */
30#define AICA_CONTROL_POINT 0xA0810000
31#define AICA_CONTROL_CHANNEL_SAMPLE_NUMBER 0xA0810008
32#define AICA_CHANNEL0_CONTROL_OFFSET 0x10004
33
34/* Command values */
35#define AICA_CMD_KICK 0x80000000
36#define AICA_CMD_NONE 0
37#define AICA_CMD_START 1
38#define AICA_CMD_STOP 2
39#define AICA_CMD_VOL 3
40
41/* Sound modes */
42#define SM_8BIT 1
43#define SM_16BIT 0
44#define SM_ADPCM 2
45
46/* Buffer and period size */
47#define AICA_BUFFER_SIZE 0x8000
48#define AICA_PERIOD_SIZE 0x800
49#define AICA_PERIOD_NUMBER 16
50
51#define AICA_CHANNEL0_OFFSET 0x11000
52#define AICA_CHANNEL1_OFFSET 0x21000
53#define CHANNEL_OFFSET 0x10000
54
55#define AICA_DMA_CHANNEL 0
56#define AICA_DMA_MODE 5
57
58#define SND_AICA_DRIVER "AICA"
59
60struct aica_channel {
61 uint32_t cmd; /* Command ID */
62 uint32_t pos; /* Sample position */
63 uint32_t length; /* Sample length */
64 uint32_t freq; /* Frequency */
65 uint32_t vol; /* Volume 0-255 */
66 uint32_t pan; /* Pan 0-255 */
67 uint32_t sfmt; /* Sound format */
68 uint32_t flags; /* Bit flags */
69};
70
71struct snd_card_aica {
72 struct snd_card *card;
73 struct aica_channel *channel;
74 struct snd_pcm_substream *substream;
75 int clicks;
76 int current_period;
77 struct timer_list timer;
78 int master_volume;
79 int dma_check;
80};