Merge branches 'release', 'APERF', 'ARAT', 'misc', 'kelvin', 'device-lock' and 'bjorn...
[sfrench/cifs-2.6.git] / sound / atmel / ac97c.c
index dd72e00e5ae1c6209bf6a4ab0230ad53b85a0d50..0c0f8771656ac7d3e2725d81d4d918ad4e47a987 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for the Atmel AC97C controller
+ * Driver for Atmel AC97C
  *
  * Copyright (C) 2005-2009 Atmel Corporation
  *
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/bitmap.h>
+#include <linux/device.h>
 #include <linux/dmaengine.h>
 #include <linux/dma-mapping.h>
 #include <linux/init.h>
@@ -65,6 +66,7 @@ struct atmel_ac97c {
        /* Serialize access to opened variable */
        spinlock_t                      lock;
        void __iomem                    *regs;
+       int                             irq;
        int                             opened;
        int                             reset_pin;
 };
@@ -150,10 +152,10 @@ static struct snd_pcm_hardware atmel_ac97c_hw = {
        .rate_max               = 48000,
        .channels_min           = 1,
        .channels_max           = 2,
-       .buffer_bytes_max       = 64 * 4096,
+       .buffer_bytes_max       = 2 * 2 * 64 * 2048,
        .period_bytes_min       = 4096,
        .period_bytes_max       = 4096,
-       .periods_min            = 4,
+       .periods_min            = 6,
        .periods_max            = 64,
 };
 
@@ -297,9 +299,11 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
 {
        struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned long word = 0;
+       unsigned long word = ac97c_readl(chip, OCA);
        int retval;
 
+       word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
+
        /* assign channels to AC97C channel A */
        switch (runtime->channels) {
        case 1:
@@ -312,7 +316,6 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
        default:
                /* TODO: support more than two channels */
                return -EINVAL;
-               break;
        }
        ac97c_writel(chip, OCA, word);
 
@@ -324,13 +327,25 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
                word |= AC97C_CMR_CEM_LITTLE;
                break;
        case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
-       default:
                word &= ~(AC97C_CMR_CEM_LITTLE);
                break;
+       default:
+               word = ac97c_readl(chip, OCA);
+               word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
+               ac97c_writel(chip, OCA, word);
+               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);
@@ -359,9 +374,11 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
 {
        struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned long word = 0;
+       unsigned long word = ac97c_readl(chip, ICA);
        int retval;
 
+       word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
+
        /* assign channels to AC97C channel A */
        switch (runtime->channels) {
        case 1:
@@ -374,7 +391,6 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
        default:
                /* TODO: support more than two channels */
                return -EINVAL;
-               break;
        }
        ac97c_writel(chip, ICA, word);
 
@@ -386,13 +402,25 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
                word |= AC97C_CMR_CEM_LITTLE;
                break;
        case SNDRV_PCM_FORMAT_S16_BE: /* fall through */
-       default:
                word &= ~(AC97C_CMR_CEM_LITTLE);
                break;
+       default:
+               word = ac97c_readl(chip, ICA);
+               word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
+               ac97c_writel(chip, ICA, word);
+               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);
@@ -543,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;
@@ -665,17 +730,17 @@ static bool filter(struct dma_chan *chan, void *slave)
 
 static void atmel_ac97c_reset(struct atmel_ac97c *chip)
 {
-       ac97c_writel(chip, MR, AC97C_MR_WRST);
+       ac97c_writel(chip, MR,   0);
+       ac97c_writel(chip, MR,   AC97C_MR_ENA);
+       ac97c_writel(chip, CAMR, 0);
+       ac97c_writel(chip, COMR, 0);
 
        if (gpio_is_valid(chip->reset_pin)) {
                gpio_set_value(chip->reset_pin, 0);
                /* AC97 v2.2 specifications says minimum 1 us. */
-               udelay(10);
+               udelay(2);
                gpio_set_value(chip->reset_pin, 1);
        }
-
-       udelay(1);
-       ac97c_writel(chip, MR, AC97C_MR_ENA);
 }
 
 static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
@@ -690,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) {
@@ -703,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");
@@ -719,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");
@@ -747,14 +826,18 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
 
        snd_card_set_dev(card, &pdev->dev);
 
+       atmel_ac97c_reset(chip);
+
+       /* 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");
                goto err_ac97_bus;
        }
 
-       atmel_ac97c_reset(chip);
-
        retval = atmel_ac97c_mixer_new(chip);
        if (retval) {
                dev_dbg(&pdev->dev, "could not register ac97 mixer\n");
@@ -773,7 +856,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
 
                dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
-                                       chip->dma.rx_chan->dev->device.bus_id);
+                               dev_name(&chip->dma.rx_chan->dev->device));
                set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
        }
 
@@ -789,7 +872,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                chip->dma.tx_chan = dma_request_channel(mask, filter, dws);
 
                dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
-                                       chip->dma.tx_chan->dev->device.bus_id);
+                               dev_name(&chip->dma.tx_chan->dev->device));
                set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
        }
 
@@ -809,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);
@@ -836,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);
@@ -884,9 +969,14 @@ static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
        if (gpio_is_valid(chip->reset_pin))
                gpio_free(chip->reset_pin);
 
+       ac97c_writel(chip, CAMR, 0);
+       ac97c_writel(chip, COMR, 0);
+       ac97c_writel(chip, MR,   0);
+
        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);