ALSA: snd-atmel-ac97c: enable interrupts to catch events for error reporting
Hans-Christian Egtvedt [Thu, 2 Apr 2009 06:21:14 +0000 (08:21 +0200)]
This patch will enable interrupts from AC97C and report about error
conditions that occurs.

On channel A both overrun and underrun will be enabled depending if
playback and/or capture are enabled. On the control channel the overrun
interrupt is enabled.

Signed-off-by: Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

sound/atmel/ac97c.c

index c9bc345..e8484cb 100644 (file)
@@ -66,6 +66,7 @@ struct atmel_ac97c {
        /* Serialize access to opened variable */
        spinlock_t                      lock;
        void __iomem                    *regs;
+       int                             irq;
        int                             opened;
        int                             reset_pin;
 };
@@ -335,8 +336,16 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
                return -EINVAL;
        }
 
+       /* Enable underrun interrupt on channel A */
+       word |= AC97C_CSR_UNRUN;
+
        ac97c_writel(chip, CAMR, word);
 
+       /* Enable channel A event interrupt */
+       word = ac97c_readl(chip, IMR);
+       word |= AC97C_SR_CAEVT;
+       ac97c_writel(chip, IER, word);
+
        /* set variable rate if needed */
        if (runtime->rate != 48000) {
                word = ac97c_readl(chip, MR);
@@ -402,8 +411,16 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
                return -EINVAL;
        }
 
+       /* Enable overrun interrupt on channel A */
+       word |= AC97C_CSR_OVRUN;
+
        ac97c_writel(chip, CAMR, word);
 
+       /* Enable channel A event interrupt */
+       word = ac97c_readl(chip, IMR);
+       word |= AC97C_SR_CAEVT;
+       ac97c_writel(chip, IER, word);
+
        /* set variable rate if needed */
        if (runtime->rate != 48000) {
                word = ac97c_readl(chip, MR);
@@ -554,6 +571,43 @@ static struct snd_pcm_ops atmel_ac97_capture_ops = {
        .pointer        = atmel_ac97c_capture_pointer,
 };
 
+static irqreturn_t atmel_ac97c_interrupt(int irq, void *dev)
+{
+       struct atmel_ac97c      *chip  = (struct atmel_ac97c *)dev;
+       irqreturn_t             retval = IRQ_NONE;
+       u32                     sr     = ac97c_readl(chip, SR);
+       u32                     casr   = ac97c_readl(chip, CASR);
+       u32                     cosr   = ac97c_readl(chip, COSR);
+
+       if (sr & AC97C_SR_CAEVT) {
+               dev_info(&chip->pdev->dev, "channel A event%s%s%s%s%s%s\n",
+                               casr & AC97C_CSR_OVRUN   ? " OVRUN"   : "",
+                               casr & AC97C_CSR_RXRDY   ? " RXRDY"   : "",
+                               casr & AC97C_CSR_UNRUN   ? " UNRUN"   : "",
+                               casr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
+                               casr & AC97C_CSR_TXRDY   ? " TXRDY"   : "",
+                               !casr                    ? " NONE"    : "");
+               retval = IRQ_HANDLED;
+       }
+
+       if (sr & AC97C_SR_COEVT) {
+               dev_info(&chip->pdev->dev, "codec channel event%s%s%s%s%s\n",
+                               cosr & AC97C_CSR_OVRUN   ? " OVRUN"   : "",
+                               cosr & AC97C_CSR_RXRDY   ? " RXRDY"   : "",
+                               cosr & AC97C_CSR_TXEMPTY ? " TXEMPTY" : "",
+                               cosr & AC97C_CSR_TXRDY   ? " TXRDY"   : "",
+                               !cosr                    ? " NONE"    : "");
+               retval = IRQ_HANDLED;
+       }
+
+       if (retval == IRQ_NONE) {
+               dev_err(&chip->pdev->dev, "spurious interrupt sr 0x%08x "
+                               "casr 0x%08x cosr 0x%08x\n", sr, casr, cosr);
+       }
+
+       return retval;
+}
+
 static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
 {
        struct snd_pcm          *pcm;
@@ -701,6 +755,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                .read   = atmel_ac97c_read,
        };
        int                             retval;
+       int                             irq;
 
        regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!regs) {
@@ -714,6 +769,12 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_dbg(&pdev->dev, "could not get irq\n");
+               return -ENXIO;
+       }
+
        pclk = clk_get(&pdev->dev, "pclk");
        if (IS_ERR(pclk)) {
                dev_dbg(&pdev->dev, "no peripheral clock\n");
@@ -730,6 +791,13 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
 
        chip = get_chip(card);
 
+       retval = request_irq(irq, atmel_ac97c_interrupt, 0, "AC97C", chip);
+       if (retval) {
+               dev_dbg(&pdev->dev, "unable to request irq %d\n", irq);
+               goto err_request_irq;
+       }
+       chip->irq = irq;
+
        spin_lock_init(&chip->lock);
 
        strcpy(card->driver, "Atmel AC97C");
@@ -758,6 +826,10 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
 
        snd_card_set_dev(card, &pdev->dev);
 
+       /* Enable overrun interrupt from codec channel */
+       ac97c_writel(chip, COMR, AC97C_CSR_OVRUN);
+       ac97c_writel(chip, IER, ac97c_readl(chip, IMR) | AC97C_SR_COEVT);
+
        retval = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus);
        if (retval) {
                dev_dbg(&pdev->dev, "could not register on ac97 bus\n");
@@ -820,7 +892,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
        retval = snd_card_register(card);
        if (retval) {
                dev_dbg(&pdev->dev, "could not register sound card\n");
-               goto err_ac97_bus;
+               goto err_dma;
        }
 
        platform_set_drvdata(pdev, card);
@@ -847,6 +919,8 @@ err_ac97_bus:
 
        iounmap(chip->regs);
 err_ioremap:
+       free_irq(irq, chip);
+err_request_irq:
        snd_card_free(card);
 err_snd_card_new:
        clk_disable(pclk);
@@ -898,6 +972,7 @@ static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
        clk_disable(chip->pclk);
        clk_put(chip->pclk);
        iounmap(chip->regs);
+       free_irq(chip->irq, chip);
 
        if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
                dma_release_channel(chip->dma.rx_chan);