Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6
[pandora-kernel.git] / sound / arm / aaci.c
index 7c1fc64..d0cead3 100644 (file)
@@ -210,6 +210,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
 
        if (mask & ISR_RXINTR) {
                struct aaci_runtime *aacirun = &aaci->capture;
+               bool period_elapsed = false;
                void *ptr;
 
                if (!aacirun->substream || !aacirun->start) {
@@ -222,15 +223,12 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
 
                ptr = aacirun->ptr;
                do {
-                       unsigned int len = aacirun->fifosz;
+                       unsigned int len = aacirun->fifo_bytes;
                        u32 val;
 
                        if (aacirun->bytes <= 0) {
                                aacirun->bytes += aacirun->period;
-                               aacirun->ptr = ptr;
-                               spin_unlock(&aacirun->lock);
-                               snd_pcm_period_elapsed(aacirun->substream);
-                               spin_lock(&aacirun->lock);
+                               period_elapsed = true;
                        }
                        if (!(aacirun->cr & CR_EN))
                                break;
@@ -260,6 +258,9 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
                aacirun->ptr = ptr;
 
                spin_unlock(&aacirun->lock);
+
+               if (period_elapsed)
+                       snd_pcm_period_elapsed(aacirun->substream);
        }
 
        if (mask & ISR_URINTR) {
@@ -269,6 +270,7 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
 
        if (mask & ISR_TXINTR) {
                struct aaci_runtime *aacirun = &aaci->playback;
+               bool period_elapsed = false;
                void *ptr;
 
                if (!aacirun->substream || !aacirun->start) {
@@ -281,15 +283,12 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
 
                ptr = aacirun->ptr;
                do {
-                       unsigned int len = aacirun->fifosz;
+                       unsigned int len = aacirun->fifo_bytes;
                        u32 val;
 
                        if (aacirun->bytes <= 0) {
                                aacirun->bytes += aacirun->period;
-                               aacirun->ptr = ptr;
-                               spin_unlock(&aacirun->lock);
-                               snd_pcm_period_elapsed(aacirun->substream);
-                               spin_lock(&aacirun->lock);
+                               period_elapsed = true;
                        }
                        if (!(aacirun->cr & CR_EN))
                                break;
@@ -319,6 +318,9 @@ static void aaci_fifo_irq(struct aaci *aaci, int channel, u32 mask)
                aacirun->ptr = ptr;
 
                spin_unlock(&aacirun->lock);
+
+               if (period_elapsed)
+                       snd_pcm_period_elapsed(aacirun->substream);
        }
 }
 
@@ -361,7 +363,7 @@ static struct snd_pcm_hardware aaci_hw_info = {
 
        /* rates are setup from the AC'97 codec */
        .channels_min           = 2,
-       .channels_max           = 6,
+       .channels_max           = 2,
        .buffer_bytes_max       = 64 * 1024,
        .period_bytes_min       = 256,
        .period_bytes_max       = PAGE_SIZE,
@@ -369,12 +371,46 @@ static struct snd_pcm_hardware aaci_hw_info = {
        .periods_max            = PAGE_SIZE / 16,
 };
 
-static int __aaci_pcm_open(struct aaci *aaci,
-                          struct snd_pcm_substream *substream,
-                          struct aaci_runtime *aacirun)
+/*
+ * We can support two and four channel audio.  Unfortunately
+ * six channel audio requires a non-standard channel ordering:
+ *   2 -> FL(3), FR(4)
+ *   4 -> FL(3), FR(4), SL(7), SR(8)
+ *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
+ *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
+ * This requires an ALSA configuration file to correct.
+ */
+static int aaci_rule_channels(struct snd_pcm_hw_params *p,
+       struct snd_pcm_hw_rule *rule)
+{
+       static unsigned int channel_list[] = { 2, 4, 6 };
+       struct aaci *aaci = rule->private;
+       unsigned int mask = 1 << 0, slots;
+
+       /* pcms[0] is the our 5.1 PCM instance. */
+       slots = aaci->ac97_bus->pcms[0].r[0].slots;
+       if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
+               mask |= 1 << 1;
+               if (slots & (1 << AC97_SLOT_LFE))
+                       mask |= 1 << 2;
+       }
+
+       return snd_interval_list(hw_param_interval(p, rule->var),
+                                ARRAY_SIZE(channel_list), channel_list, mask);
+}
+
+static int aaci_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int ret;
+       struct aaci *aaci = substream->private_data;
+       struct aaci_runtime *aacirun;
+       int ret = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               aacirun = &aaci->playback;
+       } else {
+               aacirun = &aaci->capture;
+       }
 
        aacirun->substream = substream;
        runtime->private_data = aacirun;
@@ -382,27 +418,37 @@ static int __aaci_pcm_open(struct aaci *aaci,
        runtime->hw.rates = aacirun->pcm->rates;
        snd_pcm_limit_hw_rates(runtime);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
-           aacirun->pcm->r[1].slots)
-               snd_ac97_pcm_double_rate_rules(runtime);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               runtime->hw.channels_max = 6;
+
+               /* Add rule describing channel dependency. */
+               ret = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                         SNDRV_PCM_HW_PARAM_CHANNELS,
+                                         aaci_rule_channels, aaci,
+                                         SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+               if (ret)
+                       return ret;
+
+               if (aacirun->pcm->r[1].slots)
+                       snd_ac97_pcm_double_rate_rules(runtime);
+       }
 
        /*
-        * FIXME: ALSA specifies fifo_size in bytes.  If we're in normal
-        * mode, each 32-bit word contains one sample.  If we're in
-        * compact mode, each 32-bit word contains two samples, effectively
-        * halving the FIFO size.  However, we don't know for sure which
-        * we'll be using at this point.  We set this to the lower limit.
+        * ALSA wants the byte-size of the FIFOs.  As we only support
+        * 16-bit samples, this is twice the FIFO depth irrespective
+        * of whether it's in compact mode or not.
         */
-       runtime->hw.fifo_size = aaci->fifosize * 2;
-
-       ret = request_irq(aaci->dev->irq[0], aaci_irq, IRQF_SHARED|IRQF_DISABLED,
-                         DRIVER_NAME, aaci);
-       if (ret)
-               goto out;
-
-       return 0;
+       runtime->hw.fifo_size = aaci->fifo_depth * 2;
+
+       mutex_lock(&aaci->irq_lock);
+       if (!aaci->users++) {
+               ret = request_irq(aaci->dev->irq[0], aaci_irq,
+                          IRQF_SHARED | IRQF_DISABLED, DRIVER_NAME, aaci);
+               if (ret != 0)
+                       aaci->users--;
+       }
+       mutex_unlock(&aaci->irq_lock);
 
- out:
        return ret;
 }
 
@@ -418,7 +464,11 @@ static int aaci_pcm_close(struct snd_pcm_substream *substream)
        WARN_ON(aacirun->cr & CR_EN);
 
        aacirun->substream = NULL;
-       free_irq(aaci->dev->irq[0], aaci);
+
+       mutex_lock(&aaci->irq_lock);
+       if (!--aaci->users)
+               free_irq(aaci->dev->irq[0], aaci);
+       mutex_unlock(&aaci->irq_lock);
 
        return 0;
 }
@@ -444,12 +494,21 @@ static int aaci_pcm_hw_free(struct snd_pcm_substream *substream)
        return 0;
 }
 
+/* Channel to slot mask */
+static const u32 channels_to_slotmask[] = {
+       [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,
+};
+
 static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
-                             struct aaci_runtime *aacirun,
                              struct snd_pcm_hw_params *params)
 {
+       struct aaci_runtime *aacirun = substream->runtime->private_data;
+       unsigned int channels = params_channels(params);
+       unsigned int rate = params_rate(params);
+       int dbl = rate > 48000;
        int err;
-       struct aaci *aaci = substream->private_data;
 
        aaci_pcm_hw_free(substream);
        if (aacirun->pcm_open) {
@@ -457,22 +516,28 @@ static int aaci_pcm_hw_params(struct snd_pcm_substream *substream,
                aacirun->pcm_open = 0;
        }
 
+       /* channels is already limited to 2, 4, or 6 by aaci_rule_channels */
+       if (dbl && channels != 2)
+               return -EINVAL;
+
        err = snd_pcm_lib_malloc_pages(substream,
                                       params_buffer_bytes(params));
        if (err >= 0) {
-               unsigned int rate = params_rate(params);
-               int dbl = rate > 48000;
+               struct aaci *aaci = substream->private_data;
 
-               err = snd_ac97_pcm_open(aacirun->pcm, rate,
-                                       params_channels(params),
+               err = snd_ac97_pcm_open(aacirun->pcm, rate, channels,
                                        aacirun->pcm->r[dbl].slots);
 
                aacirun->pcm_open = err == 0;
                aacirun->cr = CR_FEN | CR_COMPACT | CR_SZ16;
-               aacirun->fifosz = aaci->fifosize * 4;
-
-               if (aacirun->cr & CR_COMPACT)
-                       aacirun->fifosz >>= 1;
+               aacirun->cr |= channels_to_slotmask[channels + dbl * 2];
+
+               /*
+                * fifo_bytes is the number of bytes we transfer to/from
+                * the FIFO, including padding.  So that's x4.  As we're
+                * in compact mode, the FIFO is half the size.
+                */
+               aacirun->fifo_bytes = aaci->fifo_depth * 4 / 2;
        }
 
        return err;
@@ -483,11 +548,11 @@ static int aaci_pcm_prepare(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct aaci_runtime *aacirun = runtime->private_data;
 
+       aacirun->period = snd_pcm_lib_period_bytes(substream);
        aacirun->start  = runtime->dma_area;
        aacirun->end    = aacirun->start + snd_pcm_lib_buffer_bytes(substream);
        aacirun->ptr    = aacirun->start;
-       aacirun->period =
-       aacirun->bytes  = frames_to_bytes(runtime, runtime->period_size);
+       aacirun->bytes  = aacirun->period;
 
        return 0;
 }
@@ -505,89 +570,6 @@ static snd_pcm_uframes_t aaci_pcm_pointer(struct snd_pcm_substream *substream)
 /*
  * Playback specific ALSA stuff
  */
-static const u32 channels_to_txmask[] = {
-       [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,
-};
-
-/*
- * We can support two and four channel audio.  Unfortunately
- * six channel audio requires a non-standard channel ordering:
- *   2 -> FL(3), FR(4)
- *   4 -> FL(3), FR(4), SL(7), SR(8)
- *   6 -> FL(3), FR(4), SL(7), SR(8), C(6), LFE(9) (required)
- *        FL(3), FR(4), C(6), SL(7), SR(8), LFE(9) (actual)
- * This requires an ALSA configuration file to correct.
- */
-static unsigned int channel_list[] = { 2, 4, 6 };
-
-static int
-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;
-
-       /*
-        * pcms[0] is the our 5.1 PCM instance.
-        */
-       slots = aaci->ac97_bus->pcms[0].r[0].slots;
-       if (slots & (1 << AC97_SLOT_PCM_SLEFT)) {
-               chan_mask |= 1 << 1;
-               if (slots & (1 << AC97_SLOT_LFE))
-                       chan_mask |= 1 << 2;
-       }
-
-       return snd_interval_list(hw_param_interval(p, rule->var),
-                                ARRAY_SIZE(channel_list), channel_list,
-                                chan_mask);
-}
-
-static int aaci_pcm_open(struct snd_pcm_substream *substream)
-{
-       struct aaci *aaci = substream->private_data;
-       int ret;
-
-       /*
-        * Add rule describing channel dependency.
-        */
-       ret = snd_pcm_hw_rule_add(substream->runtime, 0,
-                                 SNDRV_PCM_HW_PARAM_CHANNELS,
-                                 aaci_rule_channels, aaci,
-                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-       if (ret)
-               return ret;
-
-       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(struct snd_pcm_substream *substream,
-                                      struct snd_pcm_hw_params *params)
-{
-       struct aaci_runtime *aacirun = substream->runtime->private_data;
-       unsigned int channels = params_channels(params);
-       int ret;
-
-       WARN_ON(channels >= ARRAY_SIZE(channels_to_txmask) ||
-               !channels_to_txmask[channels]);
-
-       ret = aaci_pcm_hw_params(substream, aacirun, params);
-
-       /*
-        * Enable FIFO, compact mode, 16 bits per sample.
-        * FIXME: double rate slots?
-        */
-       if (ret >= 0)
-               aacirun->cr |= channels_to_txmask[channels];
-
-       return ret;
-}
-
 static void aaci_pcm_playback_stop(struct aaci_runtime *aacirun)
 {
        u32 ie;
@@ -657,27 +639,13 @@ 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,
+       .hw_params      = aaci_pcm_hw_params,
        .hw_free        = aaci_pcm_hw_free,
        .prepare        = aaci_pcm_prepare,
        .trigger        = aaci_pcm_playback_trigger,
        .pointer        = aaci_pcm_pointer,
 };
 
-static int aaci_pcm_capture_hw_params(struct snd_pcm_substream *substream,
-                                     struct snd_pcm_hw_params *params)
-{
-       struct aaci_runtime *aacirun = substream->runtime->private_data;
-       int ret;
-
-       ret = aaci_pcm_hw_params(substream, aacirun, params);
-       if (ret >= 0)
-               /* Line in record: slot 3 and 4 */
-               aacirun->cr |= CR_SL3 | CR_SL4;
-
-       return ret;
-}
-
 static void aaci_pcm_capture_stop(struct aaci_runtime *aacirun)
 {
        u32 ie;
@@ -774,7 +742,7 @@ 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_params      = aaci_pcm_hw_params,
        .hw_free        = aaci_pcm_hw_free,
        .prepare        = aaci_pcm_capture_prepare,
        .trigger        = aaci_pcm_capture_trigger,
@@ -941,12 +909,13 @@ static struct aaci * __devinit aaci_init_card(struct amba_device *dev)
        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%016llx, irq %d",
-                card->shortname, (unsigned long long)dev->res.start,
-                dev->irq[0]);
+                "%s PL%03x rev%u at 0x%08llx, irq %d",
+                card->shortname, amba_part(dev), amba_rev(dev),
+                (unsigned long long)dev->res.start, dev->irq[0]);
 
        aaci = card->private_data;
        mutex_init(&aaci->ac97_sem);
+       mutex_init(&aaci->irq_lock);
        aaci->card = card;
        aaci->dev = dev;
 
@@ -984,6 +953,10 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
        struct aaci_runtime *aacirun = &aaci->playback;
        int i;
 
+       /*
+        * Enable the channel, but don't assign it to any slots, so
+        * it won't empty onto the AC'97 link.
+        */
        writel(CR_FEN | CR_SZ16 | CR_EN, aacirun->base + AACI_TXCR);
 
        for (i = 0; !(readl(aacirun->base + AACI_SR) & SR_TXFF) && i < 4096; i++)
@@ -1002,7 +975,7 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
        writel(aaci->maincr, aaci->base + AACI_MAINCR);
 
        /*
-        * If we hit 4096, we failed.  Go back to the specified
+        * If we hit 4096 entries, we failed.  Go back to the specified
         * fifo depth.
         */
        if (i == 4096)
@@ -1011,7 +984,8 @@ static unsigned int __devinit aaci_size_fifo(struct aaci *aaci)
        return i;
 }
 
-static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
+static int __devinit aaci_probe(struct amba_device *dev,
+       const struct amba_id *id)
 {
        struct aaci *aaci;
        int ret, i;
@@ -1067,11 +1041,12 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
 
        /*
         * Size the FIFOs (must be multiple of 16).
+        * This is the number of entries in the FIFO.
         */
-       aaci->fifosize = aaci_size_fifo(aaci);
-       if (aaci->fifosize & 15) {
-               printk(KERN_WARNING "AACI: fifosize = %d not supported\n",
-                      aaci->fifosize);
+       aaci->fifo_depth = aaci_size_fifo(aaci);
+       if (aaci->fifo_depth & 15) {
+               printk(KERN_WARNING "AACI: FIFO depth %d not supported\n",
+                      aaci->fifo_depth);
                ret = -ENODEV;
                goto out;
        }
@@ -1084,8 +1059,8 @@ static int __devinit aaci_probe(struct amba_device *dev, struct amba_id *id)
 
        ret = snd_card_register(aaci->card);
        if (ret == 0) {
-               dev_info(&dev->dev, "%s, fifo %d\n", aaci->card->longname,
-                        aaci->fifosize);
+               dev_info(&dev->dev, "%s\n", aaci->card->longname);
+               dev_info(&dev->dev, "FIFO %u entries\n", aaci->fifo_depth);
                amba_set_drvdata(dev, aaci->card);
                return ret;
        }