diff options
Diffstat (limited to 'sound/sh/aica.c')
| -rw-r--r-- | sound/sh/aica.c | 160 |
1 files changed, 52 insertions, 108 deletions
diff --git a/sound/sh/aica.c b/sound/sh/aica.c index 78a369785a9e..fa81bfba59c1 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -1,27 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* -* 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> @@ -35,12 +19,12 @@ #include <linux/timer.h> #include <linux/delay.h> #include <linux/workqueue.h> +#include <linux/io.h> #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> #include <sound/initval.h> #include <sound/info.h> -#include <asm/io.h> #include <asm/dma.h> #include <mach/sysasic.h> #include "aica.h" @@ -48,7 +32,6 @@ 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}}"); MODULE_FIRMWARE("aica_firmware.bin"); /* module parameters */ @@ -63,9 +46,6 @@ MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); module_param(enable, bool, 0644); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); -/* Use workqueue */ -static struct workqueue_struct *aica_queue; - /* Simple platform device */ static struct platform_device *pd; static struct resource aica_memory_space[2] = { @@ -95,8 +75,7 @@ static void spu_write_wait(void) /* To ensure hardware failure doesn't wedge kernel */ time_count++; if (time_count > 0x10000) { - snd_printk - ("WARNING: G2 FIFO appears to be blocked.\n"); + pr_warn("WARNING: G2 FIFO appears to be blocked.\n"); break; } } @@ -120,10 +99,10 @@ static void spu_memset(u32 toi, u32 what, int length) } /* spu_memload - write to SPU address space */ -static void spu_memload(u32 toi, void *from, int length) +static void spu_memload(u32 toi, const void *from, int length) { unsigned long flags; - u32 *froml = from; + const u32 *froml = from; u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi); int i; u32 val; @@ -214,7 +193,7 @@ static void aica_chn_halt(void) } /* ALSA code below */ -static struct snd_pcm_hardware snd_pcm_aica_playback_hw = { +static const struct snd_pcm_hardware snd_pcm_aica_playback_hw = { .info = (SNDRV_PCM_INFO_NONINTERLEAVED), .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | @@ -298,18 +277,21 @@ static void run_spu_dma(struct work_struct *work) dreamcastcard->clicks++; if (unlikely(dreamcastcard->clicks >= AICA_PERIOD_NUMBER)) dreamcastcard->clicks %= AICA_PERIOD_NUMBER; - mod_timer(&dreamcastcard->timer, jiffies + 1); + if (snd_pcm_running(dreamcastcard->substream)) + mod_timer(&dreamcastcard->timer, jiffies + 1); } } -static void aica_period_elapsed(unsigned long timer_var) +static void aica_period_elapsed(struct timer_list *t) { + struct snd_card_aica *dreamcastcard = timer_container_of(dreamcastcard, + t, timer); + struct snd_pcm_substream *substream = dreamcastcard->substream; /*timer function - so cannot sleep */ int play_period; struct snd_pcm_runtime *runtime; - struct snd_pcm_substream *substream; - struct snd_card_aica *dreamcastcard; - substream = (struct snd_pcm_substream *) timer_var; + if (!snd_pcm_running(substream)) + return; runtime = substream->runtime; dreamcastcard = substream->pcm->private_data; /* Have we played out an additional period? */ @@ -327,27 +309,16 @@ static void aica_period_elapsed(unsigned long timer_var) dreamcastcard->current_period = play_period; if (unlikely(dreamcastcard->dma_check == 0)) dreamcastcard->dma_check = 1; - queue_work(aica_queue, &(dreamcastcard->spu_dma_work)); + schedule_work(&(dreamcastcard->spu_dma_work)); } static void spu_begin_dma(struct snd_pcm_substream *substream) { struct snd_card_aica *dreamcastcard; - struct snd_pcm_runtime *runtime; - runtime = substream->runtime; dreamcastcard = substream->pcm->private_data; /*get the queue to do the work */ - queue_work(aica_queue, &(dreamcastcard->spu_dma_work)); - /* Timer may already be running */ - if (unlikely(dreamcastcard->timer.data)) { - mod_timer(&dreamcastcard->timer, jiffies + 4); - return; - } - 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)); + schedule_work(&(dreamcastcard->spu_dma_work)); + mod_timer(&dreamcastcard->timer, jiffies + 4); } static int snd_aicapcm_pcm_open(struct snd_pcm_substream @@ -379,35 +350,25 @@ static int snd_aicapcm_pcm_open(struct snd_pcm_substream return 0; } +static int snd_aicapcm_pcm_sync_stop(struct snd_pcm_substream *substream) +{ + struct snd_card_aica *dreamcastcard = substream->pcm->private_data; + + timer_delete_sync(&dreamcastcard->timer); + cancel_work_sync(&dreamcastcard->spu_dma_work); + 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); + dreamcastcard->substream = NULL; 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) { @@ -441,15 +402,13 @@ static unsigned long snd_aicapcm_pcm_pointer(struct snd_pcm_substream return readl(AICA_CONTROL_CHANNEL_SAMPLE_NUMBER); } -static struct snd_pcm_ops snd_aicapcm_playback_ops = { +static const 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, + .sync_stop = snd_aicapcm_pcm_sync_stop, }; /* TO DO: set up to handle more than one pcm instance */ @@ -465,18 +424,16 @@ static int __init snd_aicapcmchip(struct snd_card_aica if (unlikely(err < 0)) return err; pcm->private_data = dreamcastcard; - strcpy(pcm->name, "AICA PCM"); + strscpy(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; + snd_pcm_set_managed_buffer_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + NULL, + AICA_BUFFER_SIZE, + AICA_BUFFER_SIZE); + return 0; } /* Mixer controls */ @@ -512,8 +469,8 @@ static int aica_pcmvolume_info(struct snd_kcontrol *kcontrol, static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_card_aica *dreamcastcard; - dreamcastcard = kcontrol->private_data; + struct snd_card_aica *dreamcastcard = snd_kcontrol_chip(kcontrol); + if (unlikely(!dreamcastcard->channel)) return -ETXTBSY; /* we've not yet been set up */ ucontrol->value.integer.value[0] = dreamcastcard->channel->vol; @@ -523,9 +480,9 @@ static int aica_pcmvolume_get(struct snd_kcontrol *kcontrol, static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { - struct snd_card_aica *dreamcastcard; + struct snd_card_aica *dreamcastcard = snd_kcontrol_chip(kcontrol); unsigned int vol; - dreamcastcard = kcontrol->private_data; + if (unlikely(!dreamcastcard->channel)) return -ETXTBSY; vol = ucontrol->value.integer.value[0]; @@ -540,7 +497,7 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new snd_aica_pcmswitch_control = { +static const struct snd_kcontrol_new snd_aica_pcmswitch_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Switch", .index = 0, @@ -549,7 +506,7 @@ static struct snd_kcontrol_new snd_aica_pcmswitch_control = { .put = aica_pcmswitch_put }; -static struct snd_kcontrol_new snd_aica_pcmvolume_control = { +static const struct snd_kcontrol_new snd_aica_pcmvolume_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "PCM Playback Volume", .index = 0, @@ -590,43 +547,38 @@ static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard) return 0; } -static int snd_aica_remove(struct platform_device *devptr) +static void 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); - return 0; } static int snd_aica_probe(struct platform_device *devptr) { int err; struct snd_card_aica *dreamcastcard; - dreamcastcard = kmalloc(sizeof(struct snd_card_aica), GFP_KERNEL); + dreamcastcard = kzalloc(sizeof(struct snd_card_aica), GFP_KERNEL); if (unlikely(!dreamcastcard)) return -ENOMEM; - err = snd_card_create(index, SND_AICA_DRIVER, THIS_MODULE, 0, - &dreamcastcard->card); + err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER, + THIS_MODULE, 0, &dreamcastcard->card); if (unlikely(err < 0)) { kfree(dreamcastcard); return err; } - strcpy(dreamcastcard->card->driver, "snd_aica"); - strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); - strcpy(dreamcastcard->card->longname, + strscpy(dreamcastcard->card->driver, "snd_aica"); + strscpy(dreamcastcard->card->shortname, SND_AICA_DRIVER); + strscpy(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); + timer_setup(&dreamcastcard->timer, aica_period_elapsed, 0); /* 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)) @@ -636,11 +588,8 @@ static int snd_aica_probe(struct platform_device *devptr) 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"); + dev_info(&devptr->dev, + "ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n"); return 0; freedreamcast: snd_card_free(dreamcastcard->card); @@ -653,7 +602,6 @@ static struct platform_driver snd_aica_driver = { .remove = snd_aica_remove, .driver = { .name = SND_AICA_DRIVER, - .owner = THIS_MODULE, }, }; @@ -675,10 +623,6 @@ static int __init aica_init(void) 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 */ |
