ALSA: platform: Check CONFIG_PM_SLEEP instead of CONFIG_PM
[linux-3.10.git] / sound / atmel / ac97c.c
index 21be9c9..3c8d3ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for the Atmel AC97C controller
+ * Driver for Atmel AC97C
  *
  * Copyright (C) 2005-2009 Atmel Corporation
  *
 #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/atmel_pdc.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/gpio.h>
+#include <linux/types.h>
 #include <linux/io.h>
 
 #include <sound/core.h>
 
 #include <linux/dw_dmac.h>
 
+#include <mach/cpu.h>
+#include <mach/gpio.h>
+
+#ifdef CONFIG_ARCH_AT91
+#include <mach/hardware.h>
+#endif
+
 #include "ac97c.h"
 
 enum {
@@ -62,9 +72,11 @@ struct atmel_ac97c {
        u64                             cur_format;
        unsigned int                    cur_rate;
        unsigned long                   flags;
+       int                             playback_period, capture_period;
        /* Serialize access to opened variable */
        spinlock_t                      lock;
        void __iomem                    *regs;
+       int                             irq;
        int                             opened;
        int                             reset_pin;
 };
@@ -91,7 +103,7 @@ static void atmel_ac97c_dma_capture_period_done(void *arg)
 
 static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
                struct snd_pcm_substream *substream,
-               enum dma_data_direction direction)
+               enum dma_transfer_direction direction)
 {
        struct dma_chan                 *chan;
        struct dw_cyclic_desc           *cdesc;
@@ -107,7 +119,7 @@ static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
                return -EINVAL;
        }
 
-       if (direction == DMA_TO_DEVICE)
+       if (direction == DMA_MEM_TO_DEV)
                chan = chip->dma.tx_chan;
        else
                chan = chip->dma.rx_chan;
@@ -122,7 +134,7 @@ static int atmel_ac97c_prepare_dma(struct atmel_ac97c *chip,
                return PTR_ERR(cdesc);
        }
 
-       if (direction == DMA_TO_DEVICE) {
+       if (direction == DMA_MEM_TO_DEV) {
                cdesc->period_callback = atmel_ac97c_dma_playback_period_done;
                set_bit(DMA_TX_READY, &chip->flags);
        } else {
@@ -150,10 +162,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,
 };
 
@@ -240,10 +252,12 @@ static int atmel_ac97c_playback_hw_params(struct snd_pcm_substream *substream,
        if (retval < 0)
                return retval;
        /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
-       if (retval == 1)
-               if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
-                       dw_dma_cyclic_free(chip->dma.tx_chan);
-
+       if (cpu_is_at32ap7000()) {
+               /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+               if (retval == 1)
+                       if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
+                               dw_dma_cyclic_free(chip->dma.tx_chan);
+       }
        /* Set restrictions to params. */
        mutex_lock(&opened_mutex);
        chip->cur_rate = params_rate(hw_params);
@@ -264,9 +278,14 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
        if (retval < 0)
                return retval;
        /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
-       if (retval == 1)
-               if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
-                       dw_dma_cyclic_free(chip->dma.rx_chan);
+       if (cpu_is_at32ap7000()) {
+               if (retval < 0)
+                       return retval;
+               /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
+               if (retval == 1)
+                       if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+                               dw_dma_cyclic_free(chip->dma.rx_chan);
+       }
 
        /* Set restrictions to params. */
        mutex_lock(&opened_mutex);
@@ -280,16 +299,20 @@ static int atmel_ac97c_capture_hw_params(struct snd_pcm_substream *substream,
 static int atmel_ac97c_playback_hw_free(struct snd_pcm_substream *substream)
 {
        struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
-       if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
-               dw_dma_cyclic_free(chip->dma.tx_chan);
+       if (cpu_is_at32ap7000()) {
+               if (test_and_clear_bit(DMA_TX_READY, &chip->flags))
+                       dw_dma_cyclic_free(chip->dma.tx_chan);
+       }
        return snd_pcm_lib_free_pages(substream);
 }
 
 static int atmel_ac97c_capture_hw_free(struct snd_pcm_substream *substream)
 {
        struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
-       if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
-               dw_dma_cyclic_free(chip->dma.rx_chan);
+       if (cpu_is_at32ap7000()) {
+               if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+                       dw_dma_cyclic_free(chip->dma.rx_chan);
+       }
        return snd_pcm_lib_free_pages(substream);
 }
 
@@ -297,9 +320,13 @@ 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;
+       int block_size = frames_to_bytes(runtime, runtime->period_size);
+       unsigned long word = ac97c_readl(chip, OCA);
        int retval;
 
+       chip->playback_period = 0;
+       word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
+
        /* assign channels to AC97C channel A */
        switch (runtime->channels) {
        case 1:
@@ -316,20 +343,37 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
        ac97c_writel(chip, OCA, word);
 
        /* configure sample format and size */
-       word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
+       word = ac97c_readl(chip, CAMR);
+       if (chip->opened <= 1)
+               word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
+       else
+               word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
 
        switch (runtime->format) {
        case SNDRV_PCM_FORMAT_S16_LE:
-               word |= AC97C_CMR_CEM_LITTLE;
+               if (cpu_is_at32ap7000())
+                       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);
@@ -347,9 +391,18 @@ static int atmel_ac97c_playback_prepare(struct snd_pcm_substream *substream)
                dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
                                runtime->rate);
 
-       if (!test_bit(DMA_TX_READY, &chip->flags))
-               retval = atmel_ac97c_prepare_dma(chip, substream,
-                               DMA_TO_DEVICE);
+       if (cpu_is_at32ap7000()) {
+               if (!test_bit(DMA_TX_READY, &chip->flags))
+                       retval = atmel_ac97c_prepare_dma(chip, substream,
+                                       DMA_MEM_TO_DEV);
+       } else {
+               /* Initialize and start the PDC */
+               writel(runtime->dma_addr, chip->regs + ATMEL_PDC_TPR);
+               writel(block_size / 2, chip->regs + ATMEL_PDC_TCR);
+               writel(runtime->dma_addr + block_size,
+                               chip->regs + ATMEL_PDC_TNPR);
+               writel(block_size / 2, chip->regs + ATMEL_PDC_TNCR);
+       }
 
        return retval;
 }
@@ -358,9 +411,13 @@ 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;
+       int block_size = frames_to_bytes(runtime, runtime->period_size);
+       unsigned long word = ac97c_readl(chip, ICA);
        int retval;
 
+       chip->capture_period = 0;
+       word &= ~(AC97C_CH_MASK(PCM_LEFT) | AC97C_CH_MASK(PCM_RIGHT));
+
        /* assign channels to AC97C channel A */
        switch (runtime->channels) {
        case 1:
@@ -377,20 +434,37 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
        ac97c_writel(chip, ICA, word);
 
        /* configure sample format and size */
-       word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
+       word = ac97c_readl(chip, CAMR);
+       if (chip->opened <= 1)
+               word = AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
+       else
+               word |= AC97C_CMR_DMAEN | AC97C_CMR_SIZE_16;
 
        switch (runtime->format) {
        case SNDRV_PCM_FORMAT_S16_LE:
-               word |= AC97C_CMR_CEM_LITTLE;
+               if (cpu_is_at32ap7000())
+                       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);
@@ -408,9 +482,18 @@ static int atmel_ac97c_capture_prepare(struct snd_pcm_substream *substream)
                dev_dbg(&chip->pdev->dev, "could not set rate %d Hz\n",
                                runtime->rate);
 
-       if (!test_bit(DMA_RX_READY, &chip->flags))
-               retval = atmel_ac97c_prepare_dma(chip, substream,
-                               DMA_FROM_DEVICE);
+       if (cpu_is_at32ap7000()) {
+               if (!test_bit(DMA_RX_READY, &chip->flags))
+                       retval = atmel_ac97c_prepare_dma(chip, substream,
+                                       DMA_DEV_TO_MEM);
+       } else {
+               /* Initialize and start the PDC */
+               writel(runtime->dma_addr, chip->regs + ATMEL_PDC_RPR);
+               writel(block_size / 2, chip->regs + ATMEL_PDC_RCR);
+               writel(runtime->dma_addr + block_size,
+                               chip->regs + ATMEL_PDC_RNPR);
+               writel(block_size / 2, chip->regs + ATMEL_PDC_RNCR);
+       }
 
        return retval;
 }
@@ -419,7 +502,7 @@ static int
 atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
-       unsigned long camr;
+       unsigned long camr, ptcr = 0;
        int retval = 0;
 
        camr = ac97c_readl(chip, CAMR);
@@ -428,15 +511,22 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
        case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
        case SNDRV_PCM_TRIGGER_START:
-               retval = dw_dma_cyclic_start(chip->dma.tx_chan);
-               if (retval)
-                       goto out;
-               camr |= AC97C_CMR_CENA;
+               if (cpu_is_at32ap7000()) {
+                       retval = dw_dma_cyclic_start(chip->dma.tx_chan);
+                       if (retval)
+                               goto out;
+               } else {
+                       ptcr = ATMEL_PDC_TXTEN;
+               }
+               camr |= AC97C_CMR_CENA | AC97C_CSR_ENDTX;
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
        case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
        case SNDRV_PCM_TRIGGER_STOP:
-               dw_dma_cyclic_stop(chip->dma.tx_chan);
+               if (cpu_is_at32ap7000())
+                       dw_dma_cyclic_stop(chip->dma.tx_chan);
+               else
+                       ptcr |= ATMEL_PDC_TXTDIS;
                if (chip->opened <= 1)
                        camr &= ~AC97C_CMR_CENA;
                break;
@@ -446,6 +536,8 @@ atmel_ac97c_playback_trigger(struct snd_pcm_substream *substream, int cmd)
        }
 
        ac97c_writel(chip, CAMR, camr);
+       if (!cpu_is_at32ap7000())
+               writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
 out:
        return retval;
 }
@@ -454,24 +546,32 @@ static int
 atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct atmel_ac97c *chip = snd_pcm_substream_chip(substream);
-       unsigned long camr;
+       unsigned long camr, ptcr = 0;
        int retval = 0;
 
        camr = ac97c_readl(chip, CAMR);
+       ptcr = readl(chip->regs + ATMEL_PDC_PTSR);
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: /* fall through */
        case SNDRV_PCM_TRIGGER_RESUME: /* fall through */
        case SNDRV_PCM_TRIGGER_START:
-               retval = dw_dma_cyclic_start(chip->dma.rx_chan);
-               if (retval)
-                       goto out;
-               camr |= AC97C_CMR_CENA;
+               if (cpu_is_at32ap7000()) {
+                       retval = dw_dma_cyclic_start(chip->dma.rx_chan);
+                       if (retval)
+                               goto out;
+               } else {
+                       ptcr = ATMEL_PDC_RXTEN;
+               }
+               camr |= AC97C_CMR_CENA | AC97C_CSR_ENDRX;
                break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* fall through */
        case SNDRV_PCM_TRIGGER_SUSPEND: /* fall through */
        case SNDRV_PCM_TRIGGER_STOP:
-               dw_dma_cyclic_stop(chip->dma.rx_chan);
+               if (cpu_is_at32ap7000())
+                       dw_dma_cyclic_stop(chip->dma.rx_chan);
+               else
+                       ptcr |= (ATMEL_PDC_RXTDIS);
                if (chip->opened <= 1)
                        camr &= ~AC97C_CMR_CENA;
                break;
@@ -481,6 +581,8 @@ atmel_ac97c_capture_trigger(struct snd_pcm_substream *substream, int cmd)
        }
 
        ac97c_writel(chip, CAMR, camr);
+       if (!cpu_is_at32ap7000())
+               writel(ptcr, chip->regs + ATMEL_PDC_PTCR);
 out:
        return retval;
 }
@@ -493,7 +595,10 @@ atmel_ac97c_playback_pointer(struct snd_pcm_substream *substream)
        snd_pcm_uframes_t       frames;
        unsigned long           bytes;
 
-       bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
+       if (cpu_is_at32ap7000())
+               bytes = dw_dma_get_src_addr(chip->dma.tx_chan);
+       else
+               bytes = readl(chip->regs + ATMEL_PDC_TPR);
        bytes -= runtime->dma_addr;
 
        frames = bytes_to_frames(runtime, bytes);
@@ -510,7 +615,10 @@ atmel_ac97c_capture_pointer(struct snd_pcm_substream *substream)
        snd_pcm_uframes_t       frames;
        unsigned long           bytes;
 
-       bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
+       if (cpu_is_at32ap7000())
+               bytes = dw_dma_get_dst_addr(chip->dma.rx_chan);
+       else
+               bytes = readl(chip->regs + ATMEL_PDC_RPR);
        bytes -= runtime->dma_addr;
 
        frames = bytes_to_frames(runtime, bytes);
@@ -541,15 +649,134 @@ 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);
+       u32                     camr   = ac97c_readl(chip, CAMR);
+
+       if (sr & AC97C_SR_CAEVT) {
+               struct snd_pcm_runtime *runtime;
+               int offset, next_period, block_size;
+               dev_dbg(&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"    : "");
+               if (!cpu_is_at32ap7000()) {
+                       if ((casr & camr) & AC97C_CSR_ENDTX) {
+                               runtime = chip->playback_substream->runtime;
+                               block_size = frames_to_bytes(runtime,
+                                               runtime->period_size);
+                               chip->playback_period++;
+
+                               if (chip->playback_period == runtime->periods)
+                                       chip->playback_period = 0;
+                               next_period = chip->playback_period + 1;
+                               if (next_period == runtime->periods)
+                                       next_period = 0;
+
+                               offset = block_size * next_period;
+
+                               writel(runtime->dma_addr + offset,
+                                               chip->regs + ATMEL_PDC_TNPR);
+                               writel(block_size / 2,
+                                               chip->regs + ATMEL_PDC_TNCR);
+
+                               snd_pcm_period_elapsed(
+                                               chip->playback_substream);
+                       }
+                       if ((casr & camr) & AC97C_CSR_ENDRX) {
+                               runtime = chip->capture_substream->runtime;
+                               block_size = frames_to_bytes(runtime,
+                                               runtime->period_size);
+                               chip->capture_period++;
+
+                               if (chip->capture_period == runtime->periods)
+                                       chip->capture_period = 0;
+                               next_period = chip->capture_period + 1;
+                               if (next_period == runtime->periods)
+                                       next_period = 0;
+
+                               offset = block_size * next_period;
+
+                               writel(runtime->dma_addr + offset,
+                                               chip->regs + ATMEL_PDC_RNPR);
+                               writel(block_size / 2,
+                                               chip->regs + ATMEL_PDC_RNCR);
+                               snd_pcm_period_elapsed(chip->capture_substream);
+                       }
+               }
+               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 struct ac97_pcm at91_ac97_pcm_defs[] __devinitdata = {
+       /* Playback */
+       {
+               .exclusive = 1,
+               .r = { {
+                       .slots = ((1 << AC97_SLOT_PCM_LEFT)
+                                 | (1 << AC97_SLOT_PCM_RIGHT)),
+               } },
+       },
+       /* PCM in */
+       {
+               .stream = 1,
+               .exclusive = 1,
+               .r = { {
+                       .slots = ((1 << AC97_SLOT_PCM_LEFT)
+                                       | (1 << AC97_SLOT_PCM_RIGHT)),
+               } }
+       },
+       /* Mic in */
+       {
+               .stream = 1,
+               .exclusive = 1,
+               .r = { {
+                       .slots = (1<<AC97_SLOT_MIC),
+               } }
+       },
+};
+
 static int __devinit atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
 {
        struct snd_pcm          *pcm;
        struct snd_pcm_hardware hw = atmel_ac97c_hw;
-       int                     capture, playback, retval;
+       int                     capture, playback, retval, err;
 
        capture = test_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
        playback = test_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
 
+       if (!cpu_is_at32ap7000()) {
+               err = snd_ac97_pcm_assign(chip->ac97_bus,
+                               ARRAY_SIZE(at91_ac97_pcm_defs),
+                               at91_ac97_pcm_defs);
+               if (err)
+                       return err;
+       }
        retval = snd_pcm_new(chip->card, chip->card->shortname,
                        chip->pdev->id, playback, capture, &pcm);
        if (retval)
@@ -663,17 +890,21 @@ 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);
+       } else {
+               ac97c_writel(chip, MR, AC97C_MR_WRST | AC97C_MR_ENA);
+               udelay(2);
+               ac97c_writel(chip, MR, AC97C_MR_ENA);
        }
-
-       udelay(1);
-       ac97c_writel(chip, MR, AC97C_MR_ENA);
 }
 
 static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
@@ -688,6 +919,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) {
@@ -701,7 +933,18 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       pclk = clk_get(&pdev->dev, "pclk");
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_dbg(&pdev->dev, "could not get irq\n");
+               return -ENXIO;
+       }
+
+       if (cpu_is_at32ap7000()) {
+               pclk = clk_get(&pdev->dev, "pclk");
+       } else {
+               pclk = clk_get(&pdev->dev, "ac97_clk");
+       }
+
        if (IS_ERR(pclk)) {
                dev_dbg(&pdev->dev, "no peripheral clock\n");
                return PTR_ERR(pclk);
@@ -717,6 +960,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");
@@ -726,7 +976,7 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
        chip->card = card;
        chip->pclk = pclk;
        chip->pdev = pdev;
-       chip->regs = ioremap(regs->start, regs->end - regs->start + 1);
+       chip->regs = ioremap(regs->start, resource_size(regs));
 
        if (!chip->regs) {
                dev_dbg(&pdev->dev, "could not remap register memory\n");
@@ -741,63 +991,102 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                        gpio_direction_output(pdata->reset_pin, 1);
                        chip->reset_pin = pdata->reset_pin;
                }
+       } else {
+               chip->reset_pin = -EINVAL;
        }
 
        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");
                goto err_ac97_bus;
        }
 
-       if (pdata->rx_dws.dma_dev) {
-               struct dw_dma_slave *dws = &pdata->rx_dws;
-               dma_cap_mask_t mask;
-
-               dws->rx_reg = regs->start + AC97C_CARHR + 2;
-
-               dma_cap_zero(mask);
-               dma_cap_set(DMA_SLAVE, mask);
+       if (cpu_is_at32ap7000()) {
+               if (pdata->rx_dws.dma_dev) {
+                       dma_cap_mask_t mask;
+
+                       dma_cap_zero(mask);
+                       dma_cap_set(DMA_SLAVE, mask);
+
+                       chip->dma.rx_chan = dma_request_channel(mask, filter,
+                                                               &pdata->rx_dws);
+                       if (chip->dma.rx_chan) {
+                               struct dma_slave_config dma_conf = {
+                                       .src_addr = regs->start + AC97C_CARHR +
+                                               2,
+                                       .src_addr_width =
+                                               DMA_SLAVE_BUSWIDTH_2_BYTES,
+                                       .src_maxburst = 1,
+                                       .dst_maxburst = 1,
+                                       .direction = DMA_DEV_TO_MEM,
+                                       .device_fc = false,
+                               };
+
+                               dmaengine_slave_config(chip->dma.rx_chan,
+                                               &dma_conf);
+                       }
+
+                       dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
+                               dev_name(&chip->dma.rx_chan->dev->device));
+                       set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+               }
 
-               chip->dma.rx_chan = dma_request_channel(mask, filter, dws);
+               if (pdata->tx_dws.dma_dev) {
+                       dma_cap_mask_t mask;
+
+                       dma_cap_zero(mask);
+                       dma_cap_set(DMA_SLAVE, mask);
+
+                       chip->dma.tx_chan = dma_request_channel(mask, filter,
+                                                               &pdata->tx_dws);
+                       if (chip->dma.tx_chan) {
+                               struct dma_slave_config dma_conf = {
+                                       .dst_addr = regs->start + AC97C_CATHR +
+                                               2,
+                                       .dst_addr_width =
+                                               DMA_SLAVE_BUSWIDTH_2_BYTES,
+                                       .src_maxburst = 1,
+                                       .dst_maxburst = 1,
+                                       .direction = DMA_MEM_TO_DEV,
+                                       .device_fc = false,
+                               };
+
+                               dmaengine_slave_config(chip->dma.tx_chan,
+                                               &dma_conf);
+                       }
+
+                       dev_info(&chip->pdev->dev, "using %s for DMA TX\n",
+                               dev_name(&chip->dma.tx_chan->dev->device));
+                       set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+               }
 
-               dev_info(&chip->pdev->dev, "using %s for DMA RX\n",
-                                       chip->dma.rx_chan->dev->device.bus_id);
+               if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
+                               !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
+                       dev_dbg(&pdev->dev, "DMA not available\n");
+                       retval = -ENODEV;
+                       goto err_dma;
+               }
+       } else {
+               /* Just pretend that we have DMA channel(for at91 i is actually
+                * the PDC) */
                set_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
-       }
-
-       if (pdata->tx_dws.dma_dev) {
-               struct dw_dma_slave *dws = &pdata->tx_dws;
-               dma_cap_mask_t mask;
-
-               dws->tx_reg = regs->start + AC97C_CATHR + 2;
-
-               dma_cap_zero(mask);
-               dma_cap_set(DMA_SLAVE, mask);
-
-               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);
                set_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
        }
 
-       if (!test_bit(DMA_RX_CHAN_PRESENT, &chip->flags) &&
-                       !test_bit(DMA_TX_CHAN_PRESENT, &chip->flags)) {
-               dev_dbg(&pdev->dev, "DMA not available\n");
-               retval = -ENODEV;
-               goto err_dma;
-       }
-
        retval = atmel_ac97c_pcm_new(chip);
        if (retval) {
                dev_dbg(&pdev->dev, "could not register ac97 pcm device\n");
@@ -807,25 +1096,27 @@ 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);
 
-       dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p\n",
-                       chip->regs);
+       dev_info(&pdev->dev, "Atmel AC97 controller at 0x%p, irq = %d\n",
+                       chip->regs, irq);
 
        return 0;
 
 err_dma:
-       if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
-               dma_release_channel(chip->dma.rx_chan);
-       if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
-               dma_release_channel(chip->dma.tx_chan);
-       clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
-       clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
-       chip->dma.rx_chan = NULL;
-       chip->dma.tx_chan = NULL;
+       if (cpu_is_at32ap7000()) {
+               if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
+                       dma_release_channel(chip->dma.rx_chan);
+               if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
+                       dma_release_channel(chip->dma.tx_chan);
+               clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+               clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+               chip->dma.rx_chan = NULL;
+               chip->dma.tx_chan = NULL;
+       }
 err_ac97_bus:
        snd_card_set_dev(card, NULL);
 
@@ -834,6 +1125,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);
@@ -841,37 +1134,42 @@ err_snd_card_new:
        return retval;
 }
 
-#ifdef CONFIG_PM
-static int atmel_ac97c_suspend(struct platform_device *pdev, pm_message_t msg)
+#ifdef CONFIG_PM_SLEEP
+static int atmel_ac97c_suspend(struct device *pdev)
 {
-       struct snd_card *card = platform_get_drvdata(pdev);
+       struct snd_card *card = dev_get_drvdata(pdev);
        struct atmel_ac97c *chip = card->private_data;
 
-       if (test_bit(DMA_RX_READY, &chip->flags))
-               dw_dma_cyclic_stop(chip->dma.rx_chan);
-       if (test_bit(DMA_TX_READY, &chip->flags))
-               dw_dma_cyclic_stop(chip->dma.tx_chan);
+       if (cpu_is_at32ap7000()) {
+               if (test_bit(DMA_RX_READY, &chip->flags))
+                       dw_dma_cyclic_stop(chip->dma.rx_chan);
+               if (test_bit(DMA_TX_READY, &chip->flags))
+                       dw_dma_cyclic_stop(chip->dma.tx_chan);
+       }
        clk_disable(chip->pclk);
 
        return 0;
 }
 
-static int atmel_ac97c_resume(struct platform_device *pdev)
+static int atmel_ac97c_resume(struct device *pdev)
 {
-       struct snd_card *card = platform_get_drvdata(pdev);
+       struct snd_card *card = dev_get_drvdata(pdev);
        struct atmel_ac97c *chip = card->private_data;
 
        clk_enable(chip->pclk);
-       if (test_bit(DMA_RX_READY, &chip->flags))
-               dw_dma_cyclic_start(chip->dma.rx_chan);
-       if (test_bit(DMA_TX_READY, &chip->flags))
-               dw_dma_cyclic_start(chip->dma.tx_chan);
-
+       if (cpu_is_at32ap7000()) {
+               if (test_bit(DMA_RX_READY, &chip->flags))
+                       dw_dma_cyclic_start(chip->dma.rx_chan);
+               if (test_bit(DMA_TX_READY, &chip->flags))
+                       dw_dma_cyclic_start(chip->dma.tx_chan);
+       }
        return 0;
 }
+
+static SIMPLE_DEV_PM_OPS(atmel_ac97c_pm, atmel_ac97c_suspend, atmel_ac97c_resume);
+#define ATMEL_AC97C_PM_OPS     &atmel_ac97c_pm
 #else
-#define atmel_ac97c_suspend NULL
-#define atmel_ac97c_resume NULL
+#define ATMEL_AC97C_PM_OPS     NULL
 #endif
 
 static int __devexit atmel_ac97c_remove(struct platform_device *pdev)
@@ -882,18 +1180,25 @@ 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);
-
-       if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
-               dma_release_channel(chip->dma.rx_chan);
-       if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
-               dma_release_channel(chip->dma.tx_chan);
-       clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
-       clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
-       chip->dma.rx_chan = NULL;
-       chip->dma.tx_chan = NULL;
+       free_irq(chip->irq, chip);
+
+       if (cpu_is_at32ap7000()) {
+               if (test_bit(DMA_RX_CHAN_PRESENT, &chip->flags))
+                       dma_release_channel(chip->dma.rx_chan);
+               if (test_bit(DMA_TX_CHAN_PRESENT, &chip->flags))
+                       dma_release_channel(chip->dma.tx_chan);
+               clear_bit(DMA_RX_CHAN_PRESENT, &chip->flags);
+               clear_bit(DMA_TX_CHAN_PRESENT, &chip->flags);
+               chip->dma.rx_chan = NULL;
+               chip->dma.tx_chan = NULL;
+       }
 
        snd_card_set_dev(card, NULL);
        snd_card_free(card);
@@ -907,9 +1212,9 @@ static struct platform_driver atmel_ac97c_driver = {
        .remove         = __devexit_p(atmel_ac97c_remove),
        .driver         = {
                .name   = "atmel_ac97c",
+               .owner  = THIS_MODULE,
+               .pm     = ATMEL_AC97C_PM_OPS,
        },
-       .suspend        = atmel_ac97c_suspend,
-       .resume         = atmel_ac97c_resume,
 };
 
 static int __init atmel_ac97c_init(void)
@@ -927,4 +1232,4 @@ module_exit(atmel_ac97c_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Driver for Atmel AC97 controller");
-MODULE_AUTHOR("Hans-Christian Egtvedt <hans-christian.egtvedt@atmel.com>");
+MODULE_AUTHOR("Hans-Christian Egtvedt <egtvedt@samfundet.no>");