aboutsummaryrefslogtreecommitdiff
path: root/sound/sh
diff options
context:
space:
mode:
Diffstat (limited to 'sound/sh')
-rw-r--r--sound/sh/Kconfig25
-rw-r--r--sound/sh/Makefile2
-rw-r--r--sound/sh/aica.c39
-rw-r--r--sound/sh/sh_dac_audio.c443
4 files changed, 486 insertions, 23 deletions
diff --git a/sound/sh/Kconfig b/sound/sh/Kconfig
index b7e08ef22a9..61139f3c161 100644
--- a/sound/sh/Kconfig
+++ b/sound/sh/Kconfig
@@ -1,14 +1,31 @@
# ALSA SH drivers
-menu "SUPERH devices"
- depends on SND!=n && SUPERH
+menuconfig SND_SUPERH
+ bool "SUPERH sound devices"
+ depends on SUPERH
+ default y
+ help
+ Support for sound devices specific to SUPERH architectures.
+ Drivers that are implemented on ASoC can be found in
+ "ALSA for SoC audio support" section.
+
+if SND_SUPERH
config SND_AICA
tristate "Dreamcast Yamaha AICA sound"
- depends on SH_DREAMCAST && SND
+ depends on SH_DREAMCAST
select SND_PCM
+ select G2_DMA
help
ALSA Sound driver for the SEGA Dreamcast console.
-endmenu
+config SND_SH_DAC_AUDIO
+ tristate "SuperH DAC audio support"
+ depends on SND
+ depends on CPU_SH3 && HIGH_RES_TIMERS
+ select SND_PCM
+ help
+ Say Y here to include support for the on-chip DAC.
+
+endif # SND_SUPERH
diff --git a/sound/sh/Makefile b/sound/sh/Makefile
index 8fdcb6e26f0..7d09b5188cf 100644
--- a/sound/sh/Makefile
+++ b/sound/sh/Makefile
@@ -3,6 +3,8 @@
#
snd-aica-objs := aica.o
+snd-sh_dac_audio-objs := sh_dac_audio.o
# Toplevel Module Dependency
obj-$(CONFIG_SND_AICA) += snd-aica.o
+obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd-sh_dac_audio.o
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index d49417bf78c..47849eaf266 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -29,7 +29,7 @@
#include <linux/slab.h>
#include <linux/time.h>
#include <linux/wait.h>
-#include <linux/moduleparam.h>
+#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <linux/timer.h>
@@ -42,19 +42,20 @@
#include <sound/info.h>
#include <asm/io.h>
#include <asm/dma.h>
-#include <asm/dreamcast/sysasic.h>
+#include <mach/sysasic.h>
#include "aica.h"
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 */
#define CARD_NAME "AICA"
static int index = -1;
static char *id;
-static int enable = 1;
+static bool enable = 1;
module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
module_param(id, charp, 0444);
@@ -106,7 +107,8 @@ static void spu_memset(u32 toi, u32 what, int length)
{
int i;
unsigned long flags;
- snd_assert(length % 4 == 0, return);
+ if (snd_BUG_ON(length % 4))
+ return;
for (i = 0; i < length; i++) {
if (!(i % 8))
spu_write_wait();
@@ -186,7 +188,7 @@ static void spu_reset(void)
spu_memset(0, 0, 0x200000 / 4);
/* Put ARM7 in endless loop */
local_irq_save(flags);
- ctrl_outl(0xea000002, SPU_MEMORY_BASE);
+ __raw_writel(0xea000002, SPU_MEMORY_BASE);
local_irq_restore(flags);
spu_enable();
}
@@ -538,7 +540,7 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
+static struct snd_kcontrol_new snd_aica_pcmswitch_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,
@@ -547,7 +549,7 @@ static struct snd_kcontrol_new snd_aica_pcmswitch_control __devinitdata = {
.put = aica_pcmswitch_put
};
-static struct snd_kcontrol_new snd_aica_pcmvolume_control __devinitdata = {
+static struct snd_kcontrol_new snd_aica_pcmvolume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
.index = 0,
@@ -564,7 +566,7 @@ static int load_aica_firmware(void)
err = request_firmware(&fw_entry, "aica_firmware.bin", &pd->dev);
if (unlikely(err))
return err;
- /* write firware into memory */
+ /* write firmware into memory */
spu_disable();
spu_memload(0, fw_entry->data, fw_entry->size);
spu_enable();
@@ -572,8 +574,7 @@ static int load_aica_firmware(void)
return err;
}
-static int __devinit add_aicamixer_controls(struct snd_card_aica
- *dreamcastcard)
+static int add_aicamixer_controls(struct snd_card_aica *dreamcastcard)
{
int err;
err = snd_ctl_add
@@ -597,22 +598,21 @@ static int snd_aica_remove(struct platform_device *devptr)
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)
+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);
if (unlikely(!dreamcastcard))
return -ENOMEM;
- dreamcastcard->card =
- snd_card_new(index, SND_AICA_DRIVER, THIS_MODULE, 0);
- if (unlikely(!dreamcastcard->card)) {
+ err = snd_card_new(&devptr->dev, index, SND_AICA_DRIVER,
+ THIS_MODULE, 0, &dreamcastcard->card);
+ if (unlikely(err < 0)) {
kfree(dreamcastcard);
- return -ENODEV;
+ return err;
}
strcpy(dreamcastcard->card->driver, "snd_aica");
strcpy(dreamcastcard->card->shortname, SND_AICA_DRIVER);
@@ -624,7 +624,6 @@ static int __init snd_aica_probe(struct platform_device *devptr)
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 */
@@ -652,7 +651,9 @@ static struct platform_driver snd_aica_driver = {
.probe = snd_aica_probe,
.remove = snd_aica_remove,
.driver = {
- .name = SND_AICA_DRIVER},
+ .name = SND_AICA_DRIVER,
+ .owner = THIS_MODULE,
+ },
};
static int __init aica_init(void)
@@ -663,7 +664,7 @@ static int __init aica_init(void)
return err;
pd = platform_device_register_simple(SND_AICA_DRIVER, -1,
aica_memory_space, 2);
- if (unlikely(IS_ERR(pd))) {
+ if (IS_ERR(pd)) {
platform_driver_unregister(&snd_aica_driver);
return PTR_ERR(pd);
}
diff --git a/sound/sh/sh_dac_audio.c b/sound/sh/sh_dac_audio.c
new file mode 100644
index 00000000000..d1fb74dabbd
--- /dev/null
+++ b/sound/sh/sh_dac_audio.c
@@ -0,0 +1,443 @@
+/*
+ * sh_dac_audio.c - SuperH DAC audio driver for ALSA
+ *
+ * Copyright (c) 2009 by Rafael Ignacio Zurita <rizurita@yahoo.com>
+ *
+ *
+ * Based on sh_dac_audio.c (Copyright (C) 2004, 2005 by Andriy Skulysh)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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/hrtimer.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/sh_dac_audio.h>
+#include <asm/clock.h>
+#include <asm/hd64461.h>
+#include <mach/hp6xx.h>
+#include <cpu/dac.h>
+
+MODULE_AUTHOR("Rafael Ignacio Zurita <rizurita@yahoo.com>");
+MODULE_DESCRIPTION("SuperH DAC audio driver");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{SuperH DAC audio support}}");
+
+/* Module Parameters */
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for SuperH DAC audio.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for SuperH DAC audio.");
+
+/* main struct */
+struct snd_sh_dac {
+ struct snd_card *card;
+ struct snd_pcm_substream *substream;
+ struct hrtimer hrtimer;
+ ktime_t wakeups_per_second;
+
+ int rate;
+ int empty;
+ char *data_buffer, *buffer_begin, *buffer_end;
+ int processed; /* bytes proccesed, to compare with period_size */
+ int buffer_size;
+ struct dac_audio_pdata *pdata;
+};
+
+
+static void dac_audio_start_timer(struct snd_sh_dac *chip)
+{
+ hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+ HRTIMER_MODE_REL);
+}
+
+static void dac_audio_stop_timer(struct snd_sh_dac *chip)
+{
+ hrtimer_cancel(&chip->hrtimer);
+}
+
+static void dac_audio_reset(struct snd_sh_dac *chip)
+{
+ dac_audio_stop_timer(chip);
+ chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+ chip->processed = 0;
+ chip->empty = 1;
+}
+
+static void dac_audio_set_rate(struct snd_sh_dac *chip)
+{
+ chip->wakeups_per_second = ktime_set(0, 1000000000 / chip->rate);
+}
+
+
+/* PCM INTERFACE */
+
+static struct snd_pcm_hardware snd_sh_dac_pcm_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_HALF_DUPLEX),
+ .formats = SNDRV_PCM_FMTBIT_U8,
+ .rates = SNDRV_PCM_RATE_8000,
+ .rate_min = 8000,
+ .rate_max = 8000,
+ .channels_min = 1,
+ .channels_max = 1,
+ .buffer_bytes_max = (48*1024),
+ .period_bytes_min = 1,
+ .period_bytes_max = (48*1024),
+ .periods_min = 1,
+ .periods_max = 1024,
+};
+
+static int snd_sh_dac_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ runtime->hw = snd_sh_dac_pcm_hw;
+
+ chip->substream = substream;
+ chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+ chip->processed = 0;
+ chip->empty = 1;
+
+ chip->pdata->start(chip->pdata);
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+ chip->substream = NULL;
+
+ dac_audio_stop_timer(chip);
+ chip->pdata->stop(chip->pdata);
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ return snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+}
+
+static int snd_sh_dac_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int snd_sh_dac_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = chip->substream->runtime;
+
+ chip->buffer_size = runtime->buffer_size;
+ memset(chip->data_buffer, 0, chip->pdata->buffer_size);
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ dac_audio_start_timer(chip);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ chip->buffer_begin = chip->buffer_end = chip->data_buffer;
+ chip->processed = 0;
+ chip->empty = 1;
+ dac_audio_stop_timer(chip);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_copy(struct snd_pcm_substream *substream, int channel,
+ snd_pcm_uframes_t pos, void __user *src, snd_pcm_uframes_t count)
+{
+ /* channel is not used (interleaved data) */
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ssize_t b_count = frames_to_bytes(runtime , count);
+ ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+ if (count < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ memcpy_toio(chip->data_buffer + b_pos, src, b_count);
+ chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+ if (chip->empty) {
+ chip->empty = 0;
+ dac_audio_start_timer(chip);
+ }
+
+ return 0;
+}
+
+static int snd_sh_dac_pcm_silence(struct snd_pcm_substream *substream,
+ int channel, snd_pcm_uframes_t pos,
+ snd_pcm_uframes_t count)
+{
+ /* channel is not used (interleaved data) */
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ ssize_t b_count = frames_to_bytes(runtime , count);
+ ssize_t b_pos = frames_to_bytes(runtime , pos);
+
+ if (count < 0)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ memset_io(chip->data_buffer + b_pos, 0, b_count);
+ chip->buffer_end = chip->data_buffer + b_pos + b_count;
+
+ if (chip->empty) {
+ chip->empty = 0;
+ dac_audio_start_timer(chip);
+ }
+
+ return 0;
+}
+
+static
+snd_pcm_uframes_t snd_sh_dac_pcm_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream);
+ int pointer = chip->buffer_begin - chip->data_buffer;
+
+ return pointer;
+}
+
+/* pcm ops */
+static struct snd_pcm_ops snd_sh_dac_pcm_ops = {
+ .open = snd_sh_dac_pcm_open,
+ .close = snd_sh_dac_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = snd_sh_dac_pcm_hw_params,
+ .hw_free = snd_sh_dac_pcm_hw_free,
+ .prepare = snd_sh_dac_pcm_prepare,
+ .trigger = snd_sh_dac_pcm_trigger,
+ .pointer = snd_sh_dac_pcm_pointer,
+ .copy = snd_sh_dac_pcm_copy,
+ .silence = snd_sh_dac_pcm_silence,
+ .mmap = snd_pcm_lib_mmap_iomem,
+};
+
+static int snd_sh_dac_pcm(struct snd_sh_dac *chip, int device)
+{
+ int err;
+ struct snd_pcm *pcm;
+
+ /* device should be always 0 for us */
+ err = snd_pcm_new(chip->card, "SH_DAC PCM", device, 1, 0, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = chip;
+ strcpy(pcm->name, "SH_DAC PCM");
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sh_dac_pcm_ops);
+
+ /* buffer size=48K */
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL),
+ 48 * 1024,
+ 48 * 1024);
+
+ return 0;
+}
+/* END OF PCM INTERFACE */
+
+
+/* driver .remove -- destructor */
+static int snd_sh_dac_remove(struct platform_device *devptr)
+{
+ snd_card_free(platform_get_drvdata(devptr));
+ return 0;
+}
+
+/* free -- it has been defined by create */
+static int snd_sh_dac_free(struct snd_sh_dac *chip)
+{
+ /* release the data */
+ kfree(chip->data_buffer);
+ kfree(chip);
+
+ return 0;
+}
+
+static int snd_sh_dac_dev_free(struct snd_device *device)
+{
+ struct snd_sh_dac *chip = device->device_data;
+
+ return snd_sh_dac_free(chip);
+}
+
+static enum hrtimer_restart sh_dac_audio_timer(struct hrtimer *handle)
+{
+ struct snd_sh_dac *chip = container_of(handle, struct snd_sh_dac,
+ hrtimer);
+ struct snd_pcm_runtime *runtime = chip->substream->runtime;
+ ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size);
+
+ if (!chip->empty) {
+ sh_dac_output(*chip->buffer_begin, chip->pdata->channel);
+ chip->buffer_begin++;
+
+ chip->processed++;
+ if (chip->processed >= b_ps) {
+ chip->processed -= b_ps;
+ snd_pcm_period_elapsed(chip->substream);
+ }
+
+ if (chip->buffer_begin == (chip->data_buffer +
+ chip->buffer_size - 1))
+ chip->buffer_begin = chip->data_buffer;
+
+ if (chip->buffer_begin == chip->buffer_end)
+ chip->empty = 1;
+
+ }
+
+ if (!chip->empty)
+ hrtimer_start(&chip->hrtimer, chip->wakeups_per_second,
+ HRTIMER_MODE_REL);
+
+ return HRTIMER_NORESTART;
+}
+
+/* create -- chip-specific constructor for the cards components */
+static int snd_sh_dac_create(struct snd_card *card,
+ struct platform_device *devptr,
+ struct snd_sh_dac **rchip)
+{
+ struct snd_sh_dac *chip;
+ int err;
+
+ static struct snd_device_ops ops = {
+ .dev_free = snd_sh_dac_dev_free,
+ };
+
+ *rchip = NULL;
+
+ chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->card = card;
+
+ hrtimer_init(&chip->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ chip->hrtimer.function = sh_dac_audio_timer;
+
+ dac_audio_reset(chip);
+ chip->rate = 8000;
+ dac_audio_set_rate(chip);
+
+ chip->pdata = devptr->dev.platform_data;
+
+ chip->data_buffer = kmalloc(chip->pdata->buffer_size, GFP_KERNEL);
+ if (chip->data_buffer == NULL) {
+ kfree(chip);
+ return -ENOMEM;
+ }
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops);
+ if (err < 0) {
+ snd_sh_dac_free(chip);
+ return err;
+ }
+
+ *rchip = chip;
+
+ return 0;
+}
+
+/* driver .probe -- constructor */
+static int snd_sh_dac_probe(struct platform_device *devptr)
+{
+ struct snd_sh_dac *chip;
+ struct snd_card *card;
+ int err;
+
+ err = snd_card_new(&devptr->dev, index, id, THIS_MODULE, 0, &card);
+ if (err < 0) {
+ snd_printk(KERN_ERR "cannot allocate the card\n");
+ return err;
+ }
+
+ err = snd_sh_dac_create(card, devptr, &chip);
+ if (err < 0)
+ goto probe_error;
+
+ err = snd_sh_dac_pcm(chip, 0);
+ if (err < 0)
+ goto probe_error;
+
+ strcpy(card->driver, "snd_sh_dac");
+ strcpy(card->shortname, "SuperH DAC audio driver");
+ printk(KERN_INFO "%s %s", card->longname, card->shortname);
+
+ err = snd_card_register(card);
+ if (err < 0)
+ goto probe_error;
+
+ snd_printk("ALSA driver for SuperH DAC audio");
+
+ platform_set_drvdata(devptr, card);
+ return 0;
+
+probe_error:
+ snd_card_free(card);
+ return err;
+}
+
+/*
+ * "driver" definition
+ */
+static struct platform_driver sh_dac_driver = {
+ .probe = snd_sh_dac_probe,
+ .remove = snd_sh_dac_remove,
+ .driver = {
+ .name = "dac_audio",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(sh_dac_driver);