Commit 198de43d758ca2700e2b52b49c0b189b4931466c

Authored by Adrian McMenamin
Committed by Jaroslav Kysela
1 parent aef3b06ac6

[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>

Showing 6 changed files with 780 additions and 1 deletions Side-by-side Diff

... ... @@ -65,6 +65,8 @@
65 65  
66 66 source "sound/mips/Kconfig"
67 67  
  68 +source "sound/sh/Kconfig"
  69 +
68 70 # the following will depend on the order of config.
69 71 # here assuming USB is defined before ALSA
70 72 source "sound/usb/Kconfig"
... ... @@ -5,7 +5,7 @@
5 5 obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
6 6 obj-$(CONFIG_SOUND_PRIME) += oss/
7 7 obj-$(CONFIG_DMASOUND) += oss/
8   -obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
  8 +obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ soc/
9 9 obj-$(CONFIG_SND_AOA) += aoa/
10 10  
11 11 # This one must be compilable even if sound is configured out
  1 +# ALSA SH drivers
  2 +
  3 +menu "SUPERH devices"
  4 + depends on SND!=n && SUPERH
  5 +
  6 +config 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 +
  13 +endmenu
  1 +#
  2 +# Makefile for ALSA
  3 +#
  4 +
  5 +snd-aica-objs := aica.o
  6 +
  7 +# Toplevel Module Dependency
  8 +obj-$(CONFIG_SND_AICA) += snd-aica.o
  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 +
  49 +MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");
  50 +MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");
  51 +MODULE_LICENSE("GPL");
  52 +MODULE_SUPPORTED_DEVICE("{{Yamaha/SEGA, AICA}}");
  53 +
  54 +/* module parameters */
  55 +#define CARD_NAME "AICA"
  56 +static int index = -1;
  57 +static char *id;
  58 +static int enable = 1;
  59 +module_param(index, int, 0444);
  60 +MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
  61 +module_param(id, charp, 0444);
  62 +MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
  63 +module_param(enable, bool, 0644);
  64 +MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
  65 +
  66 +/* Use workqueue */
  67 +
  68 +static struct spu_work_holder {
  69 + struct work_struct spu_dma_work;
  70 + void *sspointer;
  71 +} spu_working;
  72 +
  73 +static struct workqueue_struct *aica_queue;
  74 +
  75 +/* Simple platform device */
  76 +static struct platform_device *pd;
  77 +static 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 */
  94 +static 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 */
  112 +static 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 */
  125 +static 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 */
  144 +static 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 */
  163 +static 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 +*/
  175 +static 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 */
  185 +static 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 */
  192 +static 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 */
  199 +static 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 +
  216 +static 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 +
  244 +static 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 +
  252 +static 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 +
  285 +static 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 +
  313 +static 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 +
  336 +static 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 +
  365 +static 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 +
  375 +static 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 +
  382 +static 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 +
  391 +static 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 +
  402 +static 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 +
  422 +static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream
  423 + *substream)
  424 +{
  425 + return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);
  426 +}
  427 +
  428 +static 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 */
  440 +static 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 */
  466 +static 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 +
  476 +static 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 +
  483 +static 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 +
  493 +static 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 +
  503 +static 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 +
  514 +static 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 +
  533 +static 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 +
  542 +static 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 +
  551 +static 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 +
  567 +static 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 +
  584 +static 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 +
  596 +static 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 +
  641 +static 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 +
  648 +static 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 +
  664 +static 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 +
  674 +module_init(aica_init);
  675 +module_exit(aica_exit);
  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 +
  60 +struct 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 +
  71 +struct 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 +};