aica.c
字号:
init_timer(&(dreamcastcard->timer)); dreamcastcard->timer.data = (unsigned long) substream; dreamcastcard->timer.function = aica_period_elapsed; dreamcastcard->timer.expires = jiffies + 4; add_timer(&(dreamcastcard->timer));}static int snd_aicapcm_pcm_open(struct snd_pcm_substream *substream){ struct snd_pcm_runtime *runtime; struct aica_channel *channel; struct snd_card_aica *dreamcastcard; if (!enable) return -ENOENT; dreamcastcard = substream->pcm->private_data; channel = kmalloc(sizeof(struct aica_channel), GFP_KERNEL); if (!channel) return -ENOMEM; /* set defaults for channel */ channel->sfmt = SM_8BIT; channel->cmd = AICA_CMD_START; channel->vol = dreamcastcard->master_volume; channel->pan = 0x80; channel->pos = 0; channel->flags = 0; /* default to mono */ dreamcastcard->channel = channel; runtime = substream->runtime; runtime->hw = snd_pcm_aica_playback_hw; spu_enable(); dreamcastcard->clicks = 0; dreamcastcard->current_period = 0; dreamcastcard->dma_check = 0; return 0;}static int snd_aicapcm_pcm_close(struct snd_pcm_substream *substream){ struct snd_card_aica *dreamcastcard = substream->pcm->private_data; flush_workqueue(aica_queue); if (dreamcastcard->timer.data) del_timer(&dreamcastcard->timer); 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 snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params));}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){ switch (cmd) { case SNDRV_PCM_TRIGGER_START: spu_begin_dma(substream); break; case SNDRV_PCM_TRIGGER_STOP: 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 = snd_pcm_new(dreamcastcard->card, "AICA PCM", pcm_index, 1, 0, &pcm); 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 */#define aica_pcmswitch_info snd_ctl_boolean_mono_infostatic 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; dreamcastcard = kcontrol->private_data; if (unlikely(!dreamcastcard->channel)) return -ETXTBSY; if (unlikely(dreamcastcard->channel->vol == ucontrol->value.integer.value[0])) return 0; dreamcastcard->channel->vol = ucontrol->value.integer.value[0]; dreamcastcard->master_volume = ucontrol->value.integer.value[0]; spu_memload(AICA_CHANNEL0_CONTROL_OFFSET, dreamcastcard->channel, sizeof(struct aica_channel)); return 1;}static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", .index = 0, .info = aica_pcmswitch_info, .get = aica_pcmswitch_get, .put = aica_pcmswitch_put};static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, .info = aica_pcmvolume_info, .get = aica_pcmvolume_get, .put = aica_pcmvolume_put};static int load_aica_firmware(void){ int err; const struct firmware *fw_entry; spu_reset(); err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev); if (unlikely(err)) return err; /* write firware into memory */ spu_disable(); spu_memload(0, fw_entry->data, fw_entry->size); spu_enable(); release_firmware(fw_entry); return err;}static int __devinit add_aicamixer_controls(struct snd_card_aica *dreamcastcard){ int err; err = snd_ctl_add (dreamcastcard->card, snd_ctl_new1(&snd_aica_pcmvolume_control, dreamcastcard)); if (unlikely(err < 0)) return err; err = snd_ctl_add (dreamcastcard->card, snd_ctl_new1(&snd_aica_pcmswitch_control, dreamcastcard)); if (unlikely(err < 0)) return err; return 0;}static int snd_aica_remove(struct platform_device *devptr){ struct snd_card_aica *dreamcastcard; dreamcastcard = platform_get_drvdata(devptr); if (unlikely(!dreamcastcard)) return -ENODEV; snd_card_free(dreamcastcard->card); kfree(dreamcastcard); platform_set_drvdata(devptr, NULL); return 0;}static int __init snd_aica_probe(struct platform_device *devptr){ int err; struct snd_card_aica *dreamcastcard; dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL); if (unlikely(!dreamcastcard)) return -ENOMEM; dreamcastcard->card = snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0); if (unlikely(!dreamcastcard->card)) { kfree(dreamcastcard); return -ENODEV; } 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"); /* Prepare to use the queue */ INIT_WORK(&(dreamcastcard->spu_dma_work), run_spu_dma); /* Load the PCM 'chip' */ err = snd_aicapcmchip(dreamcastcard, 0); if (unlikely(err < 0)) goto freedreamcast; snd_card_set_dev(dreamcastcard->card, &devptr->dev); dreamcastcard->timer.data = 0; dreamcastcard->channel = NULL; /* Add basic controls */ err = add_aicamixer_controls(dreamcastcard); if (unlikely(err < 0)) goto freedreamcast; /* Register the card with ALSA subsystem */ err = snd_card_register(dreamcastcard->card); if (unlikely(err < 0)) goto freedreamcast; platform_set_drvdata(devptr, dreamcastcard); aica_queue = create_workqueue(CARD_NAME); if (unlikely(!aica_queue)) goto freedreamcast; snd_printk ("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n"); return 0; freedreamcast: snd_card_free(dreamcastcard->card); kfree(dreamcastcard); return err;}static struct platform_driver snd_aica_driver = { .probe = snd_aica_probe, .remove = snd_aica_remove, .driver = { .name = SND_AICA_DRIVER},};static int __init aica_init(void){ int err; err = platform_driver_register(&snd_aica_driver); if (unlikely(err < 0)) return err; pd = platform_device_register_simple(SND_AICA_DRIVER, -1, aica_memory_space, 2); if (unlikely(IS_ERR(pd))) { platform_driver_unregister(&snd_aica_driver); return PTR_ERR(pd); } /* Load the firmware */ return load_aica_firmware();}static void __exit aica_exit(void){ /* Destroy the aica kernel thread * * being extra cautious to check if it exists*/ if (likely(aica_queue)) destroy_workqueue(aica_queue); 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -