[PATCH] Au1xxx sound registration and power management

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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);

[Index of Archives]     [Linux MIPS Home]     [LKML Archive]     [Linux ARM Kernel]     [Linux ARM]     [Linux]     [Git]     [Yosemite News]     [Linux SCSI]     [Linux Hams]

  Powered by Linux