diff options
author | Kristoffer Ericson <kristoffer.ericson@gmail.com> | 2009-12-27 20:25:22 +0100 |
---|---|---|
committer | Kristoffer Ericson <kristoffer.ericson@gmail.com> | 2009-12-27 20:25:22 +0100 |
commit | 6e7bc0863c7228f02045b439c9ebf54b7ef67469 (patch) | |
tree | c91b91801085fdbb29db4a7d7c24f2d45b6d4f56 /recipes/linux/linux-jlime-jornada6xx-2.6.17/alsa_hp6xx_2.6.17.patch | |
parent | 371a4860922514c2ae28c0209c8b0c6ccc41d623 (diff) |
Updated JLiMe 2.6.17 kernel. Changes include addition of alsa driver and also rtc.
Signed-off-by: Alex Ferguson <thoughtmonster@gmail.com>
Signed-off-by: Kristoffer Ericson <kristoffer.ericson@gmail.com>
Diffstat (limited to 'recipes/linux/linux-jlime-jornada6xx-2.6.17/alsa_hp6xx_2.6.17.patch')
-rw-r--r-- | recipes/linux/linux-jlime-jornada6xx-2.6.17/alsa_hp6xx_2.6.17.patch | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/recipes/linux/linux-jlime-jornada6xx-2.6.17/alsa_hp6xx_2.6.17.patch b/recipes/linux/linux-jlime-jornada6xx-2.6.17/alsa_hp6xx_2.6.17.patch new file mode 100644 index 0000000000..bca97bc87b --- /dev/null +++ b/recipes/linux/linux-jlime-jornada6xx-2.6.17/alsa_hp6xx_2.6.17.patch @@ -0,0 +1,636 @@ +diff -up linux-2.6.17/sound/sh/Kconfig linux-2.6.17.alsa/sound/sh/Kconfig +--- linux-2.6.17/sound/sh/Kconfig 2009-04-05 12:58:14.000000000 -0300 ++++ linux-2.6.17.alsa/sound/sh/Kconfig 2009-04-05 11:41:34.000000000 -0300 +@@ -12,4 +12,13 @@ config SND_AICA + To compile this driver as a module, choose M here: the module + will be called snd-aica. + ++config SND_SH_DAC_AUDIO ++ tristate "SuperH DAC audio support" ++ depends on SND ++ depends on CPU_SH3 ++ select SND_PCM ++ help ++ Alsa Sound driver for the HP Jornada 680/690 and ++ HP Palmtop 620lx/660lx. ++ + endmenu +diff -up linux-2.6.17/sound/sh/Makefile linux-2.6.17.alsa/sound/sh/Makefile +--- linux-2.6.17/sound/sh/Makefile 2009-04-05 12:58:14.000000000 -0300 ++++ linux-2.6.17.alsa/sound/sh/Makefile 2009-04-05 11:41:34.000000000 -0300 +@@ -1,4 +1,4 @@ + + snd-aica-objs := aica.o + obj-$(CONFIG_SND_AICA) += snd-aica.o +- ++obj-$(CONFIG_SND_SH_DAC_AUDIO) += snd_sh_dac_audio.o +diff -up linux-2.6.17/sound/sh/snd_sh_dac_audio.c linux-2.6.17.alsa/sound/sh/snd_sh_dac_audio.c +--- linux-2.6.17/sound/sh/snd_sh_dac_audio.c 2009-04-05 13:00:51.000000000 -0300 ++++ linux-2.6.17.alsa/sound/sh/snd_sh_dac_audio.c 2009-04-05 12:24:23.000000000 -0300 +@@ -0,0 +1,606 @@ ++/* ++ * snd_sh_dac_audio.c - SuperH DAC audio driver for ALSA ++ * ++ * Copyright (c) 2007 by Rafael Ignacio Zurita <rizurita@yahoo.com> ++ * ++ * ++ * Based completely on sh_dac_audio.c (Copyright (C) 2004,2005 by Andriy ++ * Skulysh) and "Writing an ALSA driver" (Copyright (c) 2002-2005 by Takashi ++ * Iwai <tiwai@suse.de>). ++ * ++ * 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/interrupt.h> ++#include <linux/platform_device.h> ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/initval.h> ++#include <asm/io.h> ++#include <asm/clock.h> ++#include <asm/cpu-sh3/dac.h> ++#include <asm/hp6xx/hp6xx.h> ++#include <asm/irq.h> ++#include <asm/hd64461.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."); ++ ++/* Simple platform device */ ++static struct platform_device *pd; ++ ++#define SND_SH_DAC_DRIVER "SH_DAC" ++#define BUFFER_SIZE 64000 ++#define SH_DAC_AUDIO_CHANNEL 1 ++ ++#define HD64461_TMU_TIMR_TMU0 0x01 ++#define HD64461_TMU_TIDR (CONFIG_HD64461_IOBASE + 0x600e) ++#define HD64461_TMU_TIRR_TMU0 0x01 ++#define HD64461_TMU_TIRR (CONFIG_HD64461_IOBASE + 0x600c) ++#define HD64461_TMU_TCVR0 (CONFIG_HD64461_IOBASE + 0x6002) ++#define PKDR_SPEAKER 0x20 ++#define PKDR 0xa4000132 ++#define HD64461_GPADR_SPEAKER 0x01 ++#define HD64461_TMU_TCR0 (CONFIG_HD64461_IOBASE + 0x600a) ++#define HD64461_TMU_TCR_STRT 0x01 /* Start Counting */ ++ ++ ++/* main struct */ ++struct snd_sh_dac { ++ struct snd_card *card; ++ struct snd_pcm_substream *substream; ++ int irq; ++ ++ int rate; ++ int empty; ++ char *data_buffer, *buffer_begin, *buffer_end; ++ int processed; /* bytes proccesed, to compare with period_size */ ++ int buffer_size; ++}; ++ ++ ++/* ++ * Hardware functions (timer, DAC, speaker) ++ * Note: The driver uses a hd64461 timer ++ */ ++ ++static void dac_audio_start_timer(void) ++{ ++ u16 tmu_tcr; ++ ++ /*printk("dac_audio_start_timer\n"); */ ++ /* Start HD64461 timer 0 countdown */ ++ tmu_tcr = inb(HD64461_TMU_TCR0); ++ tmu_tcr |= HD64461_TMU_TCR_STRT; ++ outb(tmu_tcr, HD64461_TMU_TCR0); ++} ++ ++static void dac_audio_stop_timer(void) ++{ ++ u16 tmu_tcr; ++ ++ /*printk("dac_audio_stop_timer\n"); */ ++ /* Stop HD64461 timer 0 countdown */ ++ tmu_tcr = inb(HD64461_TMU_TCR0); ++ tmu_tcr &= ~HD64461_TMU_TCR_STRT; ++ outb(tmu_tcr, HD64461_TMU_TCR0); ++} ++ ++static void dac_audio_reset(struct snd_sh_dac *chip) ++{ ++ /*printk("dac_audio_reset\n"); */ ++ dac_audio_stop_timer(); ++ ++ chip->buffer_begin = chip->buffer_end = chip->data_buffer; ++ chip->processed = 0; ++ chip->empty = 1; ++} ++ ++static void dac_audio_sync(struct snd_sh_dac *chip) ++{ ++ /*printk("dac_audio_sync\n"); */ ++ while (!chip->empty) ++ schedule(); ++} ++ ++static void dac_audio_start(void) ++{ ++ u16 v; ++ u8 v8; ++ ++ /*printk("dac_audio_start\n"); */ ++ if (mach_is_hp6xx()) { ++ /* HP Jornada 680/690 speaker on */ ++ v = inw(HD64461_GPADR); ++ v &= ~HD64461_GPADR_SPEAKER; ++ outw(v, HD64461_GPADR); ++ ++ /* HP Palmtop 620lx/660lx speaker on */ ++ v8 = inb(PKDR); ++ v8 &= ~PKDR_SPEAKER; ++ outb(v8, PKDR); ++ } ++ ++ sh_dac_enable(SH_DAC_AUDIO_CHANNEL); ++} ++ ++static void dac_audio_stop(void) ++{ ++ u16 v; ++ u8 v8; ++ ++ /*printk("dac_audio_stop\n"); */ ++ dac_audio_stop_timer(); ++ ++ if (mach_is_hp6xx()) { ++ /* HP Jornada 680/690 speaker off */ ++ v = inw(HD64461_GPADR); ++ v |= HD64461_GPADR_SPEAKER; ++ outw(v, HD64461_GPADR); ++ ++ /* HP Palmtop 620lx/660lx speaker off */ ++ v8 = inb(PKDR); ++ v8 |= PKDR_SPEAKER; ++ outb(v8, PKDR); ++ } ++ ++ sh_dac_output(0, SH_DAC_AUDIO_CHANNEL); ++ sh_dac_disable(SH_DAC_AUDIO_CHANNEL); ++} ++ ++static void dac_audio_set_rate(int rate) ++{ ++ unsigned long interval; ++ struct clk *clk; ++ ++ /*printk("dac_audio_set_rate\n"); */ ++ /* ++ * Constant is autoloaded once zero is reached ++ * We need 8K interrupts per second ++ */ ++ /* clk = clk_get(NULL, "module_clk"); */ ++ clk = clk_get("module_clk"); ++ interval = (clk_get_rate(clk) / 16) / rate; ++ ctrl_outl(interval, HD64461_TMU_TCVR0); ++} ++ ++/* end of the hardware functions */ ++ ++ ++/* 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; ++ ++ /*printk("snd_sh_dac_pcm_open\n"); */ ++ 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; ++ ++ dac_audio_start(); ++ ++ return 0; ++} ++ ++static int snd_sh_dac_pcm_close(struct snd_pcm_substream *substream) ++{ ++ struct snd_sh_dac *chip = snd_pcm_substream_chip(substream); ++ ++ /*printk("snd_sh_dac_pcm_close\n"); */ ++ dac_audio_sync(chip); ++ dac_audio_stop(); ++ ++ return 0; ++} ++ ++static int snd_sh_dac_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hw_params) ++{ ++ /*printk("snd_sh_dac_pcm_hw_params\n"); */ ++ 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) ++{ ++ /*printk("snd_sh_dac_pcm_hw_free\n"); */ ++ /* Free the buffer */ ++ 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; ++ /*printk("snd_sh_dac_pcm_prepare\n"); */ ++ chip->buffer_size = runtime->buffer_size; ++ memset(chip->data_buffer, 0, 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); ++ /*printk("snd_sh_dac_pcm_trigger\n"); */ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ dac_audio_start_timer(); ++ 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(); ++ 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); ++ ++ /*printk("snd_sh_dac_pcm_copy\n"); */ ++ if (count < 0) ++ return -EINVAL; ++ ++ if (!count) { ++ dac_audio_sync(chip); ++ 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(); ++ } ++ ++ 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); ++ ++ /*printk("snd_sh_dac_pcm_silence\n"); */ ++ if (count < 0) ++ return -EINVAL; ++ ++ if (!count) { ++ dac_audio_sync(chip); ++ 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(); ++ } ++ 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; ++ ++ /*printk("snd_pcm_uframes_t \n"); */ ++ 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 __devinit snd_sh_dac_pcm(struct snd_sh_dac *chip, int device) ++{ ++ int err; ++ struct snd_pcm *pcm; ++ /*printk("snd_sh_dac_pcm\n"); */ ++ /* 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) ++{ ++ /*printk("snd_sh_dac_remove\n"); */ ++ snd_card_free(platform_get_drvdata(devptr)); ++ platform_set_drvdata(devptr, NULL); ++ return 0; ++} ++ ++/* free -- it has been defined by create */ ++static int snd_sh_dac_free(struct snd_sh_dac *chip) ++{ ++ /*printk("snd_sh_dac_free\n"); */ ++ /* release the irq */ ++ free_irq(chip->irq, (void *)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; ++ /*printk("snd_sh_dac_dev_free\n"); */ ++ return snd_sh_dac_free(chip); ++} ++ ++static irqreturn_t snd_sh_dac_interrupt(int irq, void *pdev, struct pt_regs *regs) ++{ ++ u16 timer_status; ++ struct snd_sh_dac *chip = (struct snd_sh_dac *) pdev; ++ struct snd_pcm_runtime *runtime = chip->substream->runtime; ++ ssize_t b_ps = frames_to_bytes(runtime, runtime->period_size); ++ ++/* printk("snd_sh_dac_interrupt\n"); */ ++ /* HD64461_TMU0 mask interrupt */ ++ timer_status = inw(HD64461_TMU_TIRR); ++ timer_status &= ~HD64461_TMU_TIRR_TMU0; ++ outw(timer_status, HD64461_TMU_TIRR); ++ ++ if (!chip->empty) { ++ sh_dac_output(*chip->buffer_begin, SH_DAC_AUDIO_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; ++ dac_audio_stop_timer(); ++ } ++ ++ } ++ return IRQ_HANDLED; ++} ++ ++/* create -- chip-specific constructor for the cards components */ ++static int __devinit snd_sh_dac_create(struct snd_card *card, ++ struct platform_device *devptr, ++ struct snd_sh_dac **rchip) ++{ ++ struct snd_sh_dac *chip; ++ int err; ++ u16 timer_status; ++ u16 hd64461_stbcr; ++ ++ static struct snd_device_ops ops = { ++ .dev_free = snd_sh_dac_dev_free, ++ }; ++ ++ /*printk("snd_sh_dac_create\n"); */ ++ *rchip = NULL; ++ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (chip == NULL) ++ return -ENOMEM; ++ ++ /* initialize the stuff */ ++ chip->card = card; ++ chip->irq = -1; ++ ++ /* Hardware Initialization */ ++ /* get standby status of all devices */ ++ hd64461_stbcr = inw(HD64461_STBCR); ++ /* remove standby mode for timer 0 */ ++ hd64461_stbcr &= ~HD64461_STBCR_STM0ST; ++ outw(hd64461_stbcr, HD64461_STBCR); ++ ++ dac_audio_reset(chip); ++ chip->rate = 8000; ++ dac_audio_set_rate(chip->rate); ++ ++ /* set bit interrupt service request */ ++ timer_status = inw(HD64461_TMU_TIRR); ++ timer_status &= ~HD64461_TMU_TIRR_TMU0; ++ outw(timer_status, HD64461_TMU_TIRR); ++ ++ /* set interrupt service request mask bit */ ++ timer_status = inw(HD64461_TMU_TIDR); ++ timer_status &= ~HD64461_TMU_TIMR_TMU0; ++ outw(timer_status, HD64461_TMU_TIDR); ++ ++ chip->data_buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); ++ if (chip->data_buffer == NULL) ++ return -ENOMEM; ++ ++ /* if (request_irq(HD64461_IRQ_TMU0, snd_sh_dac_interrupt, IRQF_DISABLED, */ ++ if (request_irq(HD64461_IRQ_TMU0, snd_sh_dac_interrupt, SA_INTERRUPT, ++ "snd_sh_dac", (void *)chip)) { ++ snd_sh_dac_free(chip); ++ printk(KERN_ERR "cannot grab irq\n"); ++ return -EBUSY; ++ } ++ chip->irq = HD64461_IRQ_TMU0; ++ ++ if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { ++ snd_sh_dac_free(chip); ++ return err; ++ } ++ *rchip = chip; ++ ++ return 0; ++} ++ ++/* driver .probe -- constructor */ ++static int __devinit snd_sh_dac_probe(struct platform_device *devptr) ++{ ++ struct snd_sh_dac *chip; ++ struct snd_card *card; ++ int err; ++ ++ card = snd_card_new(index, id, THIS_MODULE, 0); ++ if (card == NULL) ++ return -ENOMEM; ++ ++ 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"); ++ /* sprintk(card->longname, "%s at HD64461 irq %i", card->shortname, */ ++ printk("%s %s at HD64461 irq %i", card->longname, card->shortname, ++ chip->irq); ++ ++ 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 driver = { ++ .probe = snd_sh_dac_probe, ++ .remove = snd_sh_dac_remove, ++ .driver = { ++ .name = SND_SH_DAC_DRIVER, ++ }, ++}; ++ ++/* clean up the module */ ++static void __exit sh_dac_exit(void) ++{ ++ /*printk("sh_dac_exit\n"); */ ++ platform_device_unregister(pd); ++ platform_driver_unregister(&driver); ++ ++ free_irq(HD64461_IRQ_TMU0, 0); ++} ++ ++ ++static int __init sh_dac_init(void) ++{ ++ int err; ++ /*printk("sh_dac_init\n"); */ ++ err = platform_driver_register(&driver); ++ if (unlikely(err < 0)) ++ return err; ++ ++ pd = platform_device_register_simple(SND_SH_DAC_DRIVER, -1, NULL, 0); ++ if (unlikely(IS_ERR(pd))) { ++ platform_driver_unregister(&driver); ++ return PTR_ERR(pd); ++ } ++ ++ return 0; ++} ++ ++module_init(sh_dac_init); ++module_exit(sh_dac_exit); |