636 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3*4* Copyright Adrian McMenamin 2005, 2006, 20075* <adrian@mcmen.demon.co.uk>6* Requires firmware (BSD licenced) available from:7* http://linuxdc.cvs.sourceforge.net/linuxdc/linux-sh-dc/sound/oss/aica/firmware/8* or the maintainer9*/10 11#include <linux/init.h>12#include <linux/jiffies.h>13#include <linux/slab.h>14#include <linux/time.h>15#include <linux/wait.h>16#include <linux/module.h>17#include <linux/platform_device.h>18#include <linux/firmware.h>19#include <linux/timer.h>20#include <linux/delay.h>21#include <linux/workqueue.h>22#include <linux/io.h>23#include <sound/core.h>24#include <sound/control.h>25#include <sound/pcm.h>26#include <sound/initval.h>27#include <sound/info.h>28#include <asm/dma.h>29#include <mach/sysasic.h>30#include "aica.h"31 32MODULE_AUTHOR("Adrian McMenamin <adrian@mcmen.demon.co.uk>");33MODULE_DESCRIPTION("Dreamcast AICA sound (pcm) driver");34MODULE_LICENSE("GPL");35MODULE_FIRMWARE("aica_firmware.bin");36 37/* module parameters */38#define CARD_NAME "AICA"39static int index = -1;40static char *id;41static bool enable = 1;42module_param(index, int, 0444);43MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");44module_param(id, charp, 0444);45MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");46module_param(enable, bool, 0644);47MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");48 49/* Simple platform device */50static struct platform_device *pd;51static struct resource aica_memory_space[2] = {52 {53 .name = "AICA ARM CONTROL",54 .start = ARM_RESET_REGISTER,55 .flags = IORESOURCE_MEM,56 .end = ARM_RESET_REGISTER + 3,57 },58 {59 .name = "AICA Sound RAM",60 .start = SPU_MEMORY_BASE,61 .flags = IORESOURCE_MEM,62 .end = SPU_MEMORY_BASE + 0x200000 - 1,63 },64};65 66/* SPU specific functions */67/* spu_write_wait - wait for G2-SH FIFO to clear */68static void spu_write_wait(void)69{70 int time_count;71 time_count = 0;72 while (1) {73 if (!(readl(G2_FIFO) & 0x11))74 break;75 /* To ensure hardware failure doesn't wedge kernel */76 time_count++;77 if (time_count > 0x10000) {78 pr_warn("WARNING: G2 FIFO appears to be blocked.\n");79 break;80 }81 }82}83 84/* spu_memset - write to memory in SPU address space */85static void spu_memset(u32 toi, u32 what, int length)86{87 int i;88 unsigned long flags;89 if (snd_BUG_ON(length % 4))90 return;91 for (i = 0; i < length; i++) {92 if (!(i % 8))93 spu_write_wait();94 local_irq_save(flags);95 writel(what, toi + SPU_MEMORY_BASE);96 local_irq_restore(flags);97 toi++;98 }99}100 101/* spu_memload - write to SPU address space */102static void spu_memload(u32 toi, const void *from, int length)103{104 unsigned long flags;105 const u32 *froml = from;106 u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi);107 int i;108 u32 val;109 length = DIV_ROUND_UP(length, 4);110 spu_write_wait();111 for (i = 0; i < length; i++) {112 if (!(i % 8))113 spu_write_wait();114 val = *froml;115 local_irq_save(flags);116 writel(val, to);117 local_irq_restore(flags);118 froml++;119 to++;120 }121}122 123/* spu_disable - set spu registers to stop sound output */124static void spu_disable(void)125{126 int i;127 unsigned long flags;128 u32 regval;129 spu_write_wait();130 regval = readl(ARM_RESET_REGISTER);131 regval |= 1;132 spu_write_wait();133 local_irq_save(flags);134 writel(regval, ARM_RESET_REGISTER);135 local_irq_restore(flags);136 for (i = 0; i < 64; i++) {137 spu_write_wait();138 regval = readl(SPU_REGISTER_BASE + (i * 0x80));139 regval = (regval & ~0x4000) | 0x8000;140 spu_write_wait();141 local_irq_save(flags);142 writel(regval, SPU_REGISTER_BASE + (i * 0x80));143 local_irq_restore(flags);144 }145}146 147/* spu_enable - set spu registers to enable sound output */148static void spu_enable(void)149{150 unsigned long flags;151 u32 regval = readl(ARM_RESET_REGISTER);152 regval &= ~1;153 spu_write_wait();154 local_irq_save(flags);155 writel(regval, ARM_RESET_REGISTER);156 local_irq_restore(flags);157}158 159/* 160 * Halt the sound processor, clear the memory,161 * load some default ARM7 code, and then restart ARM7162*/163static void spu_reset(void)164{165 unsigned long flags;166 spu_disable();167 spu_memset(0, 0, 0x200000 / 4);168 /* Put ARM7 in endless loop */169 local_irq_save(flags);170 __raw_writel(0xea000002, SPU_MEMORY_BASE);171 local_irq_restore(flags);172 spu_enable();173}174 175/* aica_chn_start - write to spu to start playback */176static void aica_chn_start(void)177{178 unsigned long flags;179 spu_write_wait();180 local_irq_save(flags);181 writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT);182 local_irq_restore(flags);183}184 185/* aica_chn_halt - write to spu to halt playback */186static void aica_chn_halt(void)187{188 unsigned long flags;189 spu_write_wait();190 local_irq_save(flags);191 writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT);192 local_irq_restore(flags);193}194 195/* ALSA code below */196static const struct snd_pcm_hardware snd_pcm_aica_playback_hw = {197 .info = (SNDRV_PCM_INFO_NONINTERLEAVED),198 .formats =199 (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |200 SNDRV_PCM_FMTBIT_IMA_ADPCM),201 .rates = SNDRV_PCM_RATE_8000_48000,202 .rate_min = 8000,203 .rate_max = 48000,204 .channels_min = 1,205 .channels_max = 2,206 .buffer_bytes_max = AICA_BUFFER_SIZE,207 .period_bytes_min = AICA_PERIOD_SIZE,208 .period_bytes_max = AICA_PERIOD_SIZE,209 .periods_min = AICA_PERIOD_NUMBER,210 .periods_max = AICA_PERIOD_NUMBER,211};212 213static int aica_dma_transfer(int channels, int buffer_size,214 struct snd_pcm_substream *substream)215{216 int q, err, period_offset;217 struct snd_card_aica *dreamcastcard;218 struct snd_pcm_runtime *runtime;219 unsigned long flags;220 err = 0;221 dreamcastcard = substream->pcm->private_data;222 period_offset = dreamcastcard->clicks;223 period_offset %= (AICA_PERIOD_NUMBER / channels);224 runtime = substream->runtime;225 for (q = 0; q < channels; q++) {226 local_irq_save(flags);227 err = dma_xfer(AICA_DMA_CHANNEL,228 (unsigned long) (runtime->dma_area +229 (AICA_BUFFER_SIZE * q) /230 channels +231 AICA_PERIOD_SIZE *232 period_offset),233 AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET +234 AICA_PERIOD_SIZE * period_offset,235 buffer_size / channels, AICA_DMA_MODE);236 if (unlikely(err < 0)) {237 local_irq_restore(flags);238 break;239 }240 dma_wait_for_completion(AICA_DMA_CHANNEL);241 local_irq_restore(flags);242 }243 return err;244}245 246static void startup_aica(struct snd_card_aica *dreamcastcard)247{248 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,249 dreamcastcard->channel, sizeof(struct aica_channel));250 aica_chn_start();251}252 253static void run_spu_dma(struct work_struct *work)254{255 int buffer_size;256 struct snd_pcm_runtime *runtime;257 struct snd_card_aica *dreamcastcard;258 dreamcastcard =259 container_of(work, struct snd_card_aica, spu_dma_work);260 runtime = dreamcastcard->substream->runtime;261 if (unlikely(dreamcastcard->dma_check == 0)) {262 buffer_size =263 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,267 dreamcastcard->substream);268 startup_aica(dreamcastcard);269 dreamcastcard->clicks =270 buffer_size / (AICA_PERIOD_SIZE * runtime->channels);271 return;272 } else {273 aica_dma_transfer(runtime->channels,274 AICA_PERIOD_SIZE * runtime->channels,275 dreamcastcard->substream);276 snd_pcm_period_elapsed(dreamcastcard->substream);277 dreamcastcard->clicks++;278 if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER))279 dreamcastcard->clicks %= AICA_PERIOD_NUMBER;280 if (snd_pcm_running(dreamcastcard->substream))281 mod_timer(&dreamcastcard->timer, jiffies + 1);282 }283}284 285static void aica_period_elapsed(struct timer_list *t)286{287 struct snd_card_aica *dreamcastcard = from_timer(dreamcastcard,288 t, timer);289 struct snd_pcm_substream *substream = dreamcastcard->substream;290 /*timer function - so cannot sleep */291 int play_period;292 struct snd_pcm_runtime *runtime;293 if (!snd_pcm_running(substream))294 return;295 runtime = substream->runtime;296 dreamcastcard = substream->pcm->private_data;297 /* Have we played out an additional period? */298 play_period =299 frames_to_bytes(runtime,300 readl301 (AICA_CONTROL_CHANNEL_SAMPLE_NUMBER)) /302 AICA_PERIOD_SIZE;303 if (play_period == dreamcastcard->current_period) {304 /* reschedule the timer */305 mod_timer(&(dreamcastcard->timer), jiffies + 1);306 return;307 }308 if (runtime->channels > 1)309 dreamcastcard->current_period = play_period;310 if (unlikely(dreamcastcard->dma_check == 0))311 dreamcastcard->dma_check = 1;312 schedule_work(&(dreamcastcard->spu_dma_work));313}314 315static void spu_begin_dma(struct snd_pcm_substream *substream)316{317 struct snd_card_aica *dreamcastcard;318 struct snd_pcm_runtime *runtime;319 runtime = substream->runtime;320 dreamcastcard = substream->pcm->private_data;321 /*get the queue to do the work */322 schedule_work(&(dreamcastcard->spu_dma_work));323 mod_timer(&dreamcastcard->timer, jiffies + 4);324}325 326static int snd_aicapcm_pcm_open(struct snd_pcm_substream327 *substream)328{329 struct snd_pcm_runtime *runtime;330 struct aica_channel *channel;331 struct snd_card_aica *dreamcastcard;332 if (!enable)333 return -ENOENT;334 dreamcastcard = substream->pcm->private_data;335 channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL);336 if (!channel)337 return -ENOMEM;338 /* set defaults for channel */339 channel->sfmt = SM_8BIT;340 channel->cmd = AICA_CMD_START;341 channel->vol = dreamcastcard->master_volume;342 channel->pan = 0x80;343 channel->pos = 0;344 channel->flags = 0; /* default to mono */345 dreamcastcard->channel = channel;346 runtime = substream->runtime;347 runtime->hw = snd_pcm_aica_playback_hw;348 spu_enable();349 dreamcastcard->clicks = 0;350 dreamcastcard->current_period = 0;351 dreamcastcard->dma_check = 0;352 return 0;353}354 355static int snd_aicapcm_pcm_sync_stop(struct snd_pcm_substream *substream)356{357 struct snd_card_aica *dreamcastcard = substream->pcm->private_data;358 359 del_timer_sync(&dreamcastcard->timer);360 cancel_work_sync(&dreamcastcard->spu_dma_work);361 return 0;362}363 364static int snd_aicapcm_pcm_close(struct snd_pcm_substream365 *substream)366{367 struct snd_card_aica *dreamcastcard = substream->pcm->private_data;368 dreamcastcard->substream = NULL;369 kfree(dreamcastcard->channel);370 spu_disable();371 return 0;372}373 374static int snd_aicapcm_pcm_prepare(struct snd_pcm_substream375 *substream)376{377 struct snd_card_aica *dreamcastcard = substream->pcm->private_data;378 if ((substream->runtime)->format == SNDRV_PCM_FORMAT_S16_LE)379 dreamcastcard->channel->sfmt = SM_16BIT;380 dreamcastcard->channel->freq = substream->runtime->rate;381 dreamcastcard->substream = substream;382 return 0;383}384 385static int snd_aicapcm_pcm_trigger(struct snd_pcm_substream386 *substream, int cmd)387{388 switch (cmd) {389 case SNDRV_PCM_TRIGGER_START:390 spu_begin_dma(substream);391 break;392 case SNDRV_PCM_TRIGGER_STOP:393 aica_chn_halt();394 break;395 default:396 return -EINVAL;397 }398 return 0;399}400 401static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream402 *substream)403{404 return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER);405}406 407static const struct snd_pcm_ops snd_aicapcm_playback_ops = {408 .open = snd_aicapcm_pcm_open,409 .close = snd_aicapcm_pcm_close,410 .prepare = snd_aicapcm_pcm_prepare,411 .trigger = snd_aicapcm_pcm_trigger,412 .pointer = snd_aicapcm_pcm_pointer,413 .sync_stop = snd_aicapcm_pcm_sync_stop,414};415 416/* TO DO: set up to handle more than one pcm instance */417static int __init snd_aicapcmchip(struct snd_card_aica418 *dreamcastcard, int pcm_index)419{420 struct snd_pcm *pcm;421 int err;422 /* AICA has no capture ability */423 err =424 snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0,425 &pcm);426 if (unlikely(err < 0))427 return err;428 pcm->private_data = dreamcastcard;429 strcpy(pcm->name, "AICA PCM");430 snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,431 &snd_aicapcm_playback_ops);432 /* Allocate the DMA buffers */433 snd_pcm_set_managed_buffer_all(pcm,434 SNDRV_DMA_TYPE_CONTINUOUS,435 NULL,436 AICA_BUFFER_SIZE,437 AICA_BUFFER_SIZE);438 return 0;439}440 441/* Mixer controls */442#define aica_pcmswitch_info snd_ctl_boolean_mono_info443 444static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol,445 struct snd_ctl_elem_value *ucontrol)446{447 ucontrol->value.integer.value[0] = 1; /* TO DO: Fix me */448 return 0;449}450 451static int aica_pcmswitch_put(struct snd_kcontrol *kcontrol,452 struct snd_ctl_elem_value *ucontrol)453{454 if (ucontrol->value.integer.value[0] == 1)455 return 0; /* TO DO: Fix me */456 else457 aica_chn_halt();458 return 0;459}460 461static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol,462 struct snd_ctl_elem_info *uinfo)463{464 uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;465 uinfo->count = 1;466 uinfo->value.integer.min = 0;467 uinfo->value.integer.max = 0xFF;468 return 0;469}470 471static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol,472 struct snd_ctl_elem_value *ucontrol)473{474 struct snd_card_aica *dreamcastcard;475 dreamcastcard = kcontrol->private_data;476 if (unlikely(!dreamcastcard->channel))477 return -ETXTBSY; /* we've not yet been set up */478 ucontrol->value.integer.value[0] = dreamcastcard->channel->vol;479 return 0;480}481 482static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,483 struct snd_ctl_elem_value *ucontrol)484{485 struct snd_card_aica *dreamcastcard;486 unsigned int vol;487 dreamcastcard = kcontrol->private_data;488 if (unlikely(!dreamcastcard->channel))489 return -ETXTBSY;490 vol = ucontrol->value.integer.value[0];491 if (vol > 0xff)492 return -EINVAL;493 if (unlikely(dreamcastcard->channel->vol == vol))494 return 0;495 dreamcastcard->channel->vol = ucontrol->value.integer.value[0];496 dreamcastcard->master_volume = ucontrol->value.integer.value[0];497 spu_memload(AICA_CHANNEL0_CONTROL_OFFSET,498 dreamcastcard->channel, sizeof(struct aica_channel));499 return 1;500}501 502static const struct snd_kcontrol_new snd_aica_pcmswitch_control = {503 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,504 .name = "PCM Playback Switch",505 .index = 0,506 .info = aica_pcmswitch_info,507 .get = aica_pcmswitch_get,508 .put = aica_pcmswitch_put509};510 511static const struct snd_kcontrol_new snd_aica_pcmvolume_control = {512 .iface = SNDRV_CTL_ELEM_IFACE_MIXER,513 .name = "PCM Playback Volume",514 .index = 0,515 .info = aica_pcmvolume_info,516 .get = aica_pcmvolume_get,517 .put = aica_pcmvolume_put518};519 520static int load_aica_firmware(void)521{522 int err;523 const struct firmware *fw_entry;524 spu_reset();525 err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);526 if (unlikely(err))527 return err;528 /* write firmware into memory */529 spu_disable();530 spu_memload(0, fw_entry->data, fw_entry->size);531 spu_enable();532 release_firmware(fw_entry);533 return err;534}535 536static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)537{538 int err;539 err = snd_ctl_add540 (dreamcastcard->card,541 snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard));542 if (unlikely(err < 0))543 return err;544 err = snd_ctl_add545 (dreamcastcard->card,546 snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard));547 if (unlikely(err < 0))548 return err;549 return 0;550}551 552static void snd_aica_remove(struct platform_device *devptr)553{554 struct snd_card_aica *dreamcastcard;555 dreamcastcard = platform_get_drvdata(devptr);556 snd_card_free(dreamcastcard->card);557 kfree(dreamcastcard);558}559 560static int snd_aica_probe(struct platform_device *devptr)561{562 int err;563 struct snd_card_aica *dreamcastcard;564 dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL);565 if (unlikely(!dreamcastcard))566 return -ENOMEM;567 err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER,568 THIS_MODULE, 0, &dreamcastcard->card);569 if (unlikely(err < 0)) {570 kfree(dreamcastcard);571 return err;572 }573 strcpy(dreamcastcard->card->driver, "snd_aica");574 strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);575 strcpy(dreamcastcard->card->longname,576 "Yamaha AICA Super Intelligent Sound Processor for SEGA Dreamcast");577 /* Prepare to use the queue */578 INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma);579 timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0);580 /* Load the PCM 'chip' */581 err = snd_aicapcmchip(dreamcastcard, 0);582 if (unlikely(err < 0))583 goto freedreamcast;584 /* Add basic controls */585 err = add_aicamixer_controls(dreamcastcard);586 if (unlikely(err < 0))587 goto freedreamcast;588 /* Register the card with ALSA subsystem */589 err = snd_card_register(dreamcastcard->card);590 if (unlikely(err < 0))591 goto freedreamcast;592 platform_set_drvdata(devptr, dreamcastcard);593 dev_info(&devptr->dev,594 "ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");595 return 0;596 freedreamcast:597 snd_card_free(dreamcastcard->card);598 kfree(dreamcastcard);599 return err;600}601 602static struct platform_driver snd_aica_driver = {603 .probe = snd_aica_probe,604 .remove_new = snd_aica_remove,605 .driver = {606 .name = SND_AICA_DRIVER,607 },608};609 610static int __init aica_init(void)611{612 int err;613 err = platform_driver_register(&snd_aica_driver);614 if (unlikely(err < 0))615 return err;616 pd = platform_device_register_simple(SND_AICA_DRIVER, -1,617 aica_memory_space, 2);618 if (IS_ERR(pd)) {619 platform_driver_unregister(&snd_aica_driver);620 return PTR_ERR(pd);621 }622 /* Load the firmware */623 return load_aica_firmware();624}625 626static void __exit aica_exit(void)627{628 platform_device_unregister(pd);629 platform_driver_unregister(&snd_aica_driver);630 /* Kill any sound still playing and reset ARM7 to safe state */631 spu_reset();632}633 634module_init(aica_init);635module_exit(aica_exit);636