diff options
Diffstat (limited to 'sound/sh')
| -rw-r--r-- | sound/sh/Kconfig | 25 | ||||
| -rw-r--r-- | sound/sh/Makefile | 2 | ||||
| -rw-r--r-- | sound/sh/aica.c | 87 | ||||
| -rw-r--r-- | sound/sh/sh_dac_audio.c | 443 |
4 files changed, 521 insertions, 36 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 739786529ca..47849eaf266 100644 --- a/sound/sh/aica.c +++ b/sound/sh/aica.c @@ -29,13 +29,12 @@ #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> #include <linux/delay.h> #include <linux/workqueue.h> -#include <sound/driver.h> #include <sound/core.h> #include <sound/control.h> #include <sound/pcm.h> @@ -43,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,11 +106,15 @@ static void spu_write_wait(void) static void spu_memset(u32 toi, u32 what, int length) { int i; - snd_assert(length % 4 == 0, return); + unsigned long flags; + if (snd_BUG_ON(length % 4)) + return; for (i = 0; i < length; i++) { if (!(i % 8)) spu_write_wait(); + local_irq_save(flags); writel(what, toi + SPU_MEMORY_BASE); + local_irq_restore(flags); toi++; } } @@ -118,6 +122,7 @@ 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) { + unsigned long flags; u32 *froml = from; u32 __iomem *to = (u32 __iomem *) (SPU_MEMORY_BASE + toi); int i; @@ -128,7 +133,9 @@ static void spu_memload(u32 toi, void *from, int length) if (!(i % 8)) spu_write_wait(); val = *froml; + local_irq_save(flags); writel(val, to); + local_irq_restore(flags); froml++; to++; } @@ -138,28 +145,36 @@ static void spu_memload(u32 toi, void *from, int length) static void spu_disable(void) { int i; + unsigned long flags; u32 regval; spu_write_wait(); regval = readl(ARM_RESET_REGISTER); regval |= 1; spu_write_wait(); + local_irq_save(flags); writel(regval, ARM_RESET_REGISTER); + local_irq_restore(flags); for (i = 0; i < 64; i++) { spu_write_wait(); regval = readl(SPU_REGISTER_BASE + (i * 0x80)); regval = (regval & ~0x4000) | 0x8000; spu_write_wait(); + local_irq_save(flags); writel(regval, SPU_REGISTER_BASE + (i * 0x80)); + local_irq_restore(flags); } } /* spu_enable - set spu registers to enable sound output */ static void spu_enable(void) { + unsigned long flags; u32 regval = readl(ARM_RESET_REGISTER); regval &= ~1; spu_write_wait(); + local_irq_save(flags); writel(regval, ARM_RESET_REGISTER); + local_irq_restore(flags); } /* @@ -168,25 +183,34 @@ static void spu_enable(void) */ static void spu_reset(void) { + unsigned long flags; spu_disable(); spu_memset(0, 0, 0x200000 / 4); /* Put ARM7 in endless loop */ - ctrl_outl(0xea000002, SPU_MEMORY_BASE); + local_irq_save(flags); + __raw_writel(0xea000002, SPU_MEMORY_BASE); + local_irq_restore(flags); spu_enable(); } /* aica_chn_start - write to spu to start playback */ static void aica_chn_start(void) { + unsigned long flags; spu_write_wait(); + local_irq_save(flags); writel(AICA_CMD_KICK | AICA_CMD_START, (u32 *) AICA_CONTROL_POINT); + local_irq_restore(flags); } /* aica_chn_halt - write to spu to halt playback */ static void aica_chn_halt(void) { + unsigned long flags; spu_write_wait(); + local_irq_save(flags); writel(AICA_CMD_KICK | AICA_CMD_STOP, (u32 *) AICA_CONTROL_POINT); + local_irq_restore(flags); } /* ALSA code below */ @@ -213,12 +237,14 @@ static int aica_dma_transfer(int channels, int buffer_size, int q, err, period_offset; struct snd_card_aica *dreamcastcard; struct snd_pcm_runtime *runtime; + unsigned long flags; err = 0; dreamcastcard = substream->pcm->private_data; period_offset = dreamcastcard->clicks; period_offset %= (AICA_PERIOD_NUMBER / channels); runtime = substream->runtime; for (q = 0; q < channels; q++) { + local_irq_save(flags); err = dma_xfer(AICA_DMA_CHANNEL, (unsigned long) (runtime->dma_area + (AICA_BUFFER_SIZE * q) / @@ -228,9 +254,12 @@ static int aica_dma_transfer(int channels, int buffer_size, AICA_CHANNEL0_OFFSET + q * CHANNEL_OFFSET + AICA_PERIOD_SIZE * period_offset, buffer_size / channels, AICA_DMA_MODE); - if (unlikely(err < 0)) + if (unlikely(err < 0)) { + local_irq_restore(flags); break; + } dma_wait_for_completion(AICA_DMA_CHANNEL); + local_irq_restore(flags); } return err; } @@ -451,15 +480,7 @@ static int __init snd_aicapcmchip(struct snd_card_aica } /* Mixer controls */ -static int aica_pcmswitch_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *uinfo) -{ - uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; - uinfo->count = 1; - uinfo->value.integer.min = 0; - uinfo->value.integer.max = 1; - return 0; -} +#define aica_pcmswitch_info snd_ctl_boolean_mono_info static int aica_pcmswitch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -503,11 +524,14 @@ static int aica_pcmvolume_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_card_aica *dreamcastcard; + unsigned int vol; dreamcastcard = kcontrol->private_data; if (unlikely(!dreamcastcard->channel)) return -ETXTBSY; - if (unlikely(dreamcastcard->channel->vol == - ucontrol->value.integer.value[0])) + vol = ucontrol->value.integer.value[0]; + if (vol > 0xff) + return -EINVAL; + if (unlikely(dreamcastcard->channel->vol == vol)) return 0; dreamcastcard->channel->vol = ucontrol->value.integer.value[0]; dreamcastcard->master_volume = ucontrol->value.integer.value[0]; @@ -516,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, @@ -525,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, @@ -542,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(); @@ -550,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 @@ -575,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); @@ -602,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 */ @@ -630,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) @@ -641,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); |
