sound: use DEFINE_PCI_DEVICE_TABLE
[safe/jmp/linux-2.6] / sound / pci / pcxhr / pcxhr.c
index 57fe953..95cfde2 100644 (file)
  */
 
 
-#include <sound/driver.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
 #include <linux/pci.h>
+#include <linux/dma-mapping.h>
 #include <linux/delay.h>
 #include <linux/moduleparam.h>
+#include <linux/mutex.h>
+
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/info.h>
 #include "pcxhr_mixer.h"
 #include "pcxhr_hwdep.h"
 #include "pcxhr_core.h"
+#include "pcxhr_mix22.h"
 
 #define DRIVER_NAME "pcxhr"
 
-MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>");
+MODULE_AUTHOR("Markus Bollinger <bollinger@digigram.com>, "
+             "Marc Titinger <titinger@digigram.com>");
 MODULE_DESCRIPTION("Digigram " DRIVER_NAME " " PCXHR_DRIVER_VERSION_STRING);
 MODULE_LICENSE("GPL");
 MODULE_SUPPORTED_DEVICE("{{Digigram," DRIVER_NAME "}}");
 
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;             /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;              /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;     /* Enable this card */
-static int mono[SNDRV_CARDS];                                  /* capture in mono only */
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card */
+static int mono[SNDRV_CARDS];                          /* capture  mono only */
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Digigram " DRIVER_NAME " soundcard");
@@ -65,18 +69,58 @@ enum {
        PCI_ID_PCX882HR,
        PCI_ID_VX881HR,
        PCI_ID_PCX881HR,
+       PCI_ID_VX882E,
+       PCI_ID_PCX882E,
+       PCI_ID_VX881E,
+       PCI_ID_PCX881E,
+       PCI_ID_VX1222HR,
        PCI_ID_PCX1222HR,
+       PCI_ID_VX1221HR,
        PCI_ID_PCX1221HR,
+       PCI_ID_VX1222E,
+       PCI_ID_PCX1222E,
+       PCI_ID_VX1221E,
+       PCI_ID_PCX1221E,
+       PCI_ID_VX222HR,
+       PCI_ID_VX222E,
+       PCI_ID_PCX22HR,
+       PCI_ID_PCX22E,
+       PCI_ID_VX222HRMIC,
+       PCI_ID_VX222E_MIC,
+       PCI_ID_PCX924HR,
+       PCI_ID_PCX924E,
+       PCI_ID_PCX924HRMIC,
+       PCI_ID_PCX924E_MIC,
        PCI_ID_LAST
 };
 
-static struct pci_device_id pcxhr_ids[] = {
-       { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },   /* VX882HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },  /* PCX882HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },   /* VX881HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },  /* PCX881HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, }, /* PCX1222HR */
-       { 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, }, /* PCX1221HR */
+static DEFINE_PCI_DEVICE_TABLE(pcxhr_ids) = {
+       { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb301, 0, 0, PCI_ID_PCX881HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xb021, 0, 0, PCI_ID_VX882E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb121, 0, 0, PCI_ID_PCX882E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb221, 0, 0, PCI_ID_VX881E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb321, 0, 0, PCI_ID_PCX881E, },
+       { 0x10b5, 0x9656, 0x1369, 0xb401, 0, 0, PCI_ID_VX1222HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb501, 0, 0, PCI_ID_PCX1222HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb601, 0, 0, PCI_ID_VX1221HR, },
+       { 0x10b5, 0x9656, 0x1369, 0xb701, 0, 0, PCI_ID_PCX1221HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xb421, 0, 0, PCI_ID_VX1222E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb521, 0, 0, PCI_ID_PCX1222E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb621, 0, 0, PCI_ID_VX1221E, },
+       { 0x10b5, 0x9056, 0x1369, 0xb721, 0, 0, PCI_ID_PCX1221E, },
+       { 0x10b5, 0x9056, 0x1369, 0xba01, 0, 0, PCI_ID_VX222HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xba21, 0, 0, PCI_ID_VX222E, },
+       { 0x10b5, 0x9056, 0x1369, 0xbd01, 0, 0, PCI_ID_PCX22HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xbd21, 0, 0, PCI_ID_PCX22E, },
+       { 0x10b5, 0x9056, 0x1369, 0xbc01, 0, 0, PCI_ID_VX222HRMIC, },
+       { 0x10b5, 0x9056, 0x1369, 0xbc21, 0, 0, PCI_ID_VX222E_MIC, },
+       { 0x10b5, 0x9056, 0x1369, 0xbb01, 0, 0, PCI_ID_PCX924HR, },
+       { 0x10b5, 0x9056, 0x1369, 0xbb21, 0, 0, PCI_ID_PCX924E, },
+       { 0x10b5, 0x9056, 0x1369, 0xbf01, 0, 0, PCI_ID_PCX924HRMIC, },
+       { 0x10b5, 0x9056, 0x1369, 0xbf21, 0, 0, PCI_ID_PCX924E_MIC, },
        { 0, }
 };
 
@@ -86,27 +130,55 @@ struct board_parameters {
        char* board_name;
        short playback_chips;
        short capture_chips;
+       short fw_file_set;
        short firmware_num;
 };
 static struct board_parameters pcxhr_board_params[] = {
-[PCI_ID_VX882HR] =     { "VX882HR",   4, 4, 41, },
-[PCI_ID_PCX882HR] =    { "PCX882HR",  4, 4, 41, },
-[PCI_ID_VX881HR] =     { "VX881HR",   4, 4, 41, },
-[PCI_ID_PCX881HR] =    { "PCX881HR",  4, 4, 41, },
-[PCI_ID_PCX1222HR] =   { "PCX1222HR", 6, 1, 42, },
-[PCI_ID_PCX1221HR] =   { "PCX1221HR", 6, 1, 42, },
+[PCI_ID_VX882HR] =      { "VX882HR",      4, 4, 0, 41 },
+[PCI_ID_PCX882HR] =     { "PCX882HR",     4, 4, 0, 41 },
+[PCI_ID_VX881HR] =      { "VX881HR",      4, 4, 0, 41 },
+[PCI_ID_PCX881HR] =     { "PCX881HR",     4, 4, 0, 41 },
+[PCI_ID_VX882E] =       { "VX882e",       4, 4, 1, 41 },
+[PCI_ID_PCX882E] =      { "PCX882e",      4, 4, 1, 41 },
+[PCI_ID_VX881E] =       { "VX881e",       4, 4, 1, 41 },
+[PCI_ID_PCX881E] =      { "PCX881e",      4, 4, 1, 41 },
+[PCI_ID_VX1222HR] =     { "VX1222HR",     6, 1, 2, 42 },
+[PCI_ID_PCX1222HR] =    { "PCX1222HR",    6, 1, 2, 42 },
+[PCI_ID_VX1221HR] =     { "VX1221HR",     6, 1, 2, 42 },
+[PCI_ID_PCX1221HR] =    { "PCX1221HR",    6, 1, 2, 42 },
+[PCI_ID_VX1222E] =      { "VX1222e",      6, 1, 3, 42 },
+[PCI_ID_PCX1222E] =     { "PCX1222e",     6, 1, 3, 42 },
+[PCI_ID_VX1221E] =      { "VX1221e",      6, 1, 3, 42 },
+[PCI_ID_PCX1221E] =     { "PCX1221e",     6, 1, 3, 42 },
+[PCI_ID_VX222HR] =      { "VX222HR",      1, 1, 4, 44 },
+[PCI_ID_VX222E] =       { "VX222e",       1, 1, 4, 44 },
+[PCI_ID_PCX22HR] =      { "PCX22HR",      1, 0, 4, 44 },
+[PCI_ID_PCX22E] =       { "PCX22e",       1, 0, 4, 44 },
+[PCI_ID_VX222HRMIC] =   { "VX222HR-Mic",  1, 1, 5, 44 },
+[PCI_ID_VX222E_MIC] =   { "VX222e-Mic",   1, 1, 5, 44 },
+[PCI_ID_PCX924HR] =     { "PCX924HR",     1, 1, 5, 44 },
+[PCI_ID_PCX924E] =      { "PCX924e",      1, 1, 5, 44 },
+[PCI_ID_PCX924HRMIC] =  { "PCX924HR-Mic", 1, 1, 5, 44 },
+[PCI_ID_PCX924E_MIC] =  { "PCX924e-Mic",  1, 1, 5, 44 },
 };
 
+/* boards without hw AES1 and SRC onboard are all using fw_file_set==4 */
+/* VX222HR, VX222e, PCX22HR and PCX22e */
+#define PCXHR_BOARD_HAS_AES1(x) (x->fw_file_set != 4)
+/* some boards do not support 192kHz on digital AES input plugs */
+#define PCXHR_BOARD_AESIN_NO_192K(x) ((x->capture_chips == 0) || \
+                                     (x->fw_file_set == 0)   || \
+                                     (x->fw_file_set == 2))
 
 static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
                                   unsigned int* realfreq)
 {
        unsigned int reg;
 
-       if (freq < 6900 || freq > 110250)
+       if (freq < 6900 || freq > 110000)
                return -EINVAL;
-       reg = (28224000 * 10) / freq;
-       reg = (reg + 5) / 10;
+       reg = (28224000 * 2) / freq;
+       reg = (reg - 1) / 2;
        if (reg < 0x200)
                *pllreg = reg + 0x800;
        else if (reg < 0x400)
@@ -119,7 +191,7 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
                reg &= ~3;
        }
        if (realfreq)
-               *realfreq = ((28224000 * 10) / reg + 5) / 10;
+               *realfreq = (28224000 / (reg + 1));
        return 0;
 }
 
@@ -149,11 +221,6 @@ static int pcxhr_pll_freq_register(unsigned int freq, unsigned int* pllreg,
 #define PCXHR_FREQ_AES_3               0x03
 #define PCXHR_FREQ_AES_4               0x0d
 
-#define PCXHR_MODIFY_CLOCK_S_BIT       0x04
-
-#define PCXHR_IRQ_TIMER_FREQ           92000
-#define PCXHR_IRQ_TIMER_PERIOD         48
-
 static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
                               unsigned int *reg, unsigned int *freq)
 {
@@ -194,19 +261,32 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
                        err = pcxhr_send_msg(mgr, &rmh);
                        if (err < 0) {
                                snd_printk(KERN_ERR
-                                          "error CMD_ACCESS_IO_WRITE for PLL register : %x!\n",
-                                          err );
+                                          "error CMD_ACCESS_IO_WRITE "
+                                          "for PLL register : %x!\n", err);
                                return err;
                        }
                }
                break;
-       case PCXHR_CLOCK_TYPE_WORD_CLOCK :      val = PCXHR_FREQ_WORD_CLOCK;    break;
-       case PCXHR_CLOCK_TYPE_AES_SYNC :        val = PCXHR_FREQ_SYNC_AES;      break;
-       case PCXHR_CLOCK_TYPE_AES_1 :           val = PCXHR_FREQ_AES_1;         break;
-       case PCXHR_CLOCK_TYPE_AES_2 :           val = PCXHR_FREQ_AES_2;         break;
-       case PCXHR_CLOCK_TYPE_AES_3 :           val = PCXHR_FREQ_AES_3;         break;
-       case PCXHR_CLOCK_TYPE_AES_4 :           val = PCXHR_FREQ_AES_4;         break;
-       default : return -EINVAL;
+       case PCXHR_CLOCK_TYPE_WORD_CLOCK:
+               val = PCXHR_FREQ_WORD_CLOCK;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_SYNC:
+               val = PCXHR_FREQ_SYNC_AES;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_1:
+               val = PCXHR_FREQ_AES_1;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_2:
+               val = PCXHR_FREQ_AES_2;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_3:
+               val = PCXHR_FREQ_AES_3;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_4:
+               val = PCXHR_FREQ_AES_4;
+               break;
+       default:
+               return -EINVAL;
        }
        *reg = val;
        *freq = realfreq;
@@ -214,14 +294,13 @@ static int pcxhr_get_clock_reg(struct pcxhr_mgr *mgr, unsigned int rate,
 }
 
 
-int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
+static int pcxhr_sub_set_clock(struct pcxhr_mgr *mgr,
+                              unsigned int rate,
+                              int *changed)
 {
        unsigned int val, realfreq, speed;
        struct pcxhr_rmh rmh;
-       int err, changed;
-
-       if (rate == 0)
-               return 0; /* nothing to do */
+       int err;
 
        err = pcxhr_get_clock_reg(mgr, rate, &val, &realfreq);
        if (err)
@@ -235,13 +314,17 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
        else
                speed = 2;      /* quad speed */
        if (mgr->codec_speed != speed) {
-               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);      /* mute outputs */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* mute outputs */
                rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+               if (DSP_EXT_CMD_SET(mgr)) {
+                       rmh.cmd[1]  = 1;
+                       rmh.cmd_len = 2;
+               }
                err = pcxhr_send_msg(mgr, &rmh);
                if (err)
                        return err;
 
-               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);      /* set speed ratio */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE); /* set speed ratio */
                rmh.cmd[0] |= IO_NUM_SPEED_RATIO;
                rmh.cmd[1] = speed;
                rmh.cmd_len = 2;
@@ -251,25 +334,57 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
        }
        /* set the new frequency */
        snd_printdd("clock register : set %x\n", val);
-       err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK, val, &changed);
+       err = pcxhr_write_io_num_reg_cont(mgr, PCXHR_FREQ_REG_MASK,
+                                         val, changed);
        if (err)
                return err;
+
        mgr->sample_rate_real = realfreq;
        mgr->cur_clock_type = mgr->use_clock_type;
 
        /* unmute after codec speed modes */
        if (mgr->codec_speed != speed) {
-               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);       /* unmute outputs */
+               pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ); /* unmute outputs */
                rmh.cmd[0] |= IO_NUM_REG_MUTE_OUT;
+               if (DSP_EXT_CMD_SET(mgr)) {
+                       rmh.cmd[1]  = 1;
+                       rmh.cmd_len = 2;
+               }
                err = pcxhr_send_msg(mgr, &rmh);
                if (err)
                        return err;
-               mgr->codec_speed = speed;                       /* save new codec speed */
+               mgr->codec_speed = speed;       /* save new codec speed */
        }
 
+       snd_printdd("pcxhr_sub_set_clock to %dHz (realfreq=%d)\n",
+                   rate, realfreq);
+       return 0;
+}
+
+#define PCXHR_MODIFY_CLOCK_S_BIT       0x04
+
+#define PCXHR_IRQ_TIMER_FREQ           92000
+#define PCXHR_IRQ_TIMER_PERIOD         48
+
+int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
+{
+       struct pcxhr_rmh rmh;
+       int err, changed;
+
+       if (rate == 0)
+               return 0; /* nothing to do */
+
+       if (mgr->is_hr_stereo)
+               err = hr222_sub_set_clock(mgr, rate, &changed);
+       else
+               err = pcxhr_sub_set_clock(mgr, rate, &changed);
+
+       if (err)
+               return err;
+
        if (changed) {
                pcxhr_init_rmh(&rmh, CMD_MODIFY_CLOCK);
-               rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT;         /* resync fifos  */
+               rmh.cmd[0] |= PCXHR_MODIFY_CLOCK_S_BIT; /* resync fifos  */
                if (rate < PCXHR_IRQ_TIMER_FREQ)
                        rmh.cmd[1] = PCXHR_IRQ_TIMER_PERIOD;
                else
@@ -280,26 +395,39 @@ int pcxhr_set_clock(struct pcxhr_mgr *mgr, unsigned int rate)
                if (err)
                        return err;
        }
-       snd_printdd("pcxhr_set_clock to %dHz (realfreq=%d)\n", rate, realfreq);
        return 0;
 }
 
 
-int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_type,
-                            int *sample_rate)
+static int pcxhr_sub_get_external_clock(struct pcxhr_mgr *mgr,
+                                       enum pcxhr_clock_type clock_type,
+                                       int *sample_rate)
 {
        struct pcxhr_rmh rmh;
        unsigned char reg;
        int err, rate;
 
        switch (clock_type) {
-       case PCXHR_CLOCK_TYPE_WORD_CLOCK :      reg = REG_STATUS_WORD_CLOCK;    break;
-       case PCXHR_CLOCK_TYPE_AES_SYNC :        reg = REG_STATUS_AES_SYNC;      break;
-       case PCXHR_CLOCK_TYPE_AES_1 :           reg = REG_STATUS_AES_1;         break;
-       case PCXHR_CLOCK_TYPE_AES_2 :           reg = REG_STATUS_AES_2;         break;
-       case PCXHR_CLOCK_TYPE_AES_3 :           reg = REG_STATUS_AES_3;         break;
-       case PCXHR_CLOCK_TYPE_AES_4 :           reg = REG_STATUS_AES_4;         break;
-       default : return -EINVAL;
+       case PCXHR_CLOCK_TYPE_WORD_CLOCK:
+               reg = REG_STATUS_WORD_CLOCK;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_SYNC:
+               reg = REG_STATUS_AES_SYNC;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_1:
+               reg = REG_STATUS_AES_1;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_2:
+               reg = REG_STATUS_AES_2;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_3:
+               reg = REG_STATUS_AES_3;
+               break;
+       case PCXHR_CLOCK_TYPE_AES_4:
+               reg = REG_STATUS_AES_4;
+               break;
+       default:
+               return -EINVAL;
        }
        pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_READ);
        rmh.cmd_len = 2;
@@ -309,7 +437,7 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
                err = pcxhr_send_msg(mgr, &rmh);
                if (err)
                        return err;
-               udelay(100);            /* wait minimum 2 sample_frames at 32kHz ! */
+               udelay(100);    /* wait minimum 2 sample_frames at 32kHz ! */
                mgr->last_reg_stat = reg;
        }
        rmh.cmd[1]  = REG_STATUS_CURRENT;
@@ -334,6 +462,18 @@ int pcxhr_get_external_clock(struct pcxhr_mgr *mgr, enum pcxhr_clock_type clock_
 }
 
 
+int pcxhr_get_external_clock(struct pcxhr_mgr *mgr,
+                            enum pcxhr_clock_type clock_type,
+                            int *sample_rate)
+{
+       if (mgr->is_hr_stereo)
+               return hr222_get_external_clock(mgr, clock_type,
+                                               sample_rate);
+       else
+               return pcxhr_sub_get_external_clock(mgr, clock_type,
+                                                   sample_rate);
+}
+
 /*
  *  start or stop playback/capture substream
  */
@@ -348,7 +488,8 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
                start = 1;
        else {
                if (stream->status != PCXHR_STREAM_STATUS_SCHEDULE_STOP) {
-                       snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state CANNOT be stopped\n");
+                       snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state "
+                                  "CANNOT be stopped\n");
                        return -EINVAL;
                }
                start = 0;
@@ -357,11 +498,12 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
                return -EINVAL;
 
        stream->timer_abs_periods = 0;
-       stream->timer_period_frag = 0;            /* reset theoretical stream pos */
+       stream->timer_period_frag = 0;  /* reset theoretical stream pos */
        stream->timer_buf_periods = 0;
        stream->timer_is_synced = 0;
 
-       stream_mask = stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
+       stream_mask =
+         stream->pipe->is_capture ? 1 : 1<<stream->substream->number;
 
        pcxhr_init_rmh(&rmh, start ? CMD_START_STREAM : CMD_STOP_STREAM);
        pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,
@@ -371,8 +513,10 @@ static int pcxhr_set_stream_state(struct pcxhr_stream *stream)
 
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
-               snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n", err);
-       stream->status = start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
+               snd_printk(KERN_ERR "ERROR pcxhr_set_stream_state err=%x;\n",
+                          err);
+       stream->status =
+         start ? PCXHR_STREAM_STATUS_STARTED : PCXHR_STREAM_STATUS_STOPPED;
        return err;
 }
 
@@ -397,13 +541,15 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
                header = HEADER_FMT_BASE_LIN;
                break;
        case SNDRV_PCM_FORMAT_S16_LE:
-               header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS | HEADER_FMT_INTEL;
+               header = HEADER_FMT_BASE_LIN |
+                        HEADER_FMT_16BITS | HEADER_FMT_INTEL;
                break;
        case SNDRV_PCM_FORMAT_S16_BE:
                header = HEADER_FMT_BASE_LIN | HEADER_FMT_16BITS;
                break;
        case SNDRV_PCM_FORMAT_S24_3LE:
-               header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS | HEADER_FMT_INTEL;
+               header = HEADER_FMT_BASE_LIN |
+                        HEADER_FMT_24BITS | HEADER_FMT_INTEL;
                break;
        case SNDRV_PCM_FORMAT_S24_3BE:
                header = HEADER_FMT_BASE_LIN | HEADER_FMT_24BITS;
@@ -412,7 +558,8 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
                header = HEADER_FMT_BASE_FLOAT | HEADER_FMT_INTEL;
                break;
        default:
-               snd_printk(KERN_ERR "error pcxhr_set_format() : unknown format\n");
+               snd_printk(KERN_ERR
+                          "error pcxhr_set_format() : unknown format\n");
                return -EINVAL;
        }
        chip = snd_pcm_substream_chip(stream->substream);
@@ -430,14 +577,31 @@ static int pcxhr_set_format(struct pcxhr_stream *stream)
        is_capture = stream->pipe->is_capture;
        stream_num = is_capture ? 0 : stream->substream->number;
 
-       pcxhr_init_rmh(&rmh, is_capture ? CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
-       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
-       if (is_capture)
-               rmh.cmd[0] |= 1<<12;
+       pcxhr_init_rmh(&rmh, is_capture ?
+                      CMD_FORMAT_STREAM_IN : CMD_FORMAT_STREAM_OUT);
+       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
+                                 stream_num, 0);
+       if (is_capture) {
+               /* bug with old dsp versions: */
+               /* bit 12 also sets the format of the playback stream */
+               if (DSP_EXT_CMD_SET(chip->mgr))
+                       rmh.cmd[0] |= 1<<10;
+               else
+                       rmh.cmd[0] |= 1<<12;
+       }
        rmh.cmd[1] = 0;
-       rmh.cmd[2] = header >> 8;
-       rmh.cmd[3] = (header & 0xff) << 16;
-       rmh.cmd_len = 4;
+       rmh.cmd_len = 2;
+       if (DSP_EXT_CMD_SET(chip->mgr)) {
+               /* add channels and set bit 19 if channels>2 */
+               rmh.cmd[1] = stream->channels;
+               if (!is_capture) {
+                       /* playback : add channel mask to command */
+                       rmh.cmd[2] = (stream->channels == 1) ? 0x01 : 0x03;
+                       rmh.cmd_len = 3;
+               }
+       }
+       rmh.cmd[rmh.cmd_len++] = header >> 8;
+       rmh.cmd[rmh.cmd_len++] = (header & 0xff) << 16;
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
                snd_printk(KERN_ERR "ERROR pcxhr_set_format err=%x;\n", err);
@@ -454,29 +618,38 @@ static int pcxhr_update_r_buffer(struct pcxhr_stream *stream)
        is_capture = (subs->stream == SNDRV_PCM_STREAM_CAPTURE);
        stream_num = is_capture ? 0 : subs->number;
 
-       snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : addr(%p) bytes(%x) subs(%d)\n",
+       snd_printdd("pcxhr_update_r_buffer(pcm%c%d) : "
+                   "addr(%p) bytes(%zx) subs(%d)\n",
                    is_capture ? 'c' : 'p',
-                   chip->chip_idx, (void*)subs->runtime->dma_addr,
+                   chip->chip_idx, (void *)(long)subs->runtime->dma_addr,
                    subs->runtime->dma_bytes, subs->number);
 
        pcxhr_init_rmh(&rmh, CMD_UPDATE_R_BUFFERS);
-       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio, stream_num, 0);
-
-       snd_assert(subs->runtime->dma_bytes < 0x200000);        /* max buffer size is 2 MByte */
-       rmh.cmd[1] = subs->runtime->dma_bytes * 8;              /* size in bits */
-       rmh.cmd[2] = subs->runtime->dma_addr >> 24;             /* most significant byte */
-       rmh.cmd[2] |= 1<<19;                                    /* this is a circular buffer */
-       rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;   /* least 3 significant bytes */
+       pcxhr_set_pipe_cmd_params(&rmh, is_capture, stream->pipe->first_audio,
+                                 stream_num, 0);
+
+       /* max buffer size is 2 MByte */
+       snd_BUG_ON(subs->runtime->dma_bytes >= 0x200000);
+       /* size in bits */
+       rmh.cmd[1] = subs->runtime->dma_bytes * 8;
+       /* most significant byte */
+       rmh.cmd[2] = subs->runtime->dma_addr >> 24;
+       /* this is a circular buffer */
+       rmh.cmd[2] |= 1<<19;
+       /* least 3 significant bytes */
+       rmh.cmd[3] = subs->runtime->dma_addr & MASK_DSP_WORD;
        rmh.cmd_len = 4;
        err = pcxhr_send_msg(chip->mgr, &rmh);
        if (err)
-               snd_printk(KERN_ERR "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
+               snd_printk(KERN_ERR
+                          "ERROR CMD_UPDATE_R_BUFFERS err=%x;\n", err);
        return err;
 }
 
 
 #if 0
-static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream, snd_pcm_uframes_t *sample_count)
+static int pcxhr_pipe_sample_count(struct pcxhr_stream *stream,
+                                  snd_pcm_uframes_t *sample_count)
 {
        struct pcxhr_rmh rmh;
        int err;
@@ -514,11 +687,11 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        int capture_mask = 0;
        int playback_mask = 0;
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
        struct timeval my_tv1, my_tv2;
        do_gettimeofday(&my_tv1);
 #endif
-       down(&mgr->setup_mutex);
+       mutex_lock(&mgr->setup_mutex);
 
        /* check the pipes concerned and build pipe_array */
        for (i = 0; i < mgr->num_cards; i++) {
@@ -530,31 +703,33 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
                for (j = 0; j < chip->nb_streams_play; j++) {
                        if (pcxhr_stream_scheduled_get_pipe(&chip->playback_stream[j], &pipe)) {
                                playback_mask |= (1 << pipe->first_audio);
-                               break;  /* add only once, as all playback streams of
-                                        * one chip use the same pipe
+                               break;  /* add only once, as all playback
+                                        * streams of one chip use the same pipe
                                         */
                        }
                }
        }
        if (capture_mask == 0 && playback_mask == 0) {
-               up(&mgr->setup_mutex);
+               mutex_unlock(&mgr->setup_mutex);
                snd_printk(KERN_ERR "pcxhr_trigger_tasklet : no pipes\n");
                return;
        }
 
-       snd_printdd("pcxhr_trigger_tasklet : playback_mask=%x capture_mask=%x\n",
+       snd_printdd("pcxhr_trigger_tasklet : "
+                   "playback_mask=%x capture_mask=%x\n",
                    playback_mask, capture_mask);
 
        /* synchronous stop of all the pipes concerned */
        err = pcxhr_set_pipe_state(mgr,  playback_mask, capture_mask, 0);
        if (err) {
-               up(&mgr->setup_mutex);
-               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error stop pipes (P%x C%x)\n",
+               mutex_unlock(&mgr->setup_mutex);
+               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+                          "error stop pipes (P%x C%x)\n",
                           playback_mask, capture_mask);
                return;
        }
 
-       /* unfortunately the dsp lost format and buffer info with the stop pipe */
+       /* the dsp lost format and buffer info with the stop pipe */
        for (i = 0; i < mgr->num_cards; i++) {
                struct pcxhr_stream *stream;
                chip = mgr->chip[i];
@@ -592,13 +767,16 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
        /* synchronous start of all the pipes concerned */
        err = pcxhr_set_pipe_state(mgr, playback_mask, capture_mask, 1);
        if (err) {
-               up(&mgr->setup_mutex);
-               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : error start pipes (P%x C%x)\n",
+               mutex_unlock(&mgr->setup_mutex);
+               snd_printk(KERN_ERR "pcxhr_trigger_tasklet : "
+                          "error start pipes (P%x C%x)\n",
                           playback_mask, capture_mask);
                return;
        }
 
-       /* put the streams into the running state now (increment pointer by interrupt) */
+       /* put the streams into the running state now
+        * (increment pointer by interrupt)
+        */
        spin_lock_irqsave(&mgr->lock, flags);
        for ( i =0; i < mgr->num_cards; i++) {
                struct pcxhr_stream *stream;
@@ -612,19 +790,19 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
                        stream = &chip->playback_stream[j];
                        if (stream->status == PCXHR_STREAM_STATUS_STARTED) {
                                /* playback will already have advanced ! */
-                               stream->timer_period_frag += PCXHR_GRANULARITY;
+                               stream->timer_period_frag += mgr->granularity;
                                stream->status = PCXHR_STREAM_STATUS_RUNNING;
                        }
                }
        }
        spin_unlock_irqrestore(&mgr->lock, flags);
 
-       up(&mgr->setup_mutex);
+       mutex_unlock(&mgr->setup_mutex);
 
-#ifdef CONFIG_SND_DEBUG_DETECT
+#ifdef CONFIG_SND_DEBUG_VERBOSE
        do_gettimeofday(&my_tv2);
        snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n",
-                   my_tv2.tv_usec - my_tv1.tv_usec, err);
+                   (long)(my_tv2.tv_usec - my_tv1.tv_usec), err);
 #endif
 }
 
@@ -635,22 +813,24 @@ static void pcxhr_trigger_tasklet(unsigned long arg)
 static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
 {
        struct pcxhr_stream *stream;
-       struct list_head *pos;
        struct snd_pcm_substream *s;
-       int i;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                snd_printdd("SNDRV_PCM_TRIGGER_START\n");
-               i = 0;
-               snd_pcm_group_for_each(pos, subs) {
-                       s = snd_pcm_group_substream_entry(pos);
-                       stream = s->runtime->private_data;
-                       stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN;
-                       snd_pcm_trigger_done(s, subs);
-                       i++;
-               }
-               if (i==1) {
+               if (snd_pcm_stream_linked(subs)) {
+                       struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
+                       snd_pcm_group_for_each_entry(s, subs) {
+                               if (snd_pcm_substream_chip(s) != chip)
+                                       continue;
+                               stream = s->runtime->private_data;
+                               stream->status =
+                                       PCXHR_STREAM_STATUS_SCHEDULE_RUN;
+                               snd_pcm_trigger_done(s, subs);
+                       }
+                       tasklet_schedule(&chip->mgr->trigger_taskq);
+               } else {
+                       stream = subs->runtime->private_data;
                        snd_printdd("Only one Substream %c %d\n",
                                    stream->pipe->is_capture ? 'C' : 'P',
                                    stream->pipe->first_audio);
@@ -659,18 +839,15 @@ static int pcxhr_trigger(struct snd_pcm_substream *subs, int cmd)
                        if (pcxhr_update_r_buffer(stream))
                                return -EINVAL;
 
+                       stream->status = PCXHR_STREAM_STATUS_SCHEDULE_RUN;
                        if (pcxhr_set_stream_state(stream))
                                return -EINVAL;
                        stream->status = PCXHR_STREAM_STATUS_RUNNING;
-               } else {
-                       struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
-                       tasklet_hi_schedule(&chip->mgr->trigger_taskq);
                }
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                snd_printdd("SNDRV_PCM_TRIGGER_STOP\n");
-               snd_pcm_group_for_each(pos, subs) {
-                       s = snd_pcm_group_substream_entry(pos);
+               snd_pcm_group_for_each_entry(s, subs) {
                        stream = s->runtime->private_data;
                        stream->status = PCXHR_STREAM_STATUS_SCHEDULE_STOP;
                        if (pcxhr_set_stream_state(stream))
@@ -695,12 +872,14 @@ static int pcxhr_hardware_timer(struct pcxhr_mgr *mgr, int start)
 
        pcxhr_init_rmh(&rmh, CMD_SET_TIMER_INTERRUPT);
        if (start) {
-               mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;    /* last dsp time invalid */
-               rmh.cmd[0] |= PCXHR_GRANULARITY;
+               /* last dsp time invalid */
+               mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;
+               rmh.cmd[0] |= mgr->granularity;
        }
        err = pcxhr_send_msg(mgr, &rmh);
        if (err < 0)
-               snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n", err);
+               snd_printk(KERN_ERR "error pcxhr_hardware_timer err(%x)\n",
+                          err);
        return err;
 }
 
@@ -711,38 +890,16 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
 {
        struct snd_pcxhr *chip = snd_pcm_substream_chip(subs);
        struct pcxhr_mgr *mgr = chip->mgr;
-       /*
-       struct pcxhr_stream *stream = (pcxhr_stream_t*)subs->runtime->private_data;
-       */
        int err = 0;
 
        snd_printdd("pcxhr_prepare : period_size(%lx) periods(%x) buffer_size(%lx)\n",
                    subs->runtime->period_size, subs->runtime->periods,
                    subs->runtime->buffer_size);
 
-       /*
-       if(subs->runtime->period_size <= PCXHR_GRANULARITY) {
-               snd_printk(KERN_ERR "pcxhr_prepare : error period_size too small (%x)\n",
-                          (unsigned int)subs->runtime->period_size);
-               return -EINVAL;
-       }
-       */
-
-       down(&mgr->setup_mutex);
+       mutex_lock(&mgr->setup_mutex);
 
        do {
-               /* if the stream was stopped before, format and buffer were reset */
-               /*
-               if(stream->status == PCXHR_STREAM_STATUS_STOPPED) {
-                       err = pcxhr_set_format(stream);
-                       if(err) break;
-                       err = pcxhr_update_r_buffer(stream);
-                       if(err) break;
-               }
-               */
-
                /* only the first stream can choose the sample rate */
-               /* the further opened streams will be limited to its frequency (see open) */
                /* set the clock only once (first stream) */
                if (mgr->sample_rate != subs->runtime->rate) {
                        err = pcxhr_set_clock(mgr, subs->runtime->rate);
@@ -755,7 +912,7 @@ static int pcxhr_prepare(struct snd_pcm_substream *subs)
                }
        } while(0);     /* do only once (so we can use break instead of goto) */
 
-       up(&mgr->setup_mutex);
+       mutex_unlock(&mgr->setup_mutex);
 
        return err;
 }
@@ -780,28 +937,15 @@ static int pcxhr_hw_params(struct snd_pcm_substream *subs,
        /*  set up format for the stream */
        format = params_format(hw);
 
-       down(&mgr->setup_mutex);
+       mutex_lock(&mgr->setup_mutex);
 
        stream->channels = channels;
        stream->format = format;
 
-       /* set the format to the board */
-       /*
-       err = pcxhr_set_format(stream);
-       if(err) {
-               up(&mgr->setup_mutex);
-               return err;
-       }
-       */
        /* allocate buffer */
        err = snd_pcm_lib_malloc_pages(subs, params_buffer_bytes(hw));
 
-       /*
-       if (err > 0) {
-               err = pcxhr_update_r_buffer(stream);
-       }
-       */
-       up(&mgr->setup_mutex);
+       mutex_unlock(&mgr->setup_mutex);
 
        return err;
 }
@@ -818,14 +962,18 @@ static int pcxhr_hw_free(struct snd_pcm_substream *subs)
  */
 static struct snd_pcm_hardware pcxhr_caps =
 {
-       .info             = ( SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED |
-                             SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_SYNC_START |
-                             0 /*SNDRV_PCM_INFO_PAUSE*/),
-       .formats          = ( SNDRV_PCM_FMTBIT_U8 |
-                             SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
-                             SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |
-                             SNDRV_PCM_FMTBIT_FLOAT_LE ),
-       .rates            = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+       .info             = (SNDRV_PCM_INFO_MMAP |
+                            SNDRV_PCM_INFO_INTERLEAVED |
+                            SNDRV_PCM_INFO_MMAP_VALID |
+                            SNDRV_PCM_INFO_SYNC_START),
+       .formats          = (SNDRV_PCM_FMTBIT_U8 |
+                            SNDRV_PCM_FMTBIT_S16_LE |
+                            SNDRV_PCM_FMTBIT_S16_BE |
+                            SNDRV_PCM_FMTBIT_S24_3LE |
+                            SNDRV_PCM_FMTBIT_S24_3BE |
+                            SNDRV_PCM_FMTBIT_FLOAT_LE),
+       .rates            = (SNDRV_PCM_RATE_CONTINUOUS |
+                            SNDRV_PCM_RATE_8000_192000),
        .rate_min         = 8000,
        .rate_max         = 192000,
        .channels_min     = 1,
@@ -845,9 +993,9 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
        struct pcxhr_mgr       *mgr = chip->mgr;
        struct snd_pcm_runtime *runtime = subs->runtime;
        struct pcxhr_stream    *stream;
-       int                 is_capture;
+       int err;
 
-       down(&mgr->setup_mutex);
+       mutex_lock(&mgr->setup_mutex);
 
        /* copy the struct snd_pcm_hardware struct */
        runtime->hw = pcxhr_caps;
@@ -855,12 +1003,10 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
        if( subs->stream == SNDRV_PCM_STREAM_PLAYBACK ) {
                snd_printdd("pcxhr_open playback chip%d subs%d\n",
                            chip->chip_idx, subs->number);
-               is_capture = 0;
                stream = &chip->playback_stream[subs->number];
        } else {
                snd_printdd("pcxhr_open capture chip%d subs%d\n",
                            chip->chip_idx, subs->number);
-               is_capture = 1;
                if (mgr->mono_capture)
                        runtime->hw.channels_max = 1;
                else
@@ -871,10 +1017,22 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
                /* streams in use */
                snd_printk(KERN_ERR "pcxhr_open chip%d subs%d in use\n",
                           chip->chip_idx, subs->number);
-               up(&mgr->setup_mutex);
+               mutex_unlock(&mgr->setup_mutex);
                return -EBUSY;
        }
 
+       /* float format support is in some cases buggy on stereo cards */
+       if (mgr->is_hr_stereo)
+               runtime->hw.formats &= ~SNDRV_PCM_FMTBIT_FLOAT_LE;
+
+       /* buffer-size should better be multiple of period-size */
+       err = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0) {
+               mutex_unlock(&mgr->setup_mutex);
+               return err;
+       }
+
        /* if a sample rate is already used or fixed by external clock,
         * the stream cannot change
         */
@@ -887,10 +1045,11 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
                                                     &external_rate) ||
                            external_rate == 0) {
                                /* cannot detect the external clock rate */
-                               up(&mgr->setup_mutex);
+                               mutex_unlock(&mgr->setup_mutex);
                                return -EBUSY;
                        }
-                       runtime->hw.rate_min = runtime->hw.rate_max = external_rate;
+                       runtime->hw.rate_min = external_rate;
+                       runtime->hw.rate_max = external_rate;
                }
        }
 
@@ -900,12 +1059,16 @@ static int pcxhr_open(struct snd_pcm_substream *subs)
 
        runtime->private_data = stream;
 
-       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 4);
-       snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 4);
+       /* better get a divisor of granularity values (96 or 192) */
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+       snd_pcm_hw_constraint_step(runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+       snd_pcm_set_sync(subs);
 
        mgr->ref_count_rate++;
 
-       up(&mgr->setup_mutex);
+       mutex_unlock(&mgr->setup_mutex);
        return 0;
 }
 
@@ -916,20 +1079,21 @@ static int pcxhr_close(struct snd_pcm_substream *subs)
        struct pcxhr_mgr *mgr = chip->mgr;
        struct pcxhr_stream *stream = subs->runtime->private_data;
 
-       down(&mgr->setup_mutex);
+       mutex_lock(&mgr->setup_mutex);
 
-       snd_printdd("pcxhr_close chip%d subs%d\n", chip->chip_idx, subs->number);
+       snd_printdd("pcxhr_close chip%d subs%d\n",
+                   chip->chip_idx, subs->number);
 
        /* sample rate released */
        if (--mgr->ref_count_rate == 0) {
-               mgr->sample_rate = 0;           /* the sample rate is no more locked */
+               mgr->sample_rate = 0;   /* the sample rate is no more locked */
                pcxhr_hardware_timer(mgr, 0);   /* stop the DSP-timer */
        }
 
        stream->status    = PCXHR_STREAM_STATUS_FREE;
        stream->substream = NULL;
 
-       up(&mgr->setup_mutex);
+       mutex_unlock(&mgr->setup_mutex);
 
        return 0;
 }
@@ -1015,7 +1179,8 @@ static int pcxhr_chip_dev_free(struct snd_device *device)
 
 /*
  */
-static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card, int idx)
+static int __devinit pcxhr_create(struct pcxhr_mgr *mgr,
+                                 struct snd_card *card, int idx)
 {
        int err;
        struct snd_pcxhr *chip;
@@ -1023,7 +1188,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
                .dev_free = pcxhr_chip_dev_free,
        };
 
-       mgr->chip[idx] = chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
        if (! chip) {
                snd_printk(KERN_ERR "cannot allocate chip\n");
                return -ENOMEM;
@@ -1039,7 +1204,7 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
 
        if (idx < mgr->capture_chips) {
                if (mgr->mono_capture)
-                       chip->nb_streams_capt = 2;      /* 2 mono streams (left+right) */
+                       chip->nb_streams_capt = 2;      /* 2 mono streams */
                else
                        chip->nb_streams_capt = 1;      /* or 1 stereo stream */
        }
@@ -1049,13 +1214,15 @@ static int __devinit pcxhr_create(struct pcxhr_mgr *mgr, struct snd_card *card,
                return err;
        }
 
+       mgr->chip[idx] = chip;
        snd_card_set_dev(card, &mgr->pci->dev);
 
        return 0;
 }
 
 /* proc interface */
-static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+static void pcxhr_proc_info(struct snd_info_entry *entry,
+                           struct snd_info_buffer *buffer)
 {
        struct snd_pcxhr *chip = entry->private_data;
        struct pcxhr_mgr *mgr = chip->mgr;
@@ -1068,8 +1235,10 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
                short ver_maj = (mgr->dsp_version >> 16) & 0xff;
                short ver_min = (mgr->dsp_version >> 8) & 0xff;
                short ver_build = mgr->dsp_version & 0xff;
-               snd_iprintf(buffer, "module version %s\n", PCXHR_DRIVER_VERSION_STRING);
-               snd_iprintf(buffer, "dsp version %d.%d.%d\n", ver_maj, ver_min, ver_build);
+               snd_iprintf(buffer, "module version %s\n",
+                           PCXHR_DRIVER_VERSION_STRING);
+               snd_iprintf(buffer, "dsp version %d.%d.%d\n",
+                           ver_maj, ver_min, ver_build);
                if (mgr->board_has_analog)
                        snd_iprintf(buffer, "analog io available\n");
                else
@@ -1083,18 +1252,22 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
                        if (ref > 0) {
                                if (mgr->sample_rate_real != 0 &&
                                    mgr->sample_rate_real != 48000) {
-                                       ref = (ref * 48000) / mgr->sample_rate_real;
-                                       if (mgr->sample_rate_real >= PCXHR_IRQ_TIMER_FREQ)
+                                       ref = (ref * 48000) /
+                                         mgr->sample_rate_real;
+                                       if (mgr->sample_rate_real >=
+                                           PCXHR_IRQ_TIMER_FREQ)
                                                ref *= 2;
                                }
                                cur = 100 - (100 * cur) / ref;
                                snd_iprintf(buffer, "cpu load    %d%%\n", cur);
-                               snd_iprintf(buffer, "buffer pool %d/%d kWords\n",
+                               snd_iprintf(buffer, "buffer pool %d/%d\n",
                                            rmh.stat[2], rmh.stat[3]);
                        }
                }
-               snd_iprintf(buffer, "dma granularity : %d\n", PCXHR_GRANULARITY);
-               snd_iprintf(buffer, "dsp time errors : %d\n", mgr->dsp_time_err);
+               snd_iprintf(buffer, "dma granularity : %d\n",
+                           mgr->granularity);
+               snd_iprintf(buffer, "dsp time errors : %d\n",
+                           mgr->dsp_time_err);
                snd_iprintf(buffer, "dsp async pipe xrun errors : %d\n",
                            mgr->async_err_pipe_xrun);
                snd_iprintf(buffer, "dsp async stream xrun errors : %d\n",
@@ -1109,47 +1282,107 @@ static void pcxhr_proc_info(struct snd_info_entry *entry, struct snd_info_buffer
                rmh.cmd_idx = CMD_LAST_INDEX;
                if( ! pcxhr_send_msg(mgr, &rmh) ) {
                        int i;
+                       if (rmh.stat_len > 8)
+                               rmh.stat_len = 8;
                        for (i = 0; i < rmh.stat_len; i++)
-                               snd_iprintf(buffer, "debug[%02d] = %06x\n", i,  rmh.stat[i]);
+                               snd_iprintf(buffer, "debug[%02d] = %06x\n",
+                                           i,  rmh.stat[i]);
                }
        } else
                snd_iprintf(buffer, "no firmware loaded\n");
        snd_iprintf(buffer, "\n");
 }
-static void pcxhr_proc_sync(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+static void pcxhr_proc_sync(struct snd_info_entry *entry,
+                           struct snd_info_buffer *buffer)
 {
        struct snd_pcxhr *chip = entry->private_data;
        struct pcxhr_mgr *mgr = chip->mgr;
-       static char *texts[7] = {
-               "Internal", "Word", "AES Sync", "AES 1", "AES 2", "AES 3", "AES 4"
+       static const char *textsHR22[3] = {
+               "Internal", "AES Sync", "AES 1"
        };
+       static const char *textsPCXHR[7] = {
+               "Internal", "Word", "AES Sync",
+               "AES 1", "AES 2", "AES 3", "AES 4"
+       };
+       const char **texts;
+       int max_clock;
+       if (mgr->is_hr_stereo) {
+               texts = textsHR22;
+               max_clock = HR22_CLOCK_TYPE_MAX;
+       } else {
+               texts = textsPCXHR;
+               max_clock = PCXHR_CLOCK_TYPE_MAX;
+       }
 
        snd_iprintf(buffer, "\n%s\n", mgr->longname);
-       snd_iprintf(buffer, "Current Sample Clock\t: %s\n", texts[mgr->cur_clock_type]);
-       snd_iprintf(buffer, "Current Sample Rate\t= %d\n", mgr->sample_rate_real);
-
+       snd_iprintf(buffer, "Current Sample Clock\t: %s\n",
+                   texts[mgr->cur_clock_type]);
+       snd_iprintf(buffer, "Current Sample Rate\t= %d\n",
+                   mgr->sample_rate_real);
        /* commands available when embedded DSP is running */
        if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
                int i, err, sample_rate;
-               for (i = PCXHR_CLOCK_TYPE_WORD_CLOCK; i< (3 + mgr->capture_chips); i++) {
+               for (i = 1; i <= max_clock; i++) {
                        err = pcxhr_get_external_clock(mgr, i, &sample_rate);
                        if (err)
                                break;
-                       snd_iprintf(buffer, "%s Clock\t\t= %d\n", texts[i], sample_rate);
+                       snd_iprintf(buffer, "%s Clock\t\t= %d\n",
+                                   texts[i], sample_rate);
                }
        } else
                snd_iprintf(buffer, "no firmware loaded\n");
        snd_iprintf(buffer, "\n");
 }
 
+static void pcxhr_proc_gpio_read(struct snd_info_entry *entry,
+                                struct snd_info_buffer *buffer)
+{
+       struct snd_pcxhr *chip = entry->private_data;
+       struct pcxhr_mgr *mgr = chip->mgr;
+       /* commands available when embedded DSP is running */
+       if (mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)) {
+               /* gpio ports on stereo boards only available */
+               int value = 0;
+               hr222_read_gpio(mgr, 1, &value);        /* GPI */
+               snd_iprintf(buffer, "GPI: 0x%x\n", value);
+               hr222_read_gpio(mgr, 0, &value);        /* GP0 */
+               snd_iprintf(buffer, "GPO: 0x%x\n", value);
+       } else
+               snd_iprintf(buffer, "no firmware loaded\n");
+       snd_iprintf(buffer, "\n");
+}
+static void pcxhr_proc_gpo_write(struct snd_info_entry *entry,
+                                struct snd_info_buffer *buffer)
+{
+       struct snd_pcxhr *chip = entry->private_data;
+       struct pcxhr_mgr *mgr = chip->mgr;
+       char line[64];
+       int value;
+       /* commands available when embedded DSP is running */
+       if (!(mgr->dsp_loaded & (1 << PCXHR_FIRMWARE_DSP_MAIN_INDEX)))
+               return;
+       while (!snd_info_get_line(buffer, line, sizeof(line))) {
+               if (sscanf(line, "GPO: 0x%x", &value) != 1)
+                       continue;
+               hr222_write_gpo(mgr, value);    /* GP0 */
+       }
+}
+
 static void __devinit pcxhr_proc_init(struct snd_pcxhr *chip)
 {
        struct snd_info_entry *entry;
 
        if (! snd_card_proc_new(chip->card, "info", &entry))
-               snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_info);
+               snd_info_set_text_ops(entry, chip, pcxhr_proc_info);
        if (! snd_card_proc_new(chip->card, "sync", &entry))
-               snd_info_set_text_ops(entry, chip, 1024, pcxhr_proc_sync);
+               snd_info_set_text_ops(entry, chip, pcxhr_proc_sync);
+       /* gpio available on stereo sound cards only */
+       if (chip->mgr->is_hr_stereo &&
+           !snd_card_proc_new(chip->card, "gpio", &entry)) {
+               snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read);
+               entry->c.text.write = pcxhr_proc_gpo_write;
+               entry->mode |= S_IWUSR;
+       }
 }
 /* end of proc interface */
 
@@ -1193,7 +1426,8 @@ static int pcxhr_free(struct pcxhr_mgr *mgr)
 /*
  *    probe function - creates the card manager
  */
-static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
+static int __devinit pcxhr_probe(struct pci_dev *pci,
+                                const struct pci_device_id *pci_id)
 {
        static int dev;
        struct pcxhr_mgr *mgr;
@@ -1215,8 +1449,9 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
        pci_set_master(pci);
 
        /* check if we can restrict PCI DMA transfers to 32 bits */
-       if (pci_set_dma_mask(pci, 0xffffffff) < 0) {
-               snd_printk(KERN_ERR "architecture does not support 32bit PCI busmaster DMA\n");
+       if (pci_set_dma_mask(pci, DMA_BIT_MASK(32)) < 0) {
+               snd_printk(KERN_ERR "architecture does not support "
+                          "32bit PCI busmaster DMA\n");
                pci_disable_device(pci);
                return -ENXIO;
        }
@@ -1228,12 +1463,30 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
                return -ENOMEM;
        }
 
-       snd_assert(pci_id->driver_data < PCI_ID_LAST, return -ENODEV);
-       card_name = pcxhr_board_params[pci_id->driver_data].board_name;
-       mgr->playback_chips = pcxhr_board_params[pci_id->driver_data].playback_chips;
-       mgr->capture_chips  = pcxhr_board_params[pci_id->driver_data].capture_chips;
-       mgr->firmware_num  = pcxhr_board_params[pci_id->driver_data].firmware_num;
+       if (snd_BUG_ON(pci_id->driver_data >= PCI_ID_LAST)) {
+               kfree(mgr);
+               pci_disable_device(pci);
+               return -ENODEV;
+       }
+       card_name =
+               pcxhr_board_params[pci_id->driver_data].board_name;
+       mgr->playback_chips =
+               pcxhr_board_params[pci_id->driver_data].playback_chips;
+       mgr->capture_chips  =
+               pcxhr_board_params[pci_id->driver_data].capture_chips;
+       mgr->fw_file_set =
+               pcxhr_board_params[pci_id->driver_data].fw_file_set;
+       mgr->firmware_num  =
+               pcxhr_board_params[pci_id->driver_data].firmware_num;
        mgr->mono_capture = mono[dev];
+       mgr->is_hr_stereo = (mgr->playback_chips == 1);
+       mgr->board_has_aes1 = PCXHR_BOARD_HAS_AES1(mgr);
+       mgr->board_aes_in_192k = !PCXHR_BOARD_AESIN_NO_192K(mgr);
+
+       if (mgr->is_hr_stereo)
+               mgr->granularity = PCXHR_GRANULARITY_HR22;
+       else
+               mgr->granularity = PCXHR_GRANULARITY;
 
        /* resource assignment */
        if ((err = pci_request_regions(pci, card_name)) < 0) {
@@ -1247,7 +1500,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
        mgr->pci = pci;
        mgr->irq = -1;
 
-       if (request_irq(pci->irq, pcxhr_interrupt, SA_INTERRUPT|SA_SHIRQ,
+       if (request_irq(pci->irq, pcxhr_interrupt, IRQF_SHARED,
                        card_name, mgr)) {
                snd_printk(KERN_ERR "unable to grab IRQ %d\n", pci->irq);
                pcxhr_free(mgr);
@@ -1256,7 +1509,8 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
        mgr->irq = pci->irq;
 
        sprintf(mgr->shortname, "Digigram %s", card_name);
-       sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i", mgr->shortname,
+       sprintf(mgr->longname, "%s at 0x%lx & 0x%lx, 0x%lx irq %i",
+               mgr->shortname,
                mgr->port[0], mgr->port[1], mgr->port[2], mgr->irq);
 
        /* ISR spinlock  */
@@ -1264,13 +1518,17 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
        spin_lock_init(&mgr->msg_lock);
 
        /* init setup mutex*/
-       init_MUTEX(&mgr->setup_mutex);
+       mutex_init(&mgr->setup_mutex);
 
        /* init taslket */
-       tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet, (unsigned long) mgr);
-       tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet, (unsigned long) mgr);
+       tasklet_init(&mgr->msg_taskq, pcxhr_msg_tasklet,
+                    (unsigned long) mgr);
+       tasklet_init(&mgr->trigger_taskq, pcxhr_trigger_tasklet,
+                    (unsigned long) mgr);
+
        mgr->prmh = kmalloc(sizeof(*mgr->prmh) + 
-                           sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS - PCXHR_SIZE_MAX_STATUS),
+                           sizeof(u32) * (PCXHR_SIZE_MAX_LONG_STATUS -
+                                          PCXHR_SIZE_MAX_STATUS),
                            GFP_KERNEL);
        if (! mgr->prmh) {
                pcxhr_free(mgr);
@@ -1291,13 +1549,14 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
                else
                        idx = index[dev] + i;
 
-               snprintf(tmpid, sizeof(tmpid), "%s-%d", id[dev] ? id[dev] : card_name, i);
-               card = snd_card_new(idx, tmpid, THIS_MODULE, 0);
+               snprintf(tmpid, sizeof(tmpid), "%s-%d",
+                        id[dev] ? id[dev] : card_name, i);
+               err = snd_card_create(idx, tmpid, THIS_MODULE, 0, &card);
 
-               if (! card) {
+               if (err < 0) {
                        snd_printk(KERN_ERR "cannot allocate the card %d\n", i);
                        pcxhr_free(mgr);
-                       return -ENOMEM;
+                       return err;
                }
 
                strcpy(card->driver, DRIVER_NAME);
@@ -1305,6 +1564,7 @@ static int __devinit pcxhr_probe(struct pci_dev *pci, const struct pci_device_id
                sprintf(card->longname, "%s [PCM #%d]", mgr->longname, i);
 
                if ((err = pcxhr_create(mgr, card, i)) < 0) {
+                       snd_card_free(card);
                        pcxhr_free(mgr);
                        return err;
                }