]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - sound/pci/oxygen/oxygen_pcm.c
Merge branches 'pxa' and 'orion-fixes1'
[linux-2.6-omap-h63xx.git] / sound / pci / oxygen / oxygen_pcm.c
index 941399bcab8a7b4f8b468bed3ab78cced71a326c..b17c405e069df5349daceba5db00416de012eb1b 100644 (file)
@@ -17,7 +17,6 @@
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  */
 
-#include <sound/driver.h>
 #include <linux/pci.h>
 #include <sound/control.h>
 #include <sound/core.h>
 #include <sound/pcm_params.h>
 #include "oxygen.h"
 
-static struct snd_pcm_hardware oxygen_hardware[PCM_COUNT] = {
-       [PCM_A] = {
-               .info = SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_SYNC_START,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                          SNDRV_PCM_FMTBIT_S32_LE,
-               .rates = SNDRV_PCM_RATE_44100 |
-                        SNDRV_PCM_RATE_48000 |
-                        SNDRV_PCM_RATE_96000 |
-                        SNDRV_PCM_RATE_192000,
-               .rate_min = 44100,
-               .rate_max = 192000,
-               .channels_min = 2,
-               .channels_max = 2,
-               .buffer_bytes_max = 256 * 1024,
-               .period_bytes_min = 128,
-               .period_bytes_max = 128 * 1024,
-               .periods_min = 2,
-               .periods_max = 2048,
-       },
-       [PCM_B] = {
-               .info = SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_SYNC_START,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                          SNDRV_PCM_FMTBIT_S32_LE,
-               .rates = SNDRV_PCM_RATE_32000 |
-                        SNDRV_PCM_RATE_44100 |
-                        SNDRV_PCM_RATE_48000 |
-                        SNDRV_PCM_RATE_64000 |
-                        SNDRV_PCM_RATE_88200 |
-                        SNDRV_PCM_RATE_96000 |
-                        SNDRV_PCM_RATE_176400 |
-                        SNDRV_PCM_RATE_192000,
-               .rate_min = 32000,
-               .rate_max = 192000,
-               .channels_min = 2,
-               .channels_max = 2,
-               .buffer_bytes_max = 256 * 1024,
-               .period_bytes_min = 128,
-               .period_bytes_max = 128 * 1024,
-               .periods_min = 2,
-               .periods_max = 2048,
-       },
-       [PCM_C] = {
-               .info = SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_SYNC_START,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                          SNDRV_PCM_FMTBIT_S32_LE,
-               .rates = SNDRV_PCM_RATE_44100 |
-                        SNDRV_PCM_RATE_48000 |
-                        SNDRV_PCM_RATE_88200 |
-                        SNDRV_PCM_RATE_96000,
-               .rate_min = 44100,
-               .rate_max = 96000,
-               .channels_min = 2,
-               .channels_max = 2,
-               .buffer_bytes_max = 256 * 1024,
-               .period_bytes_min = 128,
-               .period_bytes_max = 128 * 1024,
-               .periods_min = 2,
-               .periods_max = 2048,
-       },
-       [PCM_SPDIF] = {
-               .info = SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_SYNC_START,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                          SNDRV_PCM_FMTBIT_S32_LE,
-               .rates = SNDRV_PCM_RATE_32000 |
-                        SNDRV_PCM_RATE_44100 |
-                        SNDRV_PCM_RATE_48000 |
-                        SNDRV_PCM_RATE_64000 |
-                        SNDRV_PCM_RATE_88200 |
-                        SNDRV_PCM_RATE_96000 |
-                        SNDRV_PCM_RATE_176400 |
-                        SNDRV_PCM_RATE_192000,
-               .rate_min = 32000,
-               .rate_max = 192000,
-               .channels_min = 2,
-               .channels_max = 2,
-               .buffer_bytes_max = 256 * 1024,
-               .period_bytes_min = 128,
-               .period_bytes_max = 128 * 1024,
-               .periods_min = 2,
-               .periods_max = 2048,
-       },
-       [PCM_MULTICH] = {
-               .info = SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_SYNC_START,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                          SNDRV_PCM_FMTBIT_S32_LE,
-               .rates = SNDRV_PCM_RATE_32000 |
-                        SNDRV_PCM_RATE_44100 |
-                        SNDRV_PCM_RATE_48000 |
-                        SNDRV_PCM_RATE_64000 |
-                        SNDRV_PCM_RATE_88200 |
-                        SNDRV_PCM_RATE_96000 |
-                        SNDRV_PCM_RATE_176400 |
-                        SNDRV_PCM_RATE_192000,
-               .rate_min = 32000,
-               .rate_max = 192000,
-               .channels_min = 2,
-               .channels_max = 8,
-               .buffer_bytes_max = 2048 * 1024,
-               .period_bytes_min = 128,
-               .period_bytes_max = 256 * 1024,
-               .periods_min = 2,
-               .periods_max = 16384,
-       },
-       [PCM_AC97] = {
-               .info = SNDRV_PCM_INFO_MMAP |
-                       SNDRV_PCM_INFO_MMAP_VALID |
-                       SNDRV_PCM_INFO_INTERLEAVED |
-                       SNDRV_PCM_INFO_PAUSE |
-                       SNDRV_PCM_INFO_SYNC_START,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-               .rates = SNDRV_PCM_RATE_48000,
-               .rate_min = 48000,
-               .rate_max = 48000,
-               .channels_min = 2,
-               .channels_max = 2,
-               .buffer_bytes_max = 256 * 1024,
-               .period_bytes_min = 128,
-               .period_bytes_max = 128 * 1024,
-               .periods_min = 2,
-               .periods_max = 2048,
-       },
+static const struct snd_pcm_hardware oxygen_stereo_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                  SNDRV_PCM_FMTBIT_S32_LE,
+       .rates = SNDRV_PCM_RATE_32000 |
+                SNDRV_PCM_RATE_44100 |
+                SNDRV_PCM_RATE_48000 |
+                SNDRV_PCM_RATE_64000 |
+                SNDRV_PCM_RATE_88200 |
+                SNDRV_PCM_RATE_96000 |
+                SNDRV_PCM_RATE_176400 |
+                SNDRV_PCM_RATE_192000,
+       .rate_min = 32000,
+       .rate_max = 192000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 256 * 1024,
+       .period_bytes_min = 128,
+       .period_bytes_max = 128 * 1024,
+       .periods_min = 2,
+       .periods_max = 2048,
+};
+static const struct snd_pcm_hardware oxygen_multichannel_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE |
+                  SNDRV_PCM_FMTBIT_S32_LE,
+       .rates = SNDRV_PCM_RATE_32000 |
+                SNDRV_PCM_RATE_44100 |
+                SNDRV_PCM_RATE_48000 |
+                SNDRV_PCM_RATE_64000 |
+                SNDRV_PCM_RATE_88200 |
+                SNDRV_PCM_RATE_96000 |
+                SNDRV_PCM_RATE_176400 |
+                SNDRV_PCM_RATE_192000,
+       .rate_min = 32000,
+       .rate_max = 192000,
+       .channels_min = 2,
+       .channels_max = 8,
+       .buffer_bytes_max = 2048 * 1024,
+       .period_bytes_min = 128,
+       .period_bytes_max = 256 * 1024,
+       .periods_min = 2,
+       .periods_max = 16384,
+};
+static const struct snd_pcm_hardware oxygen_ac97_hardware = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .rates = SNDRV_PCM_RATE_48000,
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+       .buffer_bytes_max = 256 * 1024,
+       .period_bytes_min = 128,
+       .period_bytes_max = 128 * 1024,
+       .periods_min = 2,
+       .periods_max = 2048,
+};
+
+static const struct snd_pcm_hardware *const oxygen_hardware[PCM_COUNT] = {
+       [PCM_A] = &oxygen_stereo_hardware,
+       [PCM_B] = &oxygen_stereo_hardware,
+       [PCM_C] = &oxygen_stereo_hardware,
+       [PCM_SPDIF] = &oxygen_stereo_hardware,
+       [PCM_MULTICH] = &oxygen_multichannel_hardware,
+       [PCM_AC97] = &oxygen_ac97_hardware,
 };
 
+static inline unsigned int
+oxygen_substream_channel(struct snd_pcm_substream *substream)
+{
+       return (unsigned int)(uintptr_t)substream->runtime->private_data;
+}
+
 static int oxygen_open(struct snd_pcm_substream *substream,
                       unsigned int channel)
 {
@@ -175,8 +117,24 @@ static int oxygen_open(struct snd_pcm_substream *substream,
        struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
-       runtime->private_data = (void *)channel;
-       runtime->hw = oxygen_hardware[channel];
+       runtime->private_data = (void *)(uintptr_t)channel;
+       if (channel == PCM_B && chip->has_ac97_1 &&
+           (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1))
+               runtime->hw = oxygen_ac97_hardware;
+       else
+               runtime->hw = *oxygen_hardware[channel];
+       switch (channel) {
+       case PCM_C:
+               runtime->hw.rates &= ~(SNDRV_PCM_RATE_32000 |
+                                      SNDRV_PCM_RATE_64000);
+               runtime->hw.rate_min = 44100;
+               break;
+       case PCM_MULTICH:
+               runtime->hw.channels_max = chip->model->dac_channels;
+               break;
+       }
+       if (chip->model->pcm_hardware_filter)
+               chip->model->pcm_hardware_filter(channel, &runtime->hw);
        err = snd_pcm_hw_constraint_step(runtime, 0,
                                         SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
        if (err < 0)
@@ -204,11 +162,11 @@ static int oxygen_open(struct snd_pcm_substream *substream,
        chip->pcm_active |= 1 << channel;
        if (channel == PCM_SPDIF) {
                chip->spdif_pcm_bits = chip->spdif_bits;
-               chip->spdif_pcm_ctl->vd[0].access &=
+               chip->controls[CONTROL_SPDIF_PCM]->vd[0].access &=
                        ~SNDRV_CTL_ELEM_ACCESS_INACTIVE;
                snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
                               SNDRV_CTL_EVENT_MASK_INFO,
-                              &chip->spdif_pcm_ctl->id);
+                              &chip->controls[CONTROL_SPDIF_PCM]->id);
        }
        mutex_unlock(&chip->mutex);
 
@@ -248,16 +206,16 @@ static int oxygen_ac97_open(struct snd_pcm_substream *substream)
 static int oxygen_close(struct snd_pcm_substream *substream)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
-       unsigned int channel = (unsigned int)substream->runtime->private_data;
+       unsigned int channel = oxygen_substream_channel(substream);
 
        mutex_lock(&chip->mutex);
        chip->pcm_active &= ~(1 << channel);
        if (channel == PCM_SPDIF) {
-               chip->spdif_pcm_ctl->vd[0].access |=
+               chip->controls[CONTROL_SPDIF_PCM]->vd[0].access |=
                        SNDRV_CTL_ELEM_ACCESS_INACTIVE;
                snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_VALUE |
                               SNDRV_CTL_EVENT_MASK_INFO,
-                              &chip->spdif_pcm_ctl->id);
+                              &chip->controls[CONTROL_SPDIF_PCM]->id);
        }
        if (channel == PCM_SPDIF || channel == PCM_MULTICH)
                oxygen_update_spdif_source(chip);
@@ -297,17 +255,20 @@ static unsigned int oxygen_rate(struct snd_pcm_hw_params *hw_params)
        }
 }
 
-static unsigned int oxygen_i2s_magic2(struct snd_pcm_hw_params *hw_params)
+static unsigned int oxygen_i2s_mclk(struct snd_pcm_hw_params *hw_params)
 {
-       return params_rate(hw_params) <= 96000 ? 0x10 : 0x00;
+       if (params_rate(hw_params) <= 96000)
+               return OXYGEN_I2S_MCLK_256;
+       else
+               return OXYGEN_I2S_MCLK_128;
 }
 
-static unsigned int oxygen_i2s_format(struct snd_pcm_hw_params *hw_params)
+static unsigned int oxygen_i2s_bits(struct snd_pcm_hw_params *hw_params)
 {
        if (params_format(hw_params) == SNDRV_PCM_FORMAT_S32_LE)
-               return OXYGEN_I2S_FORMAT_24;
+               return OXYGEN_I2S_BITS_24;
        else
-               return OXYGEN_I2S_FORMAT_16;
+               return OXYGEN_I2S_BITS_16;
 }
 
 static unsigned int oxygen_play_channels(struct snd_pcm_hw_params *hw_params)
@@ -337,7 +298,7 @@ static int oxygen_hw_params(struct snd_pcm_substream *substream,
                            struct snd_pcm_hw_params *hw_params)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
-       unsigned int channel = (unsigned int)substream->runtime->private_data;
+       unsigned int channel = oxygen_substream_channel(substream);
        int err;
 
        err = snd_pcm_lib_malloc_pages(substream,
@@ -375,14 +336,15 @@ static int oxygen_rec_a_hw_params(struct snd_pcm_substream *substream,
        oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
                             oxygen_format(hw_params) << OXYGEN_REC_FORMAT_A_SHIFT,
                             OXYGEN_REC_FORMAT_A_MASK);
-       oxygen_write8_masked(chip, OXYGEN_I2S_A_FORMAT,
-                            oxygen_rate(hw_params) |
-                            oxygen_i2s_magic2(hw_params) |
-                            oxygen_i2s_format(hw_params),
-                            OXYGEN_I2S_RATE_MASK |
-                            OXYGEN_I2S_MAGIC2_MASK |
-                            OXYGEN_I2S_FORMAT_MASK);
-       oxygen_clear_bits8(chip, OXYGEN_REC_ROUTING, 0x08);
+       oxygen_write16_masked(chip, OXYGEN_I2S_A_FORMAT,
+                             oxygen_rate(hw_params) |
+                             oxygen_i2s_mclk(hw_params) |
+                             chip->model->adc_i2s_format |
+                             oxygen_i2s_bits(hw_params),
+                             OXYGEN_I2S_RATE_MASK |
+                             OXYGEN_I2S_FORMAT_MASK |
+                             OXYGEN_I2S_MCLK_MASK |
+                             OXYGEN_I2S_BITS_MASK);
        spin_unlock_irq(&chip->reg_lock);
 
        mutex_lock(&chip->mutex);
@@ -395,29 +357,37 @@ static int oxygen_rec_b_hw_params(struct snd_pcm_substream *substream,
                                  struct snd_pcm_hw_params *hw_params)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
+       int is_ac97;
        int err;
 
        err = oxygen_hw_params(substream, hw_params);
        if (err < 0)
                return err;
 
+       is_ac97 = chip->has_ac97_1 &&
+               (chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+
        spin_lock_irq(&chip->reg_lock);
        oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
                             oxygen_format(hw_params) << OXYGEN_REC_FORMAT_B_SHIFT,
                             OXYGEN_REC_FORMAT_B_MASK);
-       oxygen_write8_masked(chip, OXYGEN_I2S_B_FORMAT,
-                            oxygen_rate(hw_params) |
-                            oxygen_i2s_magic2(hw_params) |
-                            oxygen_i2s_format(hw_params),
-                            OXYGEN_I2S_RATE_MASK |
-                            OXYGEN_I2S_MAGIC2_MASK |
-                            OXYGEN_I2S_FORMAT_MASK);
-       oxygen_clear_bits8(chip, OXYGEN_REC_ROUTING, 0x10);
+       if (!is_ac97)
+               oxygen_write16_masked(chip, OXYGEN_I2S_B_FORMAT,
+                                     oxygen_rate(hw_params) |
+                                     oxygen_i2s_mclk(hw_params) |
+                                     chip->model->adc_i2s_format |
+                                     oxygen_i2s_bits(hw_params),
+                                     OXYGEN_I2S_RATE_MASK |
+                                     OXYGEN_I2S_FORMAT_MASK |
+                                     OXYGEN_I2S_MCLK_MASK |
+                                     OXYGEN_I2S_BITS_MASK);
        spin_unlock_irq(&chip->reg_lock);
 
-       mutex_lock(&chip->mutex);
-       chip->model->set_adc_params(chip, hw_params);
-       mutex_unlock(&chip->mutex);
+       if (!is_ac97) {
+               mutex_lock(&chip->mutex);
+               chip->model->set_adc_params(chip, hw_params);
+               mutex_unlock(&chip->mutex);
+       }
        return 0;
 }
 
@@ -435,7 +405,6 @@ static int oxygen_rec_c_hw_params(struct snd_pcm_substream *substream,
        oxygen_write8_masked(chip, OXYGEN_REC_FORMAT,
                             oxygen_format(hw_params) << OXYGEN_REC_FORMAT_C_SHIFT,
                             OXYGEN_REC_FORMAT_C_MASK);
-       oxygen_clear_bits8(chip, OXYGEN_REC_ROUTING, 0x20);
        spin_unlock_irq(&chip->reg_lock);
        return 0;
 }
@@ -482,9 +451,12 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
                             oxygen_format(hw_params) << OXYGEN_MULTICH_FORMAT_SHIFT,
                             OXYGEN_MULTICH_FORMAT_MASK);
        oxygen_write16_masked(chip, OXYGEN_I2S_MULTICH_FORMAT,
-                             oxygen_rate(hw_params) | oxygen_i2s_format(hw_params),
-                             OXYGEN_I2S_RATE_MASK | OXYGEN_I2S_FORMAT_MASK);
-       oxygen_clear_bits16(chip, OXYGEN_PLAY_ROUTING, 0x001f);
+                             oxygen_rate(hw_params) |
+                             chip->model->dac_i2s_format |
+                             oxygen_i2s_bits(hw_params),
+                             OXYGEN_I2S_RATE_MASK |
+                             OXYGEN_I2S_FORMAT_MASK |
+                             OXYGEN_I2S_BITS_MASK);
        oxygen_update_dac_routing(chip);
        oxygen_update_spdif_source(chip);
        spin_unlock_irq(&chip->reg_lock);
@@ -495,28 +467,10 @@ static int oxygen_multich_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int oxygen_ac97_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *hw_params)
-{
-       struct oxygen *chip = snd_pcm_substream_chip(substream);
-       int err;
-
-       err = oxygen_hw_params(substream, hw_params);
-       if (err < 0)
-               return err;
-
-       spin_lock_irq(&chip->reg_lock);
-       oxygen_write8_masked(chip, OXYGEN_PLAY_FORMAT,
-                            oxygen_format(hw_params) << OXYGEN_AC97_FORMAT_SHIFT,
-                            OXYGEN_AC97_FORMAT_MASK);
-       spin_unlock_irq(&chip->reg_lock);
-       return 0;
-}
-
 static int oxygen_hw_free(struct snd_pcm_substream *substream)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
-       unsigned int channel = (unsigned int)substream->runtime->private_data;
+       unsigned int channel = oxygen_substream_channel(substream);
 
        spin_lock_irq(&chip->reg_lock);
        chip->interrupt_mask &= ~(1 << channel);
@@ -540,7 +494,7 @@ static int oxygen_spdif_hw_free(struct snd_pcm_substream *substream)
 static int oxygen_prepare(struct snd_pcm_substream *substream)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
-       unsigned int channel = (unsigned int)substream->runtime->private_data;
+       unsigned int channel = oxygen_substream_channel(substream);
        unsigned int channel_mask = 1 << channel;
 
        spin_lock_irq(&chip->reg_lock);
@@ -558,16 +512,16 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
        struct oxygen *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_substream *s;
        unsigned int mask = 0;
-       int running;
+       int pausing;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               running = 0;
-               break;
        case SNDRV_PCM_TRIGGER_START:
+               pausing = 0;
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               running = 1;
+               pausing = 1;
                break;
        default:
                return -EINVAL;
@@ -575,17 +529,24 @@ static int oxygen_trigger(struct snd_pcm_substream *substream, int cmd)
 
        snd_pcm_group_for_each_entry(s, substream) {
                if (snd_pcm_substream_chip(s) == chip) {
-                       mask |= 1 << (unsigned int)s->runtime->private_data;
+                       mask |= 1 << oxygen_substream_channel(s);
                        snd_pcm_trigger_done(s, substream);
                }
        }
 
        spin_lock(&chip->reg_lock);
-       if (running)
-               chip->pcm_running |= mask;
-       else
-               chip->pcm_running &= ~mask;
-       oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
+       if (!pausing) {
+               if (cmd == SNDRV_PCM_TRIGGER_START)
+                       chip->pcm_running |= mask;
+               else
+                       chip->pcm_running &= ~mask;
+               oxygen_write8(chip, OXYGEN_DMA_STATUS, chip->pcm_running);
+       } else {
+               if (cmd == SNDRV_PCM_TRIGGER_PAUSE_PUSH)
+                       oxygen_set_bits8(chip, OXYGEN_DMA_PAUSE, mask);
+               else
+                       oxygen_clear_bits8(chip, OXYGEN_DMA_PAUSE, mask);
+       }
        spin_unlock(&chip->reg_lock);
        return 0;
 }
@@ -594,7 +555,7 @@ static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
 {
        struct oxygen *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
-       unsigned int channel = (unsigned int)runtime->private_data;
+       unsigned int channel = oxygen_substream_channel(substream);
        u32 curr_addr;
 
        /* no spinlock, this read should be atomic */
@@ -661,7 +622,7 @@ static struct snd_pcm_ops oxygen_ac97_ops = {
        .open      = oxygen_ac97_open,
        .close     = oxygen_close,
        .ioctl     = snd_pcm_lib_ioctl,
-       .hw_params = oxygen_ac97_hw_params,
+       .hw_params = oxygen_hw_params,
        .hw_free   = oxygen_hw_free,
        .prepare   = oxygen_prepare,
        .trigger   = oxygen_trigger,
@@ -673,51 +634,88 @@ static void oxygen_pcm_free(struct snd_pcm *pcm)
        snd_pcm_lib_preallocate_free_for_all(pcm);
 }
 
-int __devinit oxygen_pcm_init(struct oxygen *chip)
+int oxygen_pcm_init(struct oxygen *chip)
 {
        struct snd_pcm *pcm;
+       int outs, ins;
        int err;
 
-       err = snd_pcm_new(chip->card, "Analog", 0, 1, 1, &pcm);
-       if (err < 0)
-               return err;
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_multich_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
-                       chip->model->record_from_dma_b ?
-                       &oxygen_rec_b_ops : &oxygen_rec_a_ops);
-       pcm->private_data = chip;
-       pcm->private_free = oxygen_pcm_free;
-       strcpy(pcm->name, "Analog");
-       snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
-                                     SNDRV_DMA_TYPE_DEV,
-                                     snd_dma_pci_data(chip->pci),
-                                     512 * 1024, 2048 * 1024);
-       snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
-                                     SNDRV_DMA_TYPE_DEV,
-                                     snd_dma_pci_data(chip->pci),
-                                     128 * 1024, 256 * 1024);
-
-       err = snd_pcm_new(chip->card, "Digital", 1, 1, 1, &pcm);
-       if (err < 0)
-               return err;
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &oxygen_spdif_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &oxygen_rec_c_ops);
-       pcm->private_data = chip;
-       pcm->private_free = oxygen_pcm_free;
-       strcpy(pcm->name, "Digital");
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-                                             snd_dma_pci_data(chip->pci),
-                                             128 * 1024, 256 * 1024);
-
-       if (chip->has_2nd_ac97_codec) {
-               err = snd_pcm_new(chip->card, "AC97", 2, 1, 0, &pcm);
+       outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_0_TO_I2S);
+       ins = !!(chip->model->pcm_dev_cfg & (CAPTURE_0_FROM_I2S_1 |
+                                            CAPTURE_0_FROM_I2S_2));
+       if (outs | ins) {
+               err = snd_pcm_new(chip->card, "Analog", 0, outs, ins, &pcm);
                if (err < 0)
                        return err;
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-                               &oxygen_ac97_ops);
+               if (outs)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &oxygen_multich_ops);
+               if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_1)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &oxygen_rec_a_ops);
+               else if (chip->model->pcm_dev_cfg & CAPTURE_0_FROM_I2S_2)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &oxygen_rec_b_ops);
+               pcm->private_data = chip;
+               pcm->private_free = oxygen_pcm_free;
+               strcpy(pcm->name, "Analog");
+               if (outs)
+                       snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream,
+                                                     SNDRV_DMA_TYPE_DEV,
+                                                     snd_dma_pci_data(chip->pci),
+                                                     512 * 1024, 2048 * 1024);
+               if (ins)
+                       snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream,
+                                                     SNDRV_DMA_TYPE_DEV,
+                                                     snd_dma_pci_data(chip->pci),
+                                                     128 * 1024, 256 * 1024);
+       }
+
+       outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_1_TO_SPDIF);
+       ins = !!(chip->model->pcm_dev_cfg & CAPTURE_1_FROM_SPDIF);
+       if (outs | ins) {
+               err = snd_pcm_new(chip->card, "Digital", 1, outs, ins, &pcm);
+               if (err < 0)
+                       return err;
+               if (outs)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &oxygen_spdif_ops);
+               if (ins)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &oxygen_rec_c_ops);
+               pcm->private_data = chip;
+               pcm->private_free = oxygen_pcm_free;
+               strcpy(pcm->name, "Digital");
+               snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+                                                     snd_dma_pci_data(chip->pci),
+                                                     128 * 1024, 256 * 1024);
+       }
+
+       if (chip->has_ac97_1) {
+               outs = !!(chip->model->pcm_dev_cfg & PLAYBACK_2_TO_AC97_1);
+               ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_AC97_1);
+       } else {
+               outs = 0;
+               ins = !!(chip->model->pcm_dev_cfg & CAPTURE_2_FROM_I2S_2);
+       }
+       if (outs | ins) {
+               err = snd_pcm_new(chip->card, outs ? "AC97" : "Analog2",
+                                 2, outs, ins, &pcm);
+               if (err < 0)
+                       return err;
+               if (outs) {
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &oxygen_ac97_ops);
+                       oxygen_write8_masked(chip, OXYGEN_REC_ROUTING,
+                                            OXYGEN_REC_B_ROUTE_AC97_1,
+                                            OXYGEN_REC_B_ROUTE_MASK);
+               }
+               if (ins)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &oxygen_rec_b_ops);
                pcm->private_data = chip;
                pcm->private_free = oxygen_pcm_free;
-               strcpy(pcm->name, "Front Panel");
+               strcpy(pcm->name, outs ? "Front Panel" : "Analog 2");
                snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                                      snd_dma_pci_data(chip->pci),
                                                      128 * 1024, 256 * 1024);