|
@@ -37,6 +37,7 @@
|
|
|
#include <linux/ioport.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
#include <linux/init.h>
|
|
|
+#include <linux/platform_device.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/module.h>
|
|
|
#include <sound/core.h>
|
|
@@ -98,6 +99,7 @@ struct snd_au1000 {
|
|
|
|
|
|
struct snd_pcm *pcm;
|
|
|
struct audio_stream *stream[2]; /* playback & capture */
|
|
|
+ int dmaid[2]; /* tx(0)/rx(1) DMA ids */
|
|
|
};
|
|
|
|
|
|
/*--------------------------- Local Functions --------------------------------*/
|
|
@@ -465,15 +467,17 @@ snd_au1000_pcm_new(struct snd_au1000 *au1000)
|
|
|
spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
|
|
|
|
|
|
flags = claim_dma_lock();
|
|
|
- if ((au1000->stream[PLAYBACK]->dma = request_au1000_dma(DMA_ID_AC97C_TX,
|
|
|
+ au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0],
|
|
|
"AC97 TX", au1000_dma_interrupt, 0,
|
|
|
- au1000->stream[PLAYBACK])) < 0) {
|
|
|
+ au1000->stream[PLAYBACK]);
|
|
|
+ if (au1000->stream[PLAYBACK]->dma < 0) {
|
|
|
release_dma_lock(flags);
|
|
|
return -EBUSY;
|
|
|
}
|
|
|
- if ((au1000->stream[CAPTURE]->dma = request_au1000_dma(DMA_ID_AC97C_RX,
|
|
|
+ au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1],
|
|
|
"AC97 RX", au1000_dma_interrupt, 0,
|
|
|
- au1000->stream[CAPTURE])) < 0){
|
|
|
+ au1000->stream[CAPTURE]);
|
|
|
+ if (au1000->stream[CAPTURE]->dma < 0){
|
|
|
release_dma_lock(flags);
|
|
|
return -EBUSY;
|
|
|
}
|
|
@@ -552,69 +556,12 @@ get the interrupt driven case to work efficiently */
|
|
|
spin_unlock(&au1000->ac97_lock);
|
|
|
}
|
|
|
|
|
|
-static int
|
|
|
-snd_au1000_ac97_new(struct snd_au1000 *au1000)
|
|
|
-{
|
|
|
- int err;
|
|
|
- struct snd_ac97_bus *pbus;
|
|
|
- struct snd_ac97_template ac97;
|
|
|
- static struct snd_ac97_bus_ops ops = {
|
|
|
- .write = snd_au1000_ac97_write,
|
|
|
- .read = snd_au1000_ac97_read,
|
|
|
- };
|
|
|
-
|
|
|
- if ((au1000->ac97_res_port = request_mem_region(CPHYSADDR(AC97C_CONFIG),
|
|
|
- 0x100000, "Au1x00 AC97")) == NULL) {
|
|
|
- snd_printk(KERN_ERR "ALSA AC97: can't grap AC97 port\n");
|
|
|
- return -EBUSY;
|
|
|
- }
|
|
|
- au1000->ac97_ioport = (struct au1000_ac97_reg *)
|
|
|
- KSEG1ADDR(au1000->ac97_res_port->start);
|
|
|
-
|
|
|
- spin_lock_init(&au1000->ac97_lock);
|
|
|
-
|
|
|
- /* configure pins for AC'97
|
|
|
- TODO: move to board_setup.c */
|
|
|
- au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
|
|
|
-
|
|
|
- /* Initialise Au1000's AC'97 Control Block */
|
|
|
- au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
|
|
|
- udelay(10);
|
|
|
- au1000->ac97_ioport->cntrl = AC97C_CE;
|
|
|
- udelay(10);
|
|
|
-
|
|
|
- /* Initialise External CODEC -- cold reset */
|
|
|
- au1000->ac97_ioport->config = AC97C_RESET;
|
|
|
- udelay(10);
|
|
|
- au1000->ac97_ioport->config = 0x0;
|
|
|
- mdelay(5);
|
|
|
-
|
|
|
- /* Initialise AC97 middle-layer */
|
|
|
- if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0)
|
|
|
- return err;
|
|
|
-
|
|
|
- memset(&ac97, 0, sizeof(ac97));
|
|
|
- ac97.private_data = au1000;
|
|
|
- if ((err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97)) < 0)
|
|
|
- return err;
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
/*------------------------------ Setup / Destroy ----------------------------*/
|
|
|
|
|
|
-void
|
|
|
-snd_au1000_free(struct snd_card *card)
|
|
|
+static void snd_au1000_free(struct snd_card *card)
|
|
|
{
|
|
|
struct snd_au1000 *au1000 = card->private_data;
|
|
|
|
|
|
- if (au1000->ac97_res_port) {
|
|
|
- /* put internal AC97 block into reset */
|
|
|
- au1000->ac97_ioport->cntrl = AC97C_RS;
|
|
|
- au1000->ac97_ioport = NULL;
|
|
|
- release_and_free_resource(au1000->ac97_res_port);
|
|
|
- }
|
|
|
-
|
|
|
if (au1000->stream[PLAYBACK]) {
|
|
|
if (au1000->stream[PLAYBACK]->dma >= 0)
|
|
|
free_au1000_dma(au1000->stream[PLAYBACK]->dma);
|
|
@@ -626,71 +573,167 @@ snd_au1000_free(struct snd_card *card)
|
|
|
free_au1000_dma(au1000->stream[CAPTURE]->dma);
|
|
|
kfree(au1000->stream[CAPTURE]);
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
+ if (au1000->ac97_res_port) {
|
|
|
+ /* put internal AC97 block into reset */
|
|
|
+ if (au1000->ac97_ioport) {
|
|
|
+ au1000->ac97_ioport->cntrl = AC97C_RS;
|
|
|
+ iounmap(au1000->ac97_ioport);
|
|
|
+ au1000->ac97_ioport = NULL;
|
|
|
+ }
|
|
|
+ release_and_free_resource(au1000->ac97_res_port);
|
|
|
+ au1000->ac97_res_port = NULL;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-static struct snd_card *au1000_card;
|
|
|
+static struct snd_ac97_bus_ops ops = {
|
|
|
+ .write = snd_au1000_ac97_write,
|
|
|
+ .read = snd_au1000_ac97_read,
|
|
|
+};
|
|
|
|
|
|
-static int __init
|
|
|
-au1000_init(void)
|
|
|
+static int au1000_ac97_probe(struct platform_device *pdev)
|
|
|
{
|
|
|
int err;
|
|
|
+ void __iomem *io;
|
|
|
+ struct resource *r;
|
|
|
struct snd_card *card;
|
|
|
struct snd_au1000 *au1000;
|
|
|
+ struct snd_ac97_bus *pbus;
|
|
|
+ struct snd_ac97_template ac97;
|
|
|
|
|
|
err = snd_card_create(-1, "AC97", THIS_MODULE,
|
|
|
- sizeof(struct snd_au1000), &card);
|
|
|
+ sizeof(struct snd_au1000), &card);
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
|
|
|
- card->private_free = snd_au1000_free;
|
|
|
au1000 = card->private_data;
|
|
|
au1000->card = card;
|
|
|
+ spin_lock_init(&au1000->ac97_lock);
|
|
|
|
|
|
- au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL);
|
|
|
- au1000->stream[CAPTURE ] = kmalloc(sizeof(struct audio_stream), GFP_KERNEL);
|
|
|
- /* so that snd_au1000_free will work as intended */
|
|
|
- au1000->ac97_res_port = NULL;
|
|
|
- if (au1000->stream[PLAYBACK])
|
|
|
- au1000->stream[PLAYBACK]->dma = -1;
|
|
|
- if (au1000->stream[CAPTURE ])
|
|
|
- au1000->stream[CAPTURE ]->dma = -1;
|
|
|
-
|
|
|
- if (au1000->stream[PLAYBACK] == NULL ||
|
|
|
- au1000->stream[CAPTURE ] == NULL) {
|
|
|
- snd_card_free(card);
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
+ /* from here on let ALSA call the special freeing function */
|
|
|
+ card->private_free = snd_au1000_free;
|
|
|
|
|
|
- if ((err = snd_au1000_ac97_new(au1000)) < 0 ) {
|
|
|
- snd_card_free(card);
|
|
|
- return err;
|
|
|
+ /* TX DMA ID */
|
|
|
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
|
|
|
+ if (!r) {
|
|
|
+ err = -ENODEV;
|
|
|
+ snd_printk(KERN_INFO "no TX DMA platform resource!\n");
|
|
|
+ goto out;
|
|
|
}
|
|
|
-
|
|
|
- if ((err = snd_au1000_pcm_new(au1000)) < 0) {
|
|
|
- snd_card_free(card);
|
|
|
- return err;
|
|
|
+ au1000->dmaid[0] = r->start;
|
|
|
+
|
|
|
+ /* RX DMA ID */
|
|
|
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
|
|
|
+ if (!r) {
|
|
|
+ err = -ENODEV;
|
|
|
+ snd_printk(KERN_INFO "no RX DMA platform resource!\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ au1000->dmaid[1] = r->start;
|
|
|
+
|
|
|
+ au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!au1000->stream[PLAYBACK])
|
|
|
+ goto out;
|
|
|
+ au1000->stream[PLAYBACK]->dma = -1;
|
|
|
+
|
|
|
+ au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (!au1000->stream[CAPTURE])
|
|
|
+ goto out;
|
|
|
+ au1000->stream[CAPTURE]->dma = -1;
|
|
|
+
|
|
|
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
|
+ if (!r)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ err = -EBUSY;
|
|
|
+ au1000->ac97_res_port = request_mem_region(r->start,
|
|
|
+ r->end - r->start + 1, pdev->name);
|
|
|
+ if (!au1000->ac97_res_port) {
|
|
|
+ snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
+ io = ioremap(r->start, r->end - r->start + 1);
|
|
|
+ if (!io)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ au1000->ac97_ioport = (struct au1000_ac97_reg *)io;
|
|
|
+
|
|
|
+ /* configure pins for AC'97
|
|
|
+ TODO: move to board_setup.c */
|
|
|
+ au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
|
|
|
+
|
|
|
+ /* Initialise Au1000's AC'97 Control Block */
|
|
|
+ au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
|
|
|
+ udelay(10);
|
|
|
+ au1000->ac97_ioport->cntrl = AC97C_CE;
|
|
|
+ udelay(10);
|
|
|
+
|
|
|
+ /* Initialise External CODEC -- cold reset */
|
|
|
+ au1000->ac97_ioport->config = AC97C_RESET;
|
|
|
+ udelay(10);
|
|
|
+ au1000->ac97_ioport->config = 0x0;
|
|
|
+ mdelay(5);
|
|
|
+
|
|
|
+ /* Initialise AC97 middle-layer */
|
|
|
+ err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus);
|
|
|
+ if (err < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ memset(&ac97, 0, sizeof(ac97));
|
|
|
+ ac97.private_data = au1000;
|
|
|
+ err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97);
|
|
|
+ if (err < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ err = snd_au1000_pcm_new(au1000);
|
|
|
+ if (err < 0)
|
|
|
+ goto out;
|
|
|
+
|
|
|
strcpy(card->driver, "Au1000-AC97");
|
|
|
strcpy(card->shortname, "AMD Au1000-AC97");
|
|
|
sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
|
|
|
|
|
|
- if ((err = snd_card_register(card)) < 0) {
|
|
|
- snd_card_free(card);
|
|
|
- return err;
|
|
|
- }
|
|
|
+ err = snd_card_register(card);
|
|
|
+ if (err < 0)
|
|
|
+ goto out;
|
|
|
|
|
|
printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
|
|
|
- au1000_card = card;
|
|
|
+
|
|
|
+ platform_set_drvdata(pdev, card);
|
|
|
+
|
|
|
return 0;
|
|
|
+
|
|
|
+ out:
|
|
|
+ snd_card_free(card);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int au1000_ac97_remove(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ return snd_card_free(platform_get_drvdata(pdev));
|
|
|
}
|
|
|
|
|
|
-static void __exit au1000_exit(void)
|
|
|
+struct platform_driver au1000_ac97c_driver = {
|
|
|
+ .driver = {
|
|
|
+ .name = "au1000-ac97c",
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ },
|
|
|
+ .probe = au1000_ac97_probe,
|
|
|
+ .remove = au1000_ac97_remove,
|
|
|
+};
|
|
|
+
|
|
|
+static int __init au1000_ac97_load(void)
|
|
|
{
|
|
|
- snd_card_free(au1000_card);
|
|
|
+ return platform_driver_register(&au1000_ac97c_driver);
|
|
|
}
|
|
|
|
|
|
-module_init(au1000_init);
|
|
|
-module_exit(au1000_exit);
|
|
|
+static void __exit au1000_ac97_unload(void)
|
|
|
+{
|
|
|
+ platform_driver_unregister(&au1000_ac97c_driver);
|
|
|
+}
|
|
|
|
|
|
+module_init(au1000_ac97_load);
|
|
|
+module_exit(au1000_ac97_unload);
|