ALSA: HDA: add powersaving hook for Realtek
[safe/jmp/linux-2.6] / sound / arm / aaci.c
index 9887703..1f0f821 100644 (file)
 #include <linux/spinlock.h>
 #include <linux/interrupt.h>
 #include <linux/err.h>
+#include <linux/amba/bus.h>
 
 #include <asm/io.h>
 #include <asm/irq.h>
-#include <asm/hardware/amba.h>
+#include <asm/sizes.h>
 
-#include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/ac97_codec.h>
@@ -39,7 +39,7 @@
  */
 #undef CONFIG_PM
 
-static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97)
+static void aaci_ac97_select_codec(struct aaci *aaci, struct snd_ac97 *ac97)
 {
        u32 v, maincr = aaci->maincr | MAINCR_SCRA(ac97->num);
 
@@ -64,15 +64,17 @@ static void aaci_ac97_select_codec(struct aaci *aaci, ac97_t *ac97)
  *  SI1TxEn, SI2TxEn and SI12TxEn bits are set in the AACI_MAINCR
  *  register.
  */
-static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val)
+static void aaci_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+                           unsigned short val)
 {
        struct aaci *aaci = ac97->private_data;
        u32 v;
+       int timeout = 5000;
 
        if (ac97->num >= 4)
                return;
 
-       down(&aaci->ac97_sem);
+       mutex_lock(&aaci->ac97_sem);
 
        aaci_ac97_select_codec(aaci, ac97);
 
@@ -88,23 +90,29 @@ static void aaci_ac97_write(ac97_t *ac97, unsigned short reg, unsigned short val
         */
        do {
                v = readl(aaci->base + AACI_SLFR);
-       } while (v & (SLFR_1TXB|SLFR_2TXB));
+       } while ((v & (SLFR_1TXB|SLFR_2TXB)) && --timeout);
+
+       if (!timeout)
+               dev_err(&aaci->dev->dev,
+                       "timeout waiting for write to complete\n");
 
-       up(&aaci->ac97_sem);
+       mutex_unlock(&aaci->ac97_sem);
 }
 
 /*
  * Read an AC'97 register.
  */
-static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg)
+static unsigned short aaci_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
 {
        struct aaci *aaci = ac97->private_data;
        u32 v;
+       int timeout = 5000;
+       int retries = 10;
 
        if (ac97->num >= 4)
                return ~0;
 
-       down(&aaci->ac97_sem);
+       mutex_lock(&aaci->ac97_sem);
 
        aaci_ac97_select_codec(aaci, ac97);
 
@@ -118,7 +126,13 @@ static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg)
         */
        do {
                v = readl(aaci->base + AACI_SLFR);
-       } while (v & SLFR_1TXB);
+       } while ((v & SLFR_1TXB) && --timeout);
+
+       if (!timeout) {
+               dev_err(&aaci->dev->dev, "timeout on slot 1 TX busy\n");
+               v = ~0;
+               goto out;
+       }
 
        /*
         * Give the AC'97 codec more than enough time
@@ -129,22 +143,36 @@ static unsigned short aaci_ac97_read(ac97_t *ac97, unsigned short reg)
        /*
         * Wait for slot 2 to indicate data.
         */
+       timeout = 5000;
        do {
                cond_resched();
                v = readl(aaci->base + AACI_SLFR) & (SLFR_1RXV|SLFR_2RXV);
-       } while (v != (SLFR_1RXV|SLFR_2RXV));
+       } while ((v != (SLFR_1RXV|SLFR_2RXV)) && --timeout);
 
-       v = readl(aaci->base + AACI_SL1RX) >> 12;
-       if (v == reg) {
-               v = readl(aaci->base + AACI_SL2RX) >> 4;
-       } else {
-               dev_err(&aaci->dev->dev,
-                       "wrong ac97 register read back (%x != %x)\n",
-                       v, reg);
+       if (!timeout) {
+               dev_err(&aaci->dev->dev, "timeout on RX valid\n");
                v = ~0;
+               goto out;
        }
 
-       up(&aaci->ac97_sem);
+       do {
+               v = readl(aaci->base + AACI_SL1RX) >> 12;
+               if (v == reg) {
+                       v = readl(aaci->base + AACI_SL2RX) >> 4;
+                       break;
+               } else if (--retries) {
+                       dev_warn(&aaci->dev->dev,
+                                "ac97 read back fail.  retry\n");
+                       continue;
+               } else {
+                       dev_warn(&aaci->dev->dev,
+                               "wrong ac97 register read back (%x != %x)\n",
+                               v, reg);
+                       v = ~0;
+               }
+       } while (retries);
+ out:
+       mutex_unlock(&aaci->ac97_sem);
        return v;
 }
 
@@ -163,10 +191,70 @@ static inline void aaci_chan_wait_ready(struct aaci_runtime *aacirun)
 /*
  * Interrupt support.
  */
-static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
+static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
 {
+       if (mask & ISR_ORINTR) {
+               dev_warn(&aaci->dev->dev, "RX overrun on chan %d\n", channel);
+               writel(ICLR_RXOEC1 << channel, aaci->base + AACI_INTCLR);
+       }
+
+       if (mask & ISR_RXTOINTR) {
+               dev_warn(&aaci->dev->dev, "RX timeout on chan %d\n", channel);
+               writel(ICLR_RXTOFEC1 << channel, aaci->base + AACI_INTCLR);
+       }
+
+       if (mask & ISR_RXINTR) {
+               struct aaci_runtime *aacirun = &aaci->capture;
+               void *ptr;
+
+               if (!aacirun->substream || !aacirun->start) {
+                       dev_warn(&aaci->dev->dev, "RX interrupt???\n");
+                       writel(0, aacirun->base + AACI_IE);
+                       return;
+               }
+               ptr = aacirun->ptr;
+
+               do {
+                       unsigned int len = aacirun->fifosz;
+                       u32 val;
+
+                       if (aacirun->bytes <= 0) {
+                               aacirun->bytes += aacirun->period;
+                               aacirun->ptr = ptr;
+                               spin_unlock(&aaci->lock);
+                               snd_pcm_period_elapsed(aacirun->substream);
+                               spin_lock(&aaci->lock);
+                       }
+                       if (!(aacirun->cr & CR_EN))
+                               break;
+
+                       val = readl(aacirun->base + AACI_SR);
+                       if (!(val & SR_RXHF))
+                               break;
+                       if (!(val & SR_RXFF))
+                               len >>= 1;
+
+                       aacirun->bytes -= len;
+
+                       /* reading 16 bytes at a time */
+                       for( ; len > 0; len -= 16) {
+                               asm(
+                                       "ldmia  %1, {r0, r1, r2, r3}\n\t"
+                                       "stmia  %0!, {r0, r1, r2, r3}"
+                                       : "+r" (ptr)
+                                       : "r" (aacirun->fifo)
+                                       : "r0", "r1", "r2", "r3", "cc");
+
+                               if (ptr >= aacirun->end)
+                                       ptr = aacirun->start;
+                       }
+               } while(1);
+               aacirun->ptr = ptr;
+       }
+
        if (mask & ISR_URINTR) {
-               writel(ICLR_TXUEC1, aaci->base + AACI_INTCLR);
+               dev_dbg(&aaci->dev->dev, "TX underrun on chan %d\n", channel);
+               writel(ICLR_TXUEC1 << channel, aaci->base + AACI_INTCLR);
        }
 
        if (mask & ISR_TXINTR) {
@@ -174,7 +262,7 @@ static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
                void *ptr;
 
                if (!aacirun->substream || !aacirun->start) {
-                       dev_warn(&aaci->dev->dev, "TX interrupt???");
+                       dev_warn(&aaci->dev->dev, "TX interrupt???\n");
                        writel(0, aacirun->base + AACI_IE);
                        return;
                }
@@ -191,7 +279,7 @@ static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
                                snd_pcm_period_elapsed(aacirun->substream);
                                spin_lock(&aaci->lock);
                        }
-                       if (!(aacirun->cr & TXCR_TXEN))
+                       if (!(aacirun->cr & CR_EN))
                                break;
 
                        val = readl(aacirun->base + AACI_SR);
@@ -220,7 +308,7 @@ static void aaci_fifo_irq(struct aaci *aaci, u32 mask)
        }
 }
 
-static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs)
+static irqreturn_t aaci_irq(int irq, void *devid)
 {
        struct aaci *aaci = devid;
        u32 mask;
@@ -232,7 +320,7 @@ static irqreturn_t aaci_irq(int irq, void *devid, struct pt_regs *regs)
                u32 m = mask;
                for (i = 0; i < 4; i++, m >>= 7) {
                        if (m & 0x7f) {
-                               aaci_fifo_irq(aaci, m);
+                               aaci_fifo_irq(aaci, i, m);
                        }
                }
        }
@@ -283,11 +371,11 @@ static unsigned int rate_list[] = {
  *  (unimplemented)
  */
 static int
-aaci_rule_rate_by_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+aaci_rule_rate_by_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
 {
        struct aaci *aaci = rule->private;
        unsigned int rate_mask = SNDRV_PCM_RATE_8000_48000|SNDRV_PCM_RATE_5512;
-       snd_interval_t *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval *c = hw_param_interval(p, SNDRV_PCM_HW_PARAM_CHANNELS);
 
        switch (c->max) {
        case 6:
@@ -303,7 +391,7 @@ aaci_rule_rate_by_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
                                 rate_mask);
 }
 
-static snd_pcm_hardware_t aaci_hw_info = {
+static struct snd_pcm_hardware aaci_hw_info = {
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
                                  SNDRV_PCM_INFO_INTERLEAVED |
@@ -329,10 +417,11 @@ static snd_pcm_hardware_t aaci_hw_info = {
        .periods_max            = PAGE_SIZE / 16,
 };
 
-static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream,
-                        struct aaci_runtime *aacirun)
+static int __aaci_pcm_open(struct aaci *aaci,
+                          struct snd_pcm_substream *substream,
+                          struct aaci_runtime *aacirun)
 {
-       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        int ret;
 
        aacirun->substream = substream;
@@ -359,7 +448,7 @@ static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream,
        if (ret)
                goto out;
 
-       ret = request_irq(aaci->dev->irq[0], aaci_irq, SA_SHIRQ|SA_INTERRUPT,
+       ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
                          DRIVER_NAME, aaci);
        if (ret)
                goto out;
@@ -374,12 +463,12 @@ static int aaci_pcm_open(struct aaci *aaci, snd_pcm_substream_t *substream,
 /*
  * Common ALSA stuff
  */
-static int aaci_pcm_close(snd_pcm_substream_t *substream)
+static int aaci_pcm_close(struct snd_pcm_substream *substream)
 {
        struct aaci *aaci = substream->private_data;
        struct aaci_runtime *aacirun = substream->runtime->private_data;
 
-       WARN_ON(aacirun->cr & TXCR_TXEN);
+       WARN_ON(aacirun->cr & CR_EN);
 
        aacirun->substream = NULL;
        free_irq(aaci->dev->irq[0], aaci);
@@ -387,14 +476,14 @@ static int aaci_pcm_close(snd_pcm_substream_t *substream)
        return 0;
 }
 
-static int aaci_pcm_hw_free(snd_pcm_substream_t *substream)
+static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
 {
        struct aaci_runtime *aacirun = substream->runtime->private_data;
 
        /*
         * This must not be called with the device enabled.
         */
-       WARN_ON(aacirun->cr & TXCR_TXEN);
+       WARN_ON(aacirun->cr & CR_EN);
 
        if (aacirun->pcm_open)
                snd_ac97_pcm_close(aacirun->pcm);
@@ -408,9 +497,9 @@ static int aaci_pcm_hw_free(snd_pcm_substream_t *substream)
        return 0;
 }
 
-static int aaci_pcm_hw_params(snd_pcm_substream_t *substream,
+static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
                              struct aaci_runtime *aacirun,
-                             snd_pcm_hw_params_t *params)
+                             struct snd_pcm_hw_params *params)
 {
        int err;
 
@@ -421,9 +510,15 @@ static int aaci_pcm_hw_params(snd_pcm_substream_t *substream,
        if (err < 0)
                goto out;
 
-       err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
-                               params_channels(params),
-                               aacirun->pcm->r[0].slots);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+                                       params_channels(params),
+                                       aacirun->pcm->r[0].slots);
+       else
+               err = snd_ac97_pcm_open(aacirun->pcm, params_rate(params),
+                                       params_channels(params),
+                                       aacirun->pcm->r[1].slots);
+
        if (err)
                goto out;
 
@@ -433,9 +528,9 @@ static int aaci_pcm_hw_params(snd_pcm_substream_t *substream,
        return err;
 }
 
-static int aaci_pcm_prepare(snd_pcm_substream_t *substream)
+static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
 {
-       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct aaci_runtime *aacirun = runtime->private_data;
 
        aacirun->start  = (void *)runtime->dma_area;
@@ -447,16 +542,16 @@ static int aaci_pcm_prepare(snd_pcm_substream_t *substream)
        return 0;
 }
 
-static snd_pcm_uframes_t aaci_pcm_pointer(snd_pcm_substream_t *substream)
+static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
 {
-       snd_pcm_runtime_t *runtime = substream->runtime;
+       struct snd_pcm_runtime *runtime = substream->runtime;
        struct aaci_runtime *aacirun = runtime->private_data;
        ssize_t bytes = aacirun->ptr - aacirun->start;
 
        return bytes_to_frames(runtime, bytes);
 }
 
-static int aaci_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *vma)
+static int aaci_pcm_mmap(struct snd_pcm_substream *substream, struct vm_area_struct *vma)
 {
        return devdma_mmap(NULL, substream, vma);
 }
@@ -466,9 +561,9 @@ static int aaci_pcm_mmap(snd_pcm_substream_t *substream, struct vm_area_struct *
  * Playback specific ALSA stuff
  */
 static const u32 channels_to_txmask[] = {
-       [2] = TXCR_TX3 | TXCR_TX4,
-       [4] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8,
-       [6] = TXCR_TX3 | TXCR_TX4 | TXCR_TX7 | TXCR_TX8 | TXCR_TX6 | TXCR_TX9,
+       [2] = CR_SL3 | CR_SL4,
+       [4] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8,
+       [6] = CR_SL3 | CR_SL4 | CR_SL7 | CR_SL8 | CR_SL6 | CR_SL9,
 };
 
 /*
@@ -483,7 +578,7 @@ static const u32 channels_to_txmask[] = {
 static unsigned int channel_list[] = { 2, 4, 6 };
 
 static int
-aaci_rule_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
+aaci_rule_channels(struct snd_pcm_hw_params *p, struct snd_pcm_hw_rule *rule)
 {
        struct aaci *aaci = rule->private;
        unsigned int chan_mask = 1 << 0, slots;
@@ -503,7 +598,7 @@ aaci_rule_channels(snd_pcm_hw_params_t *p, snd_pcm_hw_rule_t *rule)
                                 chan_mask);
 }
 
-static int aaci_pcm_playback_open(snd_pcm_substream_t *substream)
+static int aaci_pcm_open(struct snd_pcm_substream *substream)
 {
        struct aaci *aaci = substream->private_data;
        int ret;
@@ -518,11 +613,16 @@ static int aaci_pcm_playback_open(snd_pcm_substream_t *substream)
        if (ret)
                return ret;
 
-       return aaci_pcm_open(aaci, substream, &aaci->playback);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               ret = __aaci_pcm_open(aaci, substream, &aaci->playback);
+       } else {
+               ret = __aaci_pcm_open(aaci, substream, &aaci->capture);
+       }
+       return ret;
 }
 
-static int aaci_pcm_playback_hw_params(snd_pcm_substream_t *substream,
-                                      snd_pcm_hw_params_t *params)
+static int aaci_pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                                      struct snd_pcm_hw_params *params)
 {
        struct aaci *aaci = substream->private_data;
        struct aaci_runtime *aacirun = substream->runtime->private_data;
@@ -539,11 +639,11 @@ static int aaci_pcm_playback_hw_params(snd_pcm_substream_t *substream,
         * FIXME: double rate slots?
         */
        if (ret >= 0) {
-               aacirun->cr = TXCR_FEN | TXCR_COMPACT | TXCR_TSZ16;
+               aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
                aacirun->cr |= channels_to_txmask[channels];
 
                aacirun->fifosz = aaci->fifosize * 4;
-               if (aacirun->cr & TXCR_COMPACT)
+               if (aacirun->cr & CR_COMPACT)
                        aacirun->fifosz >>= 1;
        }
        return ret;
@@ -556,7 +656,7 @@ static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
        ie = readl(aacirun->base + AACI_IE);
        ie &= ~(IE_URIE|IE_TXIE);
        writel(ie, aacirun->base + AACI_IE);
-       aacirun->cr &= ~TXCR_TXEN;
+       aacirun->cr &= ~CR_EN;
        aaci_chan_wait_ready(aacirun);
        writel(aacirun->cr, aacirun->base + AACI_TXCR);
 }
@@ -566,7 +666,7 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
        u32 ie;
 
        aaci_chan_wait_ready(aacirun);
-       aacirun->cr |= TXCR_TXEN;
+       aacirun->cr |= CR_EN;
 
        ie = readl(aacirun->base + AACI_IE);
        ie |= IE_URIE | IE_TXIE;
@@ -574,7 +674,7 @@ static void aaci_pcm_playback_start(struct aaci_runtime *aacirun)
        writel(aacirun->cr, aacirun->base + AACI_TXCR);
 }
 
-static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd)
+static int aaci_pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct aaci *aaci = substream->private_data;
        struct aaci_runtime *aacirun = substream->runtime->private_data;
@@ -613,8 +713,8 @@ static int aaci_pcm_playback_trigger(snd_pcm_substream_t *substream, int cmd)
        return ret;
 }
 
-static snd_pcm_ops_t aaci_playback_ops = {
-       .open           = aaci_pcm_playback_open,
+static struct snd_pcm_ops aaci_playback_ops = {
+       .open           = aaci_pcm_open,
        .close          = aaci_pcm_close,
        .ioctl          = snd_pcm_lib_ioctl,
        .hw_params      = aaci_pcm_playback_hw_params,
@@ -625,40 +725,161 @@ static snd_pcm_ops_t aaci_playback_ops = {
        .mmap           = aaci_pcm_mmap,
 };
 
+static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                                     struct snd_pcm_hw_params *params)
+{
+       struct aaci *aaci = substream->private_data;
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+       int ret;
+
+       ret = aaci_pcm_hw_params(substream, aacirun, params);
+
+       if (ret >= 0) {
+               aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
+
+               /* Line in record: slot 3 and 4 */
+               aacirun->cr |= CR_SL3 | CR_SL4;
+
+               aacirun->fifosz = aaci->fifosize * 4;
+
+               if (aacirun->cr & CR_COMPACT)
+                       aacirun->fifosz >>= 1;
+       }
+       return ret;
+}
+
+static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
+{
+       u32 ie;
+
+       aaci_chan_wait_ready(aacirun);
+
+       ie = readl(aacirun->base + AACI_IE);
+       ie &= ~(IE_ORIE | IE_RXIE);
+       writel(ie, aacirun->base+AACI_IE);
+
+       aacirun->cr &= ~CR_EN;
 
+       writel(aacirun->cr, aacirun->base + AACI_RXCR);
+}
+
+static void aaci_pcm_capture_start(struct aaci_runtime *aacirun)
+{
+       u32 ie;
+
+       aaci_chan_wait_ready(aacirun);
+
+#ifdef DEBUG
+       /* RX Timeout value: bits 28:17 in RXCR */
+       aacirun->cr |= 0xf << 17;
+#endif
+
+       aacirun->cr |= CR_EN;
+       writel(aacirun->cr, aacirun->base + AACI_RXCR);
+
+       ie = readl(aacirun->base + AACI_IE);
+       ie |= IE_ORIE |IE_RXIE; // overrun and rx interrupt -- half full
+       writel(ie, aacirun->base + AACI_IE);
+}
+
+static int aaci_pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct aaci *aaci = substream->private_data;
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&aaci->lock, flags);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               aaci_pcm_capture_start(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_RESUME:
+               aaci_pcm_capture_start(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+               aaci_pcm_capture_stop(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               aaci_pcm_capture_stop(aacirun);
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               break;
+
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               break;
+
+       default:
+               ret = -EINVAL;
+       }
+
+       spin_unlock_irqrestore(&aaci->lock, flags);
+
+       return ret;
+}
+
+static int aaci_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct aaci *aaci = substream->private_data;
+
+       aaci_pcm_prepare(substream);
+
+       /* allow changing of sample rate */
+       aaci_ac97_write(aaci->ac97, AC97_EXTENDED_STATUS, 0x0001); /* VRA */
+       aaci_ac97_write(aaci->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
+       aaci_ac97_write(aaci->ac97, AC97_PCM_MIC_ADC_RATE, runtime->rate);
+
+       /* Record select: Mic: 0, Aux: 3, Line: 4 */
+       aaci_ac97_write(aaci->ac97, AC97_REC_SEL, 0x0404);
+
+       return 0;
+}
+
+static struct snd_pcm_ops aaci_capture_ops = {
+       .open           = aaci_pcm_open,
+       .close          = aaci_pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = aaci_pcm_capture_hw_params,
+       .hw_free        = aaci_pcm_hw_free,
+       .prepare        = aaci_pcm_capture_prepare,
+       .trigger        = aaci_pcm_capture_trigger,
+       .pointer        = aaci_pcm_pointer,
+       .mmap           = aaci_pcm_mmap,
+};
 
 /*
  * Power Management.
  */
 #ifdef CONFIG_PM
-static int aaci_do_suspend(snd_card_t *card, unsigned int state)
+static int aaci_do_suspend(struct snd_card *card, unsigned int state)
 {
        struct aaci *aaci = card->private_data;
-       if (aaci->card->power_state != SNDRV_CTL_POWER_D3cold) {
-               snd_pcm_suspend_all(aaci->pcm);
-               snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D3cold);
-       }
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
+       snd_pcm_suspend_all(aaci->pcm);
        return 0;
 }
 
-static int aaci_do_resume(snd_card_t *card, unsigned int state)
+static int aaci_do_resume(struct snd_card *card, unsigned int state)
 {
-       struct aaci *aaci = card->private_data;
-       if (aaci->card->power_state != SNDRV_CTL_POWER_D0) {
-               snd_power_change_state(aaci->card, SNDRV_CTL_POWER_D0);
-       }
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
        return 0;
 }
 
-static int aaci_suspend(struct amba_device *dev, u32 state)
+static int aaci_suspend(struct amba_device *dev, pm_message_t state)
 {
-       snd_card_t *card = amba_get_drvdata(dev);
+       struct snd_card *card = amba_get_drvdata(dev);
        return card ? aaci_do_suspend(card) : 0;
 }
 
 static int aaci_resume(struct amba_device *dev)
 {
-       snd_card_t *card = amba_get_drvdata(dev);
+       struct snd_card *card = amba_get_drvdata(dev);
        return card ? aaci_do_resume(card) : 0;
 }
 #else
@@ -670,7 +891,7 @@ static int aaci_resume(struct amba_device *dev)
 
 
 static struct ac97_pcm ac97_defs[] __devinitdata = {
-       [0] = {         /* Front PCM */
+       [0] = { /* Front PCM */
                .exclusive = 1,
                .r = {
                        [0] = {
@@ -704,18 +925,19 @@ static struct ac97_pcm ac97_defs[] __devinitdata = {
        }
 };
 
-static ac97_bus_ops_t aaci_bus_ops = {
+static struct snd_ac97_bus_ops aaci_bus_ops = {
        .write  = aaci_ac97_write,
        .read   = aaci_ac97_read,
 };
 
 static int __devinit aaci_probe_ac97(struct aaci *aaci)
 {
-       ac97_template_t ac97_template;
-       ac97_bus_t *ac97_bus;
-       ac97_t *ac97;
+       struct snd_ac97_template ac97_template;
+       struct snd_ac97_bus *ac97_bus;
+       struct snd_ac97 *ac97;
        int ret;
 
+       writel(0, aaci->base + AC97_POWERDOWN);
        /*
         * Assert AACIRESET for 2us
         */
@@ -736,7 +958,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
        ac97_bus->clock = 48000;
        aaci->ac97_bus = ac97_bus;
 
-       memset(&ac97_template, 0, sizeof(ac97_template_t));
+       memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
        ac97_template.private_data = aaci;
        ac97_template.num = 0;
        ac97_template.scaps = AC97_SCAP_SKIP_MODEM;
@@ -744,6 +966,7 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
        ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
        if (ret)
                goto out;
+       aaci->ac97 = ac97;
 
        /*
         * Disable AC97 PC Beep input on audio codecs.
@@ -756,12 +979,13 @@ static int __devinit aaci_probe_ac97(struct aaci *aaci)
                goto out;
 
        aaci->playback.pcm = &ac97_bus->pcms[0];
+       aaci->capture.pcm  = &ac97_bus->pcms[1];
 
  out:
        return ret;
 }
 
-static void aaci_free_card(snd_card_t *card)
+static void aaci_free_card(struct snd_card *card)
 {
        struct aaci *aaci = card->private_data;
        if (aaci->base)
@@ -771,24 +995,25 @@ static void aaci_free_card(snd_card_t *card)
 static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
 {
        struct aaci *aaci;
-       snd_card_t *card;
+       struct snd_card *card;
+       int err;
 
-       card = snd_card_new(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
-                           THIS_MODULE, sizeof(struct aaci));
-       if (card == NULL)
-               return ERR_PTR(-ENOMEM);
+       err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1,
+                             THIS_MODULE, sizeof(struct aaci), &card);
+       if (err < 0)
+               return NULL;
 
        card->private_free = aaci_free_card;
-       snd_card_set_pm_callback(card, aaci_do_suspend, aaci_do_resume, NULL);
 
        strlcpy(card->driver, DRIVER_NAME, sizeof(card->driver));
        strlcpy(card->shortname, "ARM AC'97 Interface", sizeof(card->shortname));
        snprintf(card->longname, sizeof(card->longname),
-                "%s at 0x%08lx, irq %d",
-                card->shortname, dev->res.start, dev->irq[0]);
+                "%s at 0x%016llx, irq %d",
+                card->shortname, (unsigned long long)dev->res.start,
+                dev->irq[0]);
 
        aaci = card->private_data;
-       init_MUTEX(&aaci->ac97_sem);
+       mutex_init(&aaci->ac97_sem);
        spin_lock_init(&aaci->lock);
        aaci->card = card;
        aaci->dev = dev;
@@ -802,10 +1027,10 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
 
 static int __devinit aaci_init_pcm(struct aaci *aaci)
 {
-       snd_pcm_t *pcm;
+       struct snd_pcm *pcm;
        int ret;
 
-       ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 0, &pcm);
+       ret = snd_pcm_new(aaci->card, "AACI AC'97", 0, 1, 1, &pcm);
        if (ret == 0) {
                aaci->pcm = pcm;
                pcm->private_data = aaci;
@@ -814,6 +1039,7 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
                strlcpy(pcm->name, DRIVER_NAME, sizeof(pcm->name));
 
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
+               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
        }
 
        return ret;
@@ -821,15 +1047,15 @@ static int __devinit aaci_init_pcm(struct aaci *aaci)
 
 static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
 {
-       void __iomem *base = aaci->base + AACI_CSCH1;
+       struct aaci_runtime *aacirun = &aaci->playback;
        int i;
 
-       writel(TXCR_FEN | TXCR_TSZ16 | TXCR_TXEN, base + AACI_TXCR);
+       writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
 
-       for (i = 0; !(readl(base + AACI_SR) & SR_TXFF) && i < 4096; i++)
-               writel(0, aaci->base + AACI_DR1);
+       for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
+               writel(0, aacirun->fifo);
 
-       writel(0, base + AACI_TXCR);
+       writel(0, aacirun->base + AACI_TXCR);
 
        /*
         * Re-initialise the AACI after the FIFO depth test, to
@@ -849,7 +1075,7 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
        return i;
 }
 
-static int __devinit aaci_probe(struct amba_device *dev, void *id)
+static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
 {
        struct aaci *aaci;
        int ret, i;
@@ -859,12 +1085,12 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
                return ret;
 
        aaci = aaci_init_card(dev);
-       if (IS_ERR(aaci)) {
-               ret = PTR_ERR(aaci);
+       if (!aaci) {
+               ret = -ENOMEM;
                goto out;
        }
 
-       aaci->base = ioremap(dev->res.start, SZ_4K);
+       aaci->base = ioremap(dev->res.start, resource_size(&dev->res));
        if (!aaci->base) {
                ret = -ENOMEM;
                goto out;
@@ -876,6 +1102,12 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
        aaci->playback.base = aaci->base + AACI_CSCH1;
        aaci->playback.fifo = aaci->base + AACI_DR1;
 
+       /*
+        * Capture uses AACI channel 0
+        */
+       aaci->capture.base = aaci->base + AACI_CSCH1;
+       aaci->capture.fifo = aaci->base + AACI_DR1;
+
        for (i = 0; i < 4; i++) {
                void __iomem *base = aaci->base + i * 0x14;
 
@@ -887,23 +1119,31 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
        writel(0x1fff, aaci->base + AACI_INTCLR);
        writel(aaci->maincr, aaci->base + AACI_MAINCR);
 
+       ret = aaci_probe_ac97(aaci);
+       if (ret)
+               goto out;
+
        /*
-        * Size the FIFOs.
+        * Size the FIFOs (must be multiple of 16).
         */
        aaci->fifosize = aaci_size_fifo(aaci);
-
-       ret = aaci_probe_ac97(aaci);
-       if (ret)
+       if (aaci->fifosize & 15) {
+               printk(KERN_WARNING "AACI: fifosize = %d not supported\n",
+                      aaci->fifosize);
+               ret = -ENODEV;
                goto out;
+       }
 
        ret = aaci_init_pcm(aaci);
        if (ret)
                goto out;
 
+       snd_card_set_dev(aaci->card, &dev->dev);
+
        ret = snd_card_register(aaci->card);
        if (ret == 0) {
                dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
-                       aaci->fifosize);
+                        aaci->fifosize);
                amba_set_drvdata(dev, aaci->card);
                return ret;
        }
@@ -917,7 +1157,7 @@ static int __devinit aaci_probe(struct amba_device *dev, void *id)
 
 static int __devexit aaci_remove(struct amba_device *dev)
 {
-       snd_card_t *card = amba_get_drvdata(dev);
+       struct snd_card *card = amba_get_drvdata(dev);
 
        amba_set_drvdata(dev, NULL);