Hello, here a patch to support device registration and power management for the au1xxx ALSA driver. Ciao, Rodolfo P.S. Please, let me know if I should send this patch to other lists too. -- GNU/Linux Solutions e-mail: giometti@xxxxxxxxxxxx Linux Device Driver giometti@xxxxxxxxx Embedded Systems giometti@xxxxxxxx UNIX programming phone: +39 349 2432127
diff --git a/arch/mips/au1000/common/platform.c b/arch/mips/au1000/common/platform.c index 0088889..34de6d3 100644 --- a/arch/mips/au1000/common/platform.c +++ b/arch/mips/au1000/common/platform.c @@ -88,6 +88,31 @@ static struct platform_device au1xxx_eth #endif #endif +#if defined(CONFIG_SND_AU1X00) +/* AC97 controller */ +static struct resource au1xxx_snd_resources[] = { + [0] = { + .name = "snd-base", + .start = AC97C_BASE, + .end = AC97C_BASE + AC97_IOSIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "snd-irq", + .start = AU1000_AC97C_INT, + .end = AU1000_AC97C_INT, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device au1xxx_snd_device = { + .name = "au1xxx-snd", + .id = -1, + .num_resources = ARRAY_SIZE(au1xxx_snd_resources), + .resource = au1xxx_snd_resources, +}; +#endif + /* OHCI (USB full speed host controller) */ static struct resource au1xxx_usb_ohci_resources[] = { [0] = { @@ -376,6 +401,9 @@ #if defined(CONFIG_SOC_AU1000) || \ &au1xxx_eth1_device, #endif #endif +#if defined(CONFIG_SND_AU1X00) + &au1xxx_snd_device, +#endif &au1xxx_usb_ohci_device, &au1x00_pcmcia_device, #ifdef CONFIG_FB_AU1100 diff --git a/include/asm-mips/mach-au1x00/au1000.h b/include/asm-mips/mach-au1x00/au1000.h index 0988af9..cb8939a 100644 --- a/include/asm-mips/mach-au1x00/au1000.h +++ b/include/asm-mips/mach-au1x00/au1000.h @@ -1600,6 +1600,9 @@ #define SYS_CPUPLL 0xB190 #define SYS_AUXPLL 0xB1900064 /* AC97 Controller */ +#define AC97C_BASE AC97_PHYS_ADDR +#define AC97_IOSIZE 0x14 + #define AC97C_CONFIG 0xB0000000 #define AC97C_RECV_SLOTS_BIT 13 #define AC97C_RECV_SLOTS_MASK (0x3ff << AC97C_RECV_SLOTS_BIT) diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c index cf476fe..2dd6862 100644 --- a/sound/mips/au1x00.c +++ b/sound/mips/au1x00.c @@ -5,6 +5,8 @@ * Copyright 2004 Cooper Street Innovations Inc. * Author: Charles Eidsness <charles@xxxxxxxxxxxxxxxxx> * + * PM support added by Rodolfo Giometti <giometti@xxxxxxxx> + * * 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 @@ -40,6 +42,7 @@ #include <sound/driver.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/version.h> +#include <linux/platform_device.h> #include <sound/core.h> #include <sound/initval.h> #include <sound/pcm.h> @@ -53,6 +56,8 @@ MODULE_DESCRIPTION("Au1000 AC'97 ALSA Dr MODULE_LICENSE("GPL"); MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}"); +#define DRV_NAME "au1xxx-snd" + #define PLAYBACK 0 #define CAPTURE 1 #define AC97_SLOT_3 0x01 @@ -526,7 +531,6 @@ get the interupt driven case to work eff } - static void snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) { @@ -550,24 +554,17 @@ get the interupt driven case to work eff spin_unlock(&au1000->ac97_lock); } +static struct snd_ac97_bus_ops au1000_ac97_ops = { + .write = snd_au1000_ac97_write, + .read = snd_au1000_ac97_read, +}; + static int __devinit 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); @@ -588,7 +585,7 @@ snd_au1000_ac97_new(struct snd_au1000 *a mdelay(5); /* Initialise AC97 middle-layer */ - if ((err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus)) < 0) + if ((err = snd_ac97_bus(au1000->card, 0, &au1000_ac97_ops, au1000, &pbus)) < 0) return err; memset(&ac97, 0, sizeof(ac97)); @@ -626,68 +623,154 @@ snd_au1000_free(struct snd_card *card) } } +#ifdef CONFIG_PM +static u32 au1000_ac97_config; + +static int au1000_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct snd_card *card = platform_get_drvdata(pdev); + struct snd_au1000 *au1000 = card->private_data; + + if (!card) + return 0; -static struct snd_card *au1000_card; + snd_power_change_state(card, SNDRV_CTL_POWER_D3cold); + snd_pcm_suspend_all(au1000->pcm); + snd_ac97_suspend(au1000->ac97); + + /* Save Au1000's AC'97 Control Block and disable the controller */ + au1000_ac97_config = au1000->ac97_ioport->config; + au1000->ac97_ioport->cntrl = AC97C_RS; + + return 0; +} -static int __init -au1000_init(void) +static int au1000_drv_resume(struct platform_device *pdev) { - int err; - struct snd_card *card; - struct snd_au1000 *au1000; + struct snd_card *card = platform_get_drvdata(pdev); + struct snd_au1000 *au1000 = card->private_data; + + if (!card) + return 0; + /* Initialise Au1000's AC'97 Control Block and restore previous + * configuration */ + au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE; + udelay(10); + au1000->ac97_ioport->cntrl = AC97C_CE; + udelay(10); + au1000->ac97_ioport->config = au1000_ac97_config; + + snd_ac97_resume(au1000->ac97); + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} +#endif + +static int __init au1000_drv_probe(struct platform_device *pdev) +{ + struct resource *res; + struct snd_card *card = NULL; + struct snd_au1000 *au1000 = NULL; + int ret; + + /* Get the resource info */ + ret = -ENODEV; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "snd-base"); + if (!res) + goto exit; + + ret = -ENOMEM; card = snd_card_new(-1, "AC97", THIS_MODULE, sizeof(struct snd_au1000)); if (card == NULL) - return -ENOMEM; + goto exit; + card->dev = &pdev->dev; card->private_free = snd_au1000_free; au1000 = card->private_data; au1000->card = card; + au1000->ac97_res_port = res; + au1000->ac97_ioport = ioremap(res->start, res->end - res->start + 1); + ret = -ENOMEM; + if (!au1000->ac97_ioport) { + printk (KERN_ERR "%s: unable to remap address %lx\n", + DRV_NAME, res->start); + goto exit; + } + 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; - } + au1000->stream[CAPTURE ] == NULL) + goto exit; - if ((err = snd_au1000_ac97_new(au1000)) < 0 ) { - snd_card_free(card); - return err; - } + if ((ret = snd_au1000_ac97_new(au1000)) < 0) + goto exit; - if ((err = snd_au1000_pcm_new(au1000)) < 0) { - snd_card_free(card); - return err; - } + if ((ret = snd_au1000_pcm_new(au1000)) < 0) + goto exit; 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; - } + if ((ret = snd_card_register(card)) < 0) + goto exit; + platform_set_drvdata(pdev, card); printk( KERN_INFO "ALSA AC97: Driver Initialized\n" ); - au1000_card = card; + return 0; + +exit : + if (au1000 && au1000->ac97_ioport) + iounmap(au1000->ac97_ioport); + if (card) + snd_card_free(card); + + return ret; } -static void __exit au1000_exit(void) +static int __exit au1000_drv_remove(struct platform_device *pdev) { - snd_card_free(au1000_card); + struct snd_card *card = platform_get_drvdata(pdev); + + if (card) + snd_card_free(card); + + return 0; } -module_init(au1000_init); -module_exit(au1000_exit); +static struct platform_driver au1000_driver = { + .probe = au1000_drv_probe, + .remove = au1000_drv_remove, +#ifdef CONFIG_PM + .suspend = au1000_drv_suspend, + .resume = au1000_drv_resume, +#endif + .driver = { + .name = DRV_NAME, + }, +}; + + +static int __init au1000_snd_init(void) +{ + return platform_driver_register(&au1000_driver); +} + +static void __exit au1000_snd_exit(void) +{ + platform_driver_unregister(&au1000_driver); +} +module_init(au1000_snd_init); +module_exit(au1000_snd_exit);