]> err.no Git - linux-2.6/commitdiff
[ALSA] Add echoaudio sound drivers
authorGiuliano Pochini <pochini@shiny.it>
Wed, 28 Jun 2006 11:53:41 +0000 (13:53 +0200)
committerJaroslav Kysela <perex@suse.cz>
Wed, 28 Jun 2006 17:31:20 +0000 (19:31 +0200)
From: Giuliano Pochini <pochini@shiny.it>Add echoaudio sound drivers (darla20, darla24, echo3g, gina20, gina24,
indigo, indigodj, indigoio, layla20, lala24, mia, mona)

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>
35 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
sound/pci/Kconfig
sound/pci/Makefile
sound/pci/echoaudio/Makefile [new file with mode: 0644]
sound/pci/echoaudio/darla20.c [new file with mode: 0644]
sound/pci/echoaudio/darla20_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/darla24.c [new file with mode: 0644]
sound/pci/echoaudio/darla24_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/echo3g.c [new file with mode: 0644]
sound/pci/echoaudio/echo3g_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/echoaudio.c [new file with mode: 0644]
sound/pci/echoaudio/echoaudio.h [new file with mode: 0644]
sound/pci/echoaudio/echoaudio_3g.c [new file with mode: 0644]
sound/pci/echoaudio/echoaudio_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/echoaudio_dsp.h [new file with mode: 0644]
sound/pci/echoaudio/echoaudio_gml.c [new file with mode: 0644]
sound/pci/echoaudio/gina20.c [new file with mode: 0644]
sound/pci/echoaudio/gina20_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/gina24.c [new file with mode: 0644]
sound/pci/echoaudio/gina24_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/indigo.c [new file with mode: 0644]
sound/pci/echoaudio/indigo_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/indigodj.c [new file with mode: 0644]
sound/pci/echoaudio/indigodj_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/indigoio.c [new file with mode: 0644]
sound/pci/echoaudio/indigoio_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/layla20.c [new file with mode: 0644]
sound/pci/echoaudio/layla20_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/layla24.c [new file with mode: 0644]
sound/pci/echoaudio/layla24_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/mia.c [new file with mode: 0644]
sound/pci/echoaudio/mia_dsp.c [new file with mode: 0644]
sound/pci/echoaudio/midi.c [new file with mode: 0644]
sound/pci/echoaudio/mona.c [new file with mode: 0644]
sound/pci/echoaudio/mona_dsp.c [new file with mode: 0644]

index 87d76a5c73d05742c0532d702819111681730af9..0846b455bcf2beb210c4b0d606c410c24503a083 100644 (file)
@@ -472,6 +472,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-darla20
+  ------------------
+
+    Module for Echoaudio Darla20
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
+  Module snd-darla24
+  ------------------
+
+    Module for Echoaudio Darla24
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-dt019x
   -----------------
 
@@ -499,6 +515,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-echo3g
+  -----------------
+
+    Module for Echoaudio 3G cards (Gina3G/Layla3G)
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-emu10k1
   ------------------
 
@@ -657,6 +681,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     
     The power-management is supported.
 
+  Module snd-gina20
+  -----------------
+
+    Module for Echoaudio Gina20
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
+  Module snd-gina24
+  -----------------
+
+    Module for Echoaudio Gina24
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-gusclassic
   ---------------------
 
@@ -937,6 +977,30 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          driver isn't configured properly or you want to try another
          type for testing.
 
+  Module snd-indigo
+  -----------------
+
+    Module for Echoaudio Indigo
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
+  Module snd-indigodj
+  -------------------
+
+    Module for Echoaudio Indigo DJ
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
+  Module snd-indigoio
+  -------------------
+
+    Module for Echoaudio Indigo IO
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-intel8x0
   -------------------
 
@@ -1036,6 +1100,22 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     This module supports multiple cards.
 
+  Module snd-layla20
+  ------------------
+
+    Module for Echoaudio Layla20
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
+  Module snd-layla24
+  ------------------
+
+    Module for Echoaudio Layla24
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-maestro3
   -------------------
 
@@ -1056,6 +1136,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-mia
+  ---------------
+
+    Module for Echoaudio Mia
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-miro
   ---------------
 
@@ -1088,6 +1176,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     When no hotplug fw loader is available, you need to load the
     firmware via mixartloader utility in alsa-tools package.
 
+  Module snd-mona
+  ---------------
+
+    Module for Echoaudio Mona
+
+    This module supports multiple cards.
+    The driver requires the firmware loader support on kernel.
+
   Module snd-mpu401
   -----------------
 
index 39162af7f19ee30cca852230b37732c18b7c19ab..23e54cedfd4a19c8c49915aa24f41e531535a68a 100644 (file)
@@ -233,6 +233,143 @@ config SND_CS5535AUDIO
          To compile this driver as a module, choose M here: the module
          will be called snd-cs5535audio.
 
+config SND_DARLA20
+       tristate "(Echoaudio) Darla20"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Darla.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-darla20
+
+config SND_GINA20
+       tristate "(Echoaudio) Gina20"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Gina.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-gina20
+
+config SND_LAYLA20
+       tristate "(Echoaudio) Layla20"
+       depends on SND
+       depends on FW_LOADER
+       select SND_RAWMIDI
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Layla.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-layla20
+
+config SND_DARLA24
+       tristate "(Echoaudio) Darla24"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Darla24.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-darla24
+
+config SND_GINA24
+       tristate "(Echoaudio) Gina24"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Gina24.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-gina24
+
+config SND_LAYLA24
+       tristate "(Echoaudio) Layla24"
+       depends on SND
+       depends on FW_LOADER
+       select SND_RAWMIDI
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Layla24.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-layla24
+
+config SND_MONA
+       tristate "(Echoaudio) Mona"
+       depends on SND
+       depends on FW_LOADER
+       select SND_RAWMIDI
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Mona.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-mona
+
+config SND_MIA
+       tristate "(Echoaudio) Mia"
+       depends on SND
+       depends on FW_LOADER
+       select SND_RAWMIDI
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Mia and Mia-midi.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-mia
+
+config SND_ECHO3G
+       tristate "(Echoaudio) 3G cards"
+       depends on SND
+       depends on FW_LOADER
+       select SND_RAWMIDI
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Gina3G and Layla3G.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-echo3g
+
+config SND_INDIGO
+       tristate "(Echoaudio) Indigo"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Indigo.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-indigo
+
+config SND_INDIGOIO
+       tristate "(Echoaudio) Indigo IO"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Indigo IO.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-indigoio
+
+config SND_INDIGODJ
+       tristate "(Echoaudio) Indigo DJ"
+       depends on SND
+       depends on FW_LOADER
+       select SND_PCM
+       help
+         Say 'Y' or 'M' to include support for Echoaudio Indigo DJ.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-indigodj
+
 config SND_EMU10K1
        tristate "Emu10k1 (SB Live!, Audigy, E-mu APS)"
        depends on SND
index cba5105aafeae000b55323e4009cb4ee23bfcbcf..e06736da9ef196e8fc766d2c2c96a8f7258ec94d 100644 (file)
@@ -57,6 +57,7 @@ obj-$(CONFIG_SND) += \
        ca0106/ \
        cs46xx/ \
        cs5535audio/ \
+       echoaudio/ \
        emu10k1/ \
        hda/ \
        ice1712/ \
diff --git a/sound/pci/echoaudio/Makefile b/sound/pci/echoaudio/Makefile
new file mode 100644 (file)
index 0000000..02ab0e5
--- /dev/null
@@ -0,0 +1,17 @@
+#
+# Makefile for ALSA Echoaudio soundcard drivers
+# Copyright (c) 2003 by Giuliano Pochini <pochini@shiny.it>
+#
+
+snd-darla20-objs := darla20.o
+snd-gina20-objs := gina20.o
+snd-layla20-objs := layla20.o
+snd-darla24-objs := darla24.o
+snd-gina24-objs := gina24.o
+snd-layla24-objs := layla24.o
+snd-mona-objs := mona.o
+snd-mia-objs := mia.o
+snd-echo3g-objs := echo3g.o
+snd-indigo-objs := indigo.o
+snd-indigoio-objs := indigoio.o
+snd-indigodj-objs := indigodj.o
diff --git a/sound/pci/echoaudio/darla20.c b/sound/pci/echoaudio/darla20.c
new file mode 100644 (file)
index 0000000..b7108e2
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_DARLA20
+#define ECHOCARD_NAME "Darla20"
+#define ECHOCARD_HAS_MONITOR
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 0 */
+#define PX_ANALOG_IN   8       /* 2 */
+#define PX_DIGITAL_IN  10      /* 0 */
+#define PX_NUM         10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 8 */
+#define BX_DIGITAL_OUT 8       /* 0 */
+#define BX_ANALOG_IN   8       /* 2 */
+#define BX_DIGITAL_IN  10      /* 0 */
+#define BX_NUM         10
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_DARLA20_DSP 0
+
+static const struct firmware card_fw[] = {
+       {0, "darla20_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0},      /* DSP 56301 Darla20 rev.0 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+       .rate_min = 44100,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "darla20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/darla20_dsp.c b/sound/pci/echoaudio/darla20_dsp.c
new file mode 100644 (file)
index 0000000..4159e3b
--- /dev/null
@@ -0,0 +1,125 @@
+/***************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Darla20\n"));
+       snd_assert((subdevice_id & 0xfff0) == DARLA20, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP];
+       chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+       chip->clock_state = GD_CLOCK_UNDEF;
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+/* The Darla20 has no external clock sources */
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The Darla20 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u8 clock_state, spdif_status;
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       switch (rate) {
+       case 44100:
+               clock_state = GD_CLOCK_44;
+               spdif_status = GD_SPDIF_STATUS_44;
+               break;
+       case 48000:
+               clock_state = GD_CLOCK_48;
+               spdif_status = GD_SPDIF_STATUS_48;
+               break;
+       default:
+               clock_state = GD_CLOCK_NOCHANGE;
+               spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+               break;
+       }
+
+       if (chip->clock_state == clock_state)
+               clock_state = GD_CLOCK_NOCHANGE;
+       if (spdif_status == chip->spdif_status)
+               spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);
+       chip->comm_page->gd_clock_state = clock_state;
+       chip->comm_page->gd_spdif_status = spdif_status;
+       chip->comm_page->gd_resampler_state = 3;        /* magic number - should always be 3 */
+
+       /* Save the new audio state if it changed */
+       if (clock_state != GD_CLOCK_NOCHANGE)
+               chip->clock_state = clock_state;
+       if (spdif_status != GD_SPDIF_STATUS_NOCHANGE)
+               chip->spdif_status = spdif_status;
+       chip->sample_rate = rate;
+
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
diff --git a/sound/pci/echoaudio/darla24.c b/sound/pci/echoaudio/darla24.c
new file mode 100644 (file)
index 0000000..e59a982
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_DARLA24
+#define ECHOCARD_NAME "Darla24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 0 */
+#define PX_ANALOG_IN   8       /* 2 */
+#define PX_DIGITAL_IN  10      /* 0 */
+#define PX_NUM         10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 8 */
+#define BX_DIGITAL_OUT 8       /* 0 */
+#define BX_ANALOG_IN   8       /* 2 */
+#define BX_DIGITAL_IN  10      /* 0 */
+#define BX_NUM         10
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_DARLA24_DSP 0
+
+static const struct firmware card_fw[] = {
+       {0, "darla24_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0},      /* DSP 56301 Darla24 rev.0 */
+       {0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0},      /* DSP 56301 Darla24 rev.1 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_8000_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 8000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "darla24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/darla24_dsp.c b/sound/pci/echoaudio/darla24_dsp.c
new file mode 100644 (file)
index 0000000..79938ee
--- /dev/null
@@ -0,0 +1,156 @@
+/***************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Darla24\n"));
+       snd_assert((subdevice_id & 0xfff0) == DARLA24, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP];
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+               ECHO_CLOCK_BIT_ESYNC;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock
+          detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_ESYNC)
+               clock_bits |= ECHO_CLOCK_BIT_ESYNC;
+
+       return clock_bits;
+}
+
+
+
+/* The Darla24 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u8 clock;
+
+       switch (rate) {
+       case 96000:
+               clock = GD24_96000;
+               break;
+       case 88200:
+               clock = GD24_88200;
+               break;
+       case 48000:
+               clock = GD24_48000;
+               break;
+       case 44100:
+               clock = GD24_44100;
+               break;
+       case 32000:
+               clock = GD24_32000;
+               break;
+       case 22050:
+               clock = GD24_22050;
+               break;
+       case 16000:
+               clock = GD24_16000;
+               break;
+       case 11025:
+               clock = GD24_11025;
+               break;
+       case 8000:
+               clock = GD24_8000;
+               break;
+       default:
+               DE_ACT(("set_sample_rate: Error, invalid sample rate %d\n",
+                       rate));
+               return -EINVAL;
+       }
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+       chip->sample_rate = rate;
+
+       /* Override the sample rate if this card is set to Echo sync. */
+       if (chip->input_clock == ECHO_CLOCK_ESYNC)
+               clock = GD24_EXT_SYNC;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP ? */
+       chip->comm_page->gd_clock_state = clock;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       snd_assert(clock == ECHO_CLOCK_INTERNAL ||
+                  clock == ECHO_CLOCK_ESYNC, return -EINVAL);
+       chip->input_clock = clock;
+       return set_sample_rate(chip, chip->sample_rate);
+}
+
diff --git a/sound/pci/echoaudio/echo3g.c b/sound/pci/echoaudio/echo3g.c
new file mode 100644 (file)
index 0000000..12099fe
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO3G_FAMILY
+#define ECHOCARD_ECHO3G
+#define ECHOCARD_NAME "Echo3G"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_ADAT      6
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+#define ECHOCARD_HAS_PHANTOM_POWER
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0
+#define PX_DIGITAL_OUT chip->px_digital_out
+#define PX_ANALOG_IN   chip->px_analog_in
+#define PX_DIGITAL_IN  chip->px_digital_in
+#define PX_NUM         chip->px_num
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0
+#define BX_DIGITAL_OUT chip->bx_digital_out
+#define BX_ANALOG_IN   chip->bx_analog_in
+#define BX_DIGITAL_IN  chip->bx_digital_in
+#define BX_NUM         chip->bx_num
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER  0
+#define FW_ECHO3G_DSP  1
+#define FW_3G_ASIC     2
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "echo3g_dsp.fw"},
+       {0, "3g_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0},      /* Echo 3G */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000 |
+                       SNDRV_PCM_RATE_CONTINUOUS,
+       .rate_min = 32000,
+       .rate_max = 100000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+};
+
+#include "echo3g_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_3g.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/echo3g_dsp.c b/sound/pci/echoaudio/echo3g_dsp.c
new file mode 100644 (file)
index 0000000..d26a1d1
--- /dev/null
@@ -0,0 +1,131 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+static int load_asic(struct echoaudio *chip);
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int check_asic_status(struct echoaudio *chip);
+static int set_sample_rate(struct echoaudio *chip, u32 rate);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_phantom_power(struct echoaudio *chip, char on);
+static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
+                            char force);
+
+#include <linux/irq.h>
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       local_irq_enable();
+       DE_INIT(("init_hw() - Echo3G\n"));
+       snd_assert((subdevice_id & 0xfff0) == ECHO3G, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->comm_page->e3g_frq_register =
+               __constant_cpu_to_le32((E3G_MAGIC_NUMBER / 48000) - 2);
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->has_midi = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP];
+
+       /* Load the DSP code and the ASIC on the PCI card and get
+       what type of external box is attached */
+       err = load_firmware(chip);
+
+       if (err < 0) {
+               return err;
+       } else if (err == E3G_GINA3G_BOX_TYPE) {
+               chip->input_clock_types =       ECHO_CLOCK_BIT_INTERNAL |
+                                               ECHO_CLOCK_BIT_SPDIF |
+                                               ECHO_CLOCK_BIT_ADAT;
+               chip->card_name = "Gina3G";
+               chip->px_digital_out = chip->bx_digital_out = 6;
+               chip->px_analog_in = chip->bx_analog_in = 14;
+               chip->px_digital_in = chip->bx_digital_in = 16;
+               chip->px_num = chip->bx_num = 24;
+               chip->has_phantom_power = TRUE;
+               chip->hasnt_input_nominal_level = TRUE;
+       } else if (err == E3G_LAYLA3G_BOX_TYPE) {
+               chip->input_clock_types =       ECHO_CLOCK_BIT_INTERNAL |
+                                               ECHO_CLOCK_BIT_SPDIF |
+                                               ECHO_CLOCK_BIT_ADAT |
+                                               ECHO_CLOCK_BIT_WORD;
+               chip->card_name = "Layla3G";
+               chip->px_digital_out = chip->bx_digital_out = 8;
+               chip->px_analog_in = chip->bx_analog_in = 16;
+               chip->px_digital_in = chip->bx_digital_in = 24;
+               chip->px_num = chip->bx_num = 32;
+       } else {
+               return -ENODEV;
+       }
+
+       chip->digital_modes =   ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+                               ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+                               ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+       chip->digital_mode =    DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->non_audio_spdif = FALSE;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+       snd_assert(err >= 0, return err);
+       err = set_phantom_power(chip, 0);
+       snd_assert(err >= 0, return err);
+       err = set_professional_spdif(chip, TRUE);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static int set_phantom_power(struct echoaudio *chip, char on)
+{
+       u32 control_reg = le32_to_cpu(chip->comm_page->control_register);
+
+       if (on)
+               control_reg |= E3G_PHANTOM_POWER;
+       else
+               control_reg &= ~E3G_PHANTOM_POWER;
+
+       chip->phantom_power = on;
+       return write_control_reg(chip, control_reg,
+                                le32_to_cpu(chip->comm_page->e3g_frq_register),
+                                0);
+}
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
new file mode 100644 (file)
index 0000000..e695502
--- /dev/null
@@ -0,0 +1,2197 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+MODULE_AUTHOR("Giuliano Pochini <pochini@shiny.it>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Echoaudio " ECHOCARD_NAME " soundcards driver");
+MODULE_SUPPORTED_DEVICE("{{Echoaudio," ECHOCARD_NAME "}}");
+MODULE_DEVICE_TABLE(pci, snd_echo_ids);
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " ECHOCARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " ECHOCARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
+
+static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
+
+static int get_firmware(const struct firmware **fw_entry,
+                       const struct firmware *frm, struct echoaudio *chip)
+{
+       int err;
+       char name[30];
+       DE_ACT(("firmware requested: %s\n", frm->data));
+       snprintf(name, sizeof(name), "ea/%s", frm->data);
+       if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0)
+               snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
+       return err;
+}
+
+static void free_firmware(const struct firmware *fw_entry)
+{
+       release_firmware(fw_entry);
+       DE_ACT(("firmware released\n"));
+}
+
+
+
+/******************************************************************************
+       PCM interface
+******************************************************************************/
+
+static void audiopipe_free(struct snd_pcm_runtime *runtime)
+{
+       struct audiopipe *pipe = runtime->private_data;
+
+       if (pipe->sgpage.area)
+               snd_dma_free_pages(&pipe->sgpage);
+       kfree(pipe);
+}
+
+
+
+static int hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params,
+                                             struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c = hw_param_interval(params,
+                                                  SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_mask fmt;
+
+       snd_mask_any(&fmt);
+
+#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+       /* >=2 channels cannot be S32_BE */
+       if (c->min == 2) {
+               fmt.bits[0] &= ~SNDRV_PCM_FMTBIT_S32_BE;
+               return snd_mask_refine(f, &fmt);
+       }
+#endif
+       /* > 2 channels cannot be U8 and S32_BE */
+       if (c->min > 2) {
+               fmt.bits[0] &= ~(SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S32_BE);
+               return snd_mask_refine(f, &fmt);
+       }
+       /* Mono is ok with any format */
+       return 0;
+}
+
+
+
+static int hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params,
+                                             struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c = hw_param_interval(params,
+                                                  SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_interval ch;
+
+       snd_interval_any(&ch);
+
+       /* S32_BE is mono (and stereo) only */
+       if (f->bits[0] == SNDRV_PCM_FMTBIT_S32_BE) {
+               ch.min = 1;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+               ch.max = 2;
+#else
+               ch.max = 1;
+#endif
+               ch.integer = 1;
+               return snd_interval_refine(c, &ch);
+       }
+       /* U8 can be only mono or stereo */
+       if (f->bits[0] == SNDRV_PCM_FMTBIT_U8) {
+               ch.min = 1;
+               ch.max = 2;
+               ch.integer = 1;
+               return snd_interval_refine(c, &ch);
+       }
+       /* S16_LE, S24_3LE and S32_LE support any number of channels. */
+       return 0;
+}
+
+
+
+static int hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params,
+                                              struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c = hw_param_interval(params,
+                                                  SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_mask fmt;
+       u64 fmask;
+       snd_mask_any(&fmt);
+
+       fmask = fmt.bits[0] + ((u64)fmt.bits[1] << 32);
+
+       /* >2 channels must be S16_LE, S24_3LE or S32_LE */
+       if (c->min > 2) {
+               fmask &= SNDRV_PCM_FMTBIT_S16_LE |
+                        SNDRV_PCM_FMTBIT_S24_3LE |
+                        SNDRV_PCM_FMTBIT_S32_LE;
+       /* 1 channel must be S32_BE or S32_LE */
+       } else if (c->max == 1)
+               fmask &= SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE;
+#ifndef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+       /* 2 channels cannot be S32_BE */
+       else if (c->min == 2 && c->max == 2)
+               fmask &= ~SNDRV_PCM_FMTBIT_S32_BE;
+#endif
+       else
+               return 0;
+
+       fmt.bits[0] &= (u32)fmask;
+       fmt.bits[1] &= (u32)(fmask >> 32);
+       return snd_mask_refine(f, &fmt);
+}
+
+
+
+static int hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params,
+                                              struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c = hw_param_interval(params,
+                                                  SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_mask *f = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+       struct snd_interval ch;
+       u64 fmask;
+
+       snd_interval_any(&ch);
+       ch.integer = 1;
+       fmask = f->bits[0] + ((u64)f->bits[1] << 32);
+
+       /* S32_BE is mono (and stereo) only */
+       if (fmask == SNDRV_PCM_FMTBIT_S32_BE) {
+               ch.min = 1;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+               ch.max = 2;
+#else
+               ch.max = 1;
+#endif
+       /* U8 is stereo only */
+       } else if (fmask == SNDRV_PCM_FMTBIT_U8)
+               ch.min = ch.max = 2;
+       /* S16_LE and S24_3LE must be at least stereo */
+       else if (!(fmask & ~(SNDRV_PCM_FMTBIT_S16_LE |
+                              SNDRV_PCM_FMTBIT_S24_3LE)))
+               ch.min = 2;
+       else
+               return 0;
+
+       return snd_interval_refine(c, &ch);
+}
+
+
+
+/* Since the sample rate is a global setting, do allow the user to change the
+sample rate only if there is only one pcm device open. */
+static int hw_rule_sample_rate(struct snd_pcm_hw_params *params,
+                              struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                                                     SNDRV_PCM_HW_PARAM_RATE);
+       struct echoaudio *chip = rule->private;
+       struct snd_interval fixed;
+
+       if (!chip->can_set_rate) {
+               snd_interval_any(&fixed);
+               fixed.min = fixed.max = chip->sample_rate;
+               return snd_interval_refine(rate, &fixed);
+       }
+       return 0;
+}
+
+
+static int pcm_open(struct snd_pcm_substream *substream,
+                   signed char max_channels)
+{
+       struct echoaudio *chip;
+       struct snd_pcm_runtime *runtime;
+       struct audiopipe *pipe;
+       int err, i;
+
+       if (max_channels <= 0)
+               return -EAGAIN;
+
+       chip = snd_pcm_substream_chip(substream);
+       runtime = substream->runtime;
+
+       if (!(pipe = kmalloc(sizeof(struct audiopipe), GFP_KERNEL)))
+               return -ENOMEM;
+       memset(pipe, 0, sizeof(struct audiopipe));
+       pipe->index = -1;               /* Not configured yet */
+
+       /* Set up hw capabilities and contraints */
+       memcpy(&pipe->hw, &pcm_hardware_skel, sizeof(struct snd_pcm_hardware));
+       DE_HWP(("max_channels=%d\n", max_channels));
+       pipe->constr.list = channels_list;
+       pipe->constr.mask = 0;
+       for (i = 0; channels_list[i] <= max_channels; i++);
+       pipe->constr.count = i;
+       if (pipe->hw.channels_max > max_channels)
+               pipe->hw.channels_max = max_channels;
+       if (chip->digital_mode == DIGITAL_MODE_ADAT) {
+               pipe->hw.rate_max = 48000;
+               pipe->hw.rates &= SNDRV_PCM_RATE_8000_48000;
+       }
+
+       runtime->hw = pipe->hw;
+       runtime->private_data = pipe;
+       runtime->private_free = audiopipe_free;
+       snd_pcm_set_sync(substream);
+
+       /* Only mono and any even number of channels are allowed */
+       if ((err = snd_pcm_hw_constraint_list(runtime, 0,
+                                             SNDRV_PCM_HW_PARAM_CHANNELS,
+                                             &pipe->constr)) < 0)
+               return err;
+
+       /* All periods should have the same size */
+       if ((err = snd_pcm_hw_constraint_integer(runtime,
+                                                SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
+               return err;
+
+       /* The hw accesses memory in chunks 32 frames long and they should be
+       32-bytes-aligned. It's not a requirement, but it seems that IRQs are
+       generated with a resolution of 32 frames. Thus we need the following */
+       if ((err = snd_pcm_hw_constraint_step(runtime, 0,
+                                             SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                             32)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_constraint_step(runtime, 0,
+                                             SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
+                                             32)) < 0)
+               return err;
+
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_RATE,
+                                       hw_rule_sample_rate, chip,
+                                      SNDRV_PCM_HW_PARAM_RATE, -1)) < 0)
+               return err;
+
+       /* Finally allocate a page for the scatter-gather list */
+       if ((err = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+                                      snd_dma_pci_data(chip->pci),
+                                      PAGE_SIZE, &pipe->sgpage)) < 0) {
+               DE_HWP(("s-g list allocation failed\n"));
+               return err;
+       }
+
+       return 0;
+}
+
+
+
+static int pcm_analog_in_open(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       int err;
+
+       DE_ACT(("pcm_analog_in_open\n"));
+       if ((err = pcm_open(substream, num_analog_busses_in(chip) -
+                           substream->number)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      hw_rule_capture_channels_by_format, NULL,
+                                      SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      hw_rule_capture_format_by_channels, NULL,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+               return err;
+       atomic_inc(&chip->opencount);
+       if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+               chip->can_set_rate=0;
+       DE_HWP(("pcm_analog_in_open  cs=%d  oc=%d  r=%d\n",
+               chip->can_set_rate, atomic_read(&chip->opencount),
+               chip->sample_rate));
+       return 0;
+}
+
+
+
+static int pcm_analog_out_open(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       int max_channels, err;
+
+#ifdef ECHOCARD_HAS_VMIXER
+       max_channels = num_pipes_out(chip);
+#else
+       max_channels = num_analog_busses_out(chip);
+#endif
+       DE_ACT(("pcm_analog_out_open\n"));
+       if ((err = pcm_open(substream, max_channels - substream->number)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      hw_rule_playback_channels_by_format,
+                                      NULL,
+                                      SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+               return err;
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      hw_rule_playback_format_by_channels,
+                                      NULL,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+               return err;
+       atomic_inc(&chip->opencount);
+       if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+               chip->can_set_rate=0;
+       DE_HWP(("pcm_analog_out_open  cs=%d  oc=%d  r=%d\n",
+               chip->can_set_rate, atomic_read(&chip->opencount),
+               chip->sample_rate));
+       return 0;
+}
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+static int pcm_digital_in_open(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       int err, max_channels;
+
+       DE_ACT(("pcm_digital_in_open\n"));
+       max_channels = num_digital_busses_in(chip) - substream->number;
+       down(&chip->mode_mutex);
+       if (chip->digital_mode == DIGITAL_MODE_ADAT)
+               err = pcm_open(substream, max_channels);
+       else    /* If the card has ADAT, subtract the 6 channels
+                * that S/PDIF doesn't have
+                */
+               err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
+
+       if (err < 0)
+               goto din_exit;
+
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      hw_rule_capture_channels_by_format, NULL,
+                                      SNDRV_PCM_HW_PARAM_FORMAT, -1)) < 0)
+               goto din_exit;
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      hw_rule_capture_format_by_channels, NULL,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS, -1)) < 0)
+               goto din_exit;
+
+       atomic_inc(&chip->opencount);
+       if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+               chip->can_set_rate=0;
+
+din_exit:
+       up(&chip->mode_mutex);
+       return err;
+}
+
+
+
+#ifndef ECHOCARD_HAS_VMIXER    /* See the note in snd_echo_new_pcm() */
+
+static int pcm_digital_out_open(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       int err, max_channels;
+
+       DE_ACT(("pcm_digital_out_open\n"));
+       max_channels = num_digital_busses_out(chip) - substream->number;
+       down(&chip->mode_mutex);
+       if (chip->digital_mode == DIGITAL_MODE_ADAT)
+               err = pcm_open(substream, max_channels);
+       else    /* If the card has ADAT, subtract the 6 channels
+                * that S/PDIF doesn't have
+                */
+               err = pcm_open(substream, max_channels - ECHOCARD_HAS_ADAT);
+
+       if (err < 0)
+               goto dout_exit;
+
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      hw_rule_playback_channels_by_format,
+                                      NULL, SNDRV_PCM_HW_PARAM_FORMAT,
+                                      -1)) < 0)
+               goto dout_exit;
+       if ((err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                      SNDRV_PCM_HW_PARAM_FORMAT,
+                                      hw_rule_playback_format_by_channels,
+                                      NULL, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                      -1)) < 0)
+               goto dout_exit;
+       atomic_inc(&chip->opencount);
+       if (atomic_read(&chip->opencount) > 1 && chip->rate_set)
+               chip->can_set_rate=0;
+dout_exit:
+       up(&chip->mode_mutex);
+       return err;
+}
+
+#endif /* !ECHOCARD_HAS_VMIXER */
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       int oc;
+
+       /* Nothing to do here. Audio is already off and pipe will be
+        * freed by its callback
+        */
+       DE_ACT(("pcm_close\n"));
+
+       atomic_dec(&chip->opencount);
+       oc = atomic_read(&chip->opencount);
+       DE_ACT(("pcm_close  oc=%d  cs=%d  rs=%d\n", oc,
+               chip->can_set_rate, chip->rate_set));
+       if (oc < 2)
+               chip->can_set_rate = 1;
+       if (oc == 0)
+               chip->rate_set = 0;
+       DE_ACT(("pcm_close2 oc=%d  cs=%d  rs=%d\n", oc,
+               chip->can_set_rate,chip->rate_set));
+
+       return 0;
+}
+
+
+
+/* Channel allocation and scatter-gather list setup */
+static int init_engine(struct snd_pcm_substream *substream,
+                      struct snd_pcm_hw_params *hw_params,
+                      int pipe_index, int interleave)
+{
+       struct echoaudio *chip;
+       int err, per, rest, page, edge, offs;
+       struct snd_sg_buf *sgbuf;
+       struct audiopipe *pipe;
+
+       chip = snd_pcm_substream_chip(substream);
+       pipe = (struct audiopipe *) substream->runtime->private_data;
+
+       /* Sets up che hardware. If it's already initialized, reset and
+        * redo with the new parameters
+        */
+       spin_lock_irq(&chip->lock);
+       if (pipe->index >= 0) {
+               DE_HWP(("hwp_ie free(%d)\n", pipe->index));
+               err = free_pipes(chip, pipe);
+               snd_assert(!err);
+               chip->substream[pipe->index] = NULL;
+       }
+
+       err = allocate_pipes(chip, pipe, pipe_index, interleave);
+       if (err < 0) {
+               spin_unlock_irq(&chip->lock);
+               DE_ACT((KERN_NOTICE "allocate_pipes(%d) err=%d\n",
+                       pipe_index, err));
+               return err;
+       }
+       spin_unlock_irq(&chip->lock);
+       DE_ACT((KERN_NOTICE "allocate_pipes()=%d\n", pipe_index));
+
+       DE_HWP(("pcm_hw_params (bufsize=%dB periods=%d persize=%dB)\n",
+               params_buffer_bytes(hw_params), params_periods(hw_params),
+               params_period_bytes(hw_params)));
+       err = snd_pcm_lib_malloc_pages(substream,
+                                      params_buffer_bytes(hw_params));
+       if (err < 0) {
+               snd_printk(KERN_ERR "malloc_pages err=%d\n", err);
+               spin_lock_irq(&chip->lock);
+               free_pipes(chip, pipe);
+               spin_unlock_irq(&chip->lock);
+               pipe->index = -1;
+               return err;
+       }
+
+       sgbuf = snd_pcm_substream_sgbuf(substream);
+
+       DE_HWP(("pcm_hw_params table size=%d pages=%d\n",
+               sgbuf->size, sgbuf->pages));
+       sglist_init(chip, pipe);
+       edge = PAGE_SIZE;
+       for (offs = page = per = 0; offs < params_buffer_bytes(hw_params);
+            per++) {
+               rest = params_period_bytes(hw_params);
+               if (offs + rest > params_buffer_bytes(hw_params))
+                       rest = params_buffer_bytes(hw_params) - offs;
+               while (rest) {
+                       if (rest <= edge - offs) {
+                               sglist_add_mapping(chip, pipe,
+                                                  snd_sgbuf_get_addr(sgbuf, offs),
+                                                  rest);
+                               sglist_add_irq(chip, pipe);
+                               offs += rest;
+                               rest = 0;
+                       } else {
+                               sglist_add_mapping(chip, pipe,
+                                                  snd_sgbuf_get_addr(sgbuf, offs),
+                                                  edge - offs);
+                               rest -= edge - offs;
+                               offs = edge;
+                       }
+                       if (offs == edge) {
+                               edge += PAGE_SIZE;
+                               page++;
+                       }
+               }
+       }
+
+       /* Close the ring buffer */
+       sglist_wrap(chip, pipe);
+
+       /* This stuff is used by the irq handler, so it must be
+        * initialized before chip->substream
+        */
+       chip->last_period[pipe_index] = 0;
+       pipe->last_counter = 0;
+       pipe->position = 0;
+       smp_wmb();
+       chip->substream[pipe_index] = substream;
+       chip->rate_set = 1;
+       spin_lock_irq(&chip->lock);
+       set_sample_rate(chip, hw_params->rate_num / hw_params->rate_den);
+       spin_unlock_irq(&chip->lock);
+       DE_HWP(("pcm_hw_params ok\n"));
+       return 0;
+}
+
+
+
+static int pcm_analog_in_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *hw_params)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+       return init_engine(substream, hw_params, px_analog_in(chip) +
+                       substream->number, params_channels(hw_params));
+}
+
+
+
+static int pcm_analog_out_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *hw_params)
+{
+       return init_engine(substream, hw_params, substream->number,
+                          params_channels(hw_params));
+}
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+static int pcm_digital_in_hw_params(struct snd_pcm_substream *substream,
+                                   struct snd_pcm_hw_params *hw_params)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+       return init_engine(substream, hw_params, px_digital_in(chip) +
+                       substream->number, params_channels(hw_params));
+}
+
+
+
+#ifndef ECHOCARD_HAS_VMIXER    /* See the note in snd_echo_new_pcm() */
+static int pcm_digital_out_hw_params(struct snd_pcm_substream *substream,
+                                    struct snd_pcm_hw_params *hw_params)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+
+       return init_engine(substream, hw_params, px_digital_out(chip) +
+                       substream->number, params_channels(hw_params));
+}
+#endif /* !ECHOCARD_HAS_VMIXER */
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+static int pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip;
+       struct audiopipe *pipe;
+
+       chip = snd_pcm_substream_chip(substream);
+       pipe = (struct audiopipe *) substream->runtime->private_data;
+
+       spin_lock_irq(&chip->lock);
+       if (pipe->index >= 0) {
+               DE_HWP(("pcm_hw_free(%d)\n", pipe->index));
+               free_pipes(chip, pipe);
+               chip->substream[pipe->index] = NULL;
+               pipe->index = -1;
+       }
+       spin_unlock_irq(&chip->lock);
+
+       DE_HWP(("pcm_hw_freed\n"));
+       snd_pcm_lib_free_pages(substream);
+       return 0;
+}
+
+
+
+static int pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct audioformat format;
+       int pipe_index = ((struct audiopipe *)runtime->private_data)->index;
+
+       DE_HWP(("Prepare rate=%d format=%d channels=%d\n",
+               runtime->rate, runtime->format, runtime->channels));
+       format.interleave = runtime->channels;
+       format.data_are_bigendian = 0;
+       format.mono_to_stereo = 0;
+       switch (runtime->format) {
+       case SNDRV_PCM_FORMAT_U8:
+               format.bits_per_sample = 8;
+               break;
+       case SNDRV_PCM_FORMAT_S16_LE:
+               format.bits_per_sample = 16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_3LE:
+               format.bits_per_sample = 24;
+               break;
+       case SNDRV_PCM_FORMAT_S32_BE:
+               format.data_are_bigendian = 1;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               format.bits_per_sample = 32;
+               break;
+       default:
+               DE_HWP(("Prepare error: unsupported format %d\n",
+                       runtime->format));
+               return -EINVAL;
+       }
+
+       snd_assert(pipe_index < px_num(chip), return -EINVAL);
+       snd_assert(is_pipe_allocated(chip, pipe_index), return -EINVAL);
+       set_audio_format(chip, pipe_index, &format);
+       return 0;
+}
+
+
+
+static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct echoaudio *chip = snd_pcm_substream_chip(substream);
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct audiopipe *pipe = runtime->private_data;
+       int i, err;
+       u32 channelmask = 0;
+       struct list_head *pos;
+       struct snd_pcm_substream *s;
+
+       snd_pcm_group_for_each(pos, substream) {
+               s = snd_pcm_group_substream_entry(pos);
+               for (i = 0; i < DSP_MAXPIPES; i++) {
+                       if (s == chip->substream[i]) {
+                               channelmask |= 1 << i;
+                               snd_pcm_trigger_done(s, substream);
+                       }
+               }
+       }
+
+       spin_lock(&chip->lock);
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               DE_ACT(("pcm_trigger start\n"));
+               for (i = 0; i < DSP_MAXPIPES; i++) {
+                       if (channelmask & (1 << i)) {
+                               pipe = chip->substream[i]->runtime->private_data;
+                               switch (pipe->state) {
+                               case PIPE_STATE_STOPPED:
+                                       chip->last_period[i] = 0;
+                                       pipe->last_counter = 0;
+                                       pipe->position = 0;
+                                       *pipe->dma_counter = 0;
+                               case PIPE_STATE_PAUSED:
+                                       pipe->state = PIPE_STATE_STARTED;
+                                       break;
+                               case PIPE_STATE_STARTED:
+                                       break;
+                               }
+                       }
+               }
+               err = start_transport(chip, channelmask,
+                                     chip->pipe_cyclic_mask);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               DE_ACT(("pcm_trigger stop\n"));
+               for (i = 0; i < DSP_MAXPIPES; i++) {
+                       if (channelmask & (1 << i)) {
+                               pipe = chip->substream[i]->runtime->private_data;
+                               pipe->state = PIPE_STATE_STOPPED;
+                       }
+               }
+               err = stop_transport(chip, channelmask);
+               break;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               DE_ACT(("pcm_trigger pause\n"));
+               for (i = 0; i < DSP_MAXPIPES; i++) {
+                       if (channelmask & (1 << i)) {
+                               pipe = chip->substream[i]->runtime->private_data;
+                               pipe->state = PIPE_STATE_PAUSED;
+                       }
+               }
+               err = pause_transport(chip, channelmask);
+               break;
+       default:
+               err = -EINVAL;
+       }
+       spin_unlock(&chip->lock);
+       return err;
+}
+
+
+
+static snd_pcm_uframes_t pcm_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct audiopipe *pipe = runtime->private_data;
+       size_t cnt, bufsize, pos;
+
+       cnt = le32_to_cpu(*pipe->dma_counter);
+       pipe->position += cnt - pipe->last_counter;
+       pipe->last_counter = cnt;
+       bufsize = substream->runtime->buffer_size;
+       pos = bytes_to_frames(substream->runtime, pipe->position);
+
+       while (pos >= bufsize) {
+               pipe->position -= frames_to_bytes(substream->runtime, bufsize);
+               pos -= bufsize;
+       }
+       return pos;
+}
+
+
+
+/* pcm *_ops structures */
+static struct snd_pcm_ops analog_playback_ops = {
+       .open = pcm_analog_out_open,
+       .close = pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = pcm_analog_out_hw_params,
+       .hw_free = pcm_hw_free,
+       .prepare = pcm_prepare,
+       .trigger = pcm_trigger,
+       .pointer = pcm_pointer,
+       .page = snd_pcm_sgbuf_ops_page,
+};
+static struct snd_pcm_ops analog_capture_ops = {
+       .open = pcm_analog_in_open,
+       .close = pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = pcm_analog_in_hw_params,
+       .hw_free = pcm_hw_free,
+       .prepare = pcm_prepare,
+       .trigger = pcm_trigger,
+       .pointer = pcm_pointer,
+       .page = snd_pcm_sgbuf_ops_page,
+};
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+#ifndef ECHOCARD_HAS_VMIXER
+static struct snd_pcm_ops digital_playback_ops = {
+       .open = pcm_digital_out_open,
+       .close = pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = pcm_digital_out_hw_params,
+       .hw_free = pcm_hw_free,
+       .prepare = pcm_prepare,
+       .trigger = pcm_trigger,
+       .pointer = pcm_pointer,
+       .page = snd_pcm_sgbuf_ops_page,
+};
+#endif /* !ECHOCARD_HAS_VMIXER */
+static struct snd_pcm_ops digital_capture_ops = {
+       .open = pcm_digital_in_open,
+       .close = pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = pcm_digital_in_hw_params,
+       .hw_free = pcm_hw_free,
+       .prepare = pcm_prepare,
+       .trigger = pcm_trigger,
+       .pointer = pcm_pointer,
+       .page = snd_pcm_sgbuf_ops_page,
+};
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+/* Preallocate memory only for the first substream because it's the most
+ * used one
+ */
+static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev)
+{
+       struct snd_pcm_substream *ss;
+       int stream, err;
+
+       for (stream = 0; stream < 2; stream++)
+               for (ss = pcm->streams[stream].substream; ss; ss = ss->next) {
+                       err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG,
+                                                           dev,
+                                                           ss->number ? 0 : 128<<10,
+                                                           256<<10);
+                       if (err < 0)
+                               return err;
+               }
+       return 0;
+}
+
+
+
+/*<--snd_echo_probe() */
+static int __devinit snd_echo_new_pcm(struct echoaudio *chip)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+#ifdef ECHOCARD_HAS_VMIXER
+       /* This card has a Vmixer, that is there is no direct mapping from PCM
+       streams to physical outputs. The user can mix the streams as he wishes
+       via control interface and it's possible to send any stream to any
+       output, thus it makes no sense to keep analog and digital outputs
+       separated */
+
+       /* PCM#0 Virtual outputs and analog inputs */
+       if ((err = snd_pcm_new(chip->card, "PCM", 0, num_pipes_out(chip),
+                               num_analog_busses_in(chip), &pcm)) < 0)
+               return err;
+       pcm->private_data = chip;
+       chip->analog_pcm = pcm;
+       strcpy(pcm->name, chip->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
+       if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+               return err;
+       DE_INIT(("Analog PCM ok\n"));
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+       /* PCM#1 Digital inputs, no outputs */
+       if ((err = snd_pcm_new(chip->card, "Digital PCM", 1, 0,
+                              num_digital_busses_in(chip), &pcm)) < 0)
+               return err;
+       pcm->private_data = chip;
+       chip->digital_pcm = pcm;
+       strcpy(pcm->name, chip->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
+       if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+               return err;
+       DE_INIT(("Digital PCM ok\n"));
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+#else /* ECHOCARD_HAS_VMIXER */
+
+       /* The card can manage substreams formed by analog and digital channels
+       at the same time, but I prefer to keep analog and digital channels
+       separated, because that mixed thing is confusing and useless. So we
+       register two PCM devices: */
+
+       /* PCM#0 Analog i/o */
+       if ((err = snd_pcm_new(chip->card, "Analog PCM", 0,
+                              num_analog_busses_out(chip),
+                              num_analog_busses_in(chip), &pcm)) < 0)
+               return err;
+       pcm->private_data = chip;
+       chip->analog_pcm = pcm;
+       strcpy(pcm->name, chip->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &analog_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &analog_capture_ops);
+       if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+               return err;
+       DE_INIT(("Analog PCM ok\n"));
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+       /* PCM#1 Digital i/o */
+       if ((err = snd_pcm_new(chip->card, "Digital PCM", 1,
+                              num_digital_busses_out(chip),
+                              num_digital_busses_in(chip), &pcm)) < 0)
+               return err;
+       pcm->private_data = chip;
+       chip->digital_pcm = pcm;
+       strcpy(pcm->name, chip->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &digital_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &digital_capture_ops);
+       if ((err = snd_echo_preallocate_pages(pcm, snd_dma_pci_data(chip->pci))) < 0)
+               return err;
+       DE_INIT(("Digital PCM ok\n"));
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+#endif /* ECHOCARD_HAS_VMIXER */
+
+       return 0;
+}
+
+
+
+
+/******************************************************************************
+       Control interface
+******************************************************************************/
+
+/******************* PCM output volume *******************/
+static int snd_echo_output_gain_info(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = num_busses_out(chip);
+       uinfo->value.integer.min = ECHOGAIN_MINOUT;
+       uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+       return 0;
+}
+
+static int snd_echo_output_gain_get(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       for (c = 0; c < num_busses_out(chip); c++)
+               ucontrol->value.integer.value[c] = chip->output_gain[c];
+       return 0;
+}
+
+static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c, changed, gain;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       spin_lock_irq(&chip->lock);
+       for (c = 0; c < num_busses_out(chip); c++) {
+               gain = ucontrol->value.integer.value[c];
+               /* Ignore out of range values */
+               if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+                       continue;
+               if (chip->output_gain[c] != gain) {
+                       set_output_gain(chip, c, gain);
+                       changed = 1;
+               }
+       }
+       if (changed)
+               update_output_line_level(chip);
+       spin_unlock_irq(&chip->lock);
+       return changed;
+}
+
+#ifdef ECHOCARD_HAS_VMIXER
+/* On Vmixer cards this one controls the line-out volume */
+static struct snd_kcontrol_new snd_echo_line_output_gain __devinitdata = {
+       .name = "Line Playback Volume",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_output_gain_info,
+       .get = snd_echo_output_gain_get,
+       .put = snd_echo_output_gain_put,
+};
+#else
+static struct snd_kcontrol_new snd_echo_pcm_output_gain __devinitdata = {
+       .name = "PCM Playback Volume",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_output_gain_info,
+       .get = snd_echo_output_gain_get,
+       .put = snd_echo_output_gain_put,
+};
+#endif
+
+
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+
+/******************* Analog input volume *******************/
+static int snd_echo_input_gain_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = num_analog_busses_in(chip);
+       uinfo->value.integer.min = ECHOGAIN_MININP;
+       uinfo->value.integer.max = ECHOGAIN_MAXINP;
+       return 0;
+}
+
+static int snd_echo_input_gain_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       for (c = 0; c < num_analog_busses_in(chip); c++)
+               ucontrol->value.integer.value[c] = chip->input_gain[c];
+       return 0;
+}
+
+static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c, gain, changed;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       spin_lock_irq(&chip->lock);
+       for (c = 0; c < num_analog_busses_in(chip); c++) {
+               gain = ucontrol->value.integer.value[c];
+               /* Ignore out of range values */
+               if (gain < ECHOGAIN_MININP || gain > ECHOGAIN_MAXINP)
+                       continue;
+               if (chip->input_gain[c] != gain) {
+                       set_input_gain(chip, c, gain);
+                       changed = 1;
+               }
+       }
+       if (changed)
+               update_input_line_level(chip);
+       spin_unlock_irq(&chip->lock);
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_line_input_gain __devinitdata = {
+       .name = "Line Capture Volume",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_input_gain_info,
+       .get = snd_echo_input_gain_get,
+       .put = snd_echo_input_gain_put,
+};
+
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+
+
+#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+
+/************ Analog output nominal level (+4dBu / -10dBV) ***************/
+static int snd_echo_output_nominal_info (struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = num_analog_busses_out(chip);
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_echo_output_nominal_get(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       for (c = 0; c < num_analog_busses_out(chip); c++)
+               ucontrol->value.integer.value[c] = chip->nominal_level[c];
+       return 0;
+}
+
+static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c, changed;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       spin_lock_irq(&chip->lock);
+       for (c = 0; c < num_analog_busses_out(chip); c++) {
+               if (chip->nominal_level[c] != ucontrol->value.integer.value[c]) {
+                       set_nominal_level(chip, c,
+                                         ucontrol->value.integer.value[c]);
+                       changed = 1;
+               }
+       }
+       if (changed)
+               update_output_line_level(chip);
+       spin_unlock_irq(&chip->lock);
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_output_nominal_level __devinitdata = {
+       .name = "Line Playback Switch (-10dBV)",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_output_nominal_info,
+       .get = snd_echo_output_nominal_get,
+       .put = snd_echo_output_nominal_put,
+};
+
+#endif /* ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL */
+
+
+
+#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+
+/*************** Analog input nominal level (+4dBu / -10dBV) ***************/
+static int snd_echo_input_nominal_info(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = num_analog_busses_in(chip);
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_echo_input_nominal_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       for (c = 0; c < num_analog_busses_in(chip); c++)
+               ucontrol->value.integer.value[c] =
+                       chip->nominal_level[bx_analog_in(chip) + c];
+       return 0;
+}
+
+static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int c, changed;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       spin_lock_irq(&chip->lock);
+       for (c = 0; c < num_analog_busses_in(chip); c++) {
+               if (chip->nominal_level[bx_analog_in(chip) + c] !=
+                   ucontrol->value.integer.value[c]) {
+                       set_nominal_level(chip, bx_analog_in(chip) + c,
+                                         ucontrol->value.integer.value[c]);
+                       changed = 1;
+               }
+       }
+       if (changed)
+               update_output_line_level(chip); /* "Output" is not a mistake
+                                                * here.
+                                                */
+       spin_unlock_irq(&chip->lock);
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_intput_nominal_level __devinitdata = {
+       .name = "Line Capture Switch (-10dBV)",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_input_nominal_info,
+       .get = snd_echo_input_nominal_get,
+       .put = snd_echo_input_nominal_put,
+};
+
+#endif /* ECHOCARD_HAS_INPUT_NOMINAL_LEVEL */
+
+
+
+#ifdef ECHOCARD_HAS_MONITOR
+
+/******************* Monitor mixer *******************/
+static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = ECHOGAIN_MINOUT;
+       uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+       uinfo->dimen.d[0] = num_busses_out(chip);
+       uinfo->dimen.d[1] = num_busses_in(chip);
+       return 0;
+}
+
+static int snd_echo_mixer_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] =
+               chip->monitor_gain[ucontrol->id.index / num_busses_in(chip)]
+                       [ucontrol->id.index % num_busses_in(chip)];
+       return 0;
+}
+
+static int snd_echo_mixer_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int changed,  gain;
+       short out, in;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       out = ucontrol->id.index / num_busses_in(chip);
+       in = ucontrol->id.index % num_busses_in(chip);
+       gain = ucontrol->value.integer.value[0];
+       if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+               return -EINVAL;
+       if (chip->monitor_gain[out][in] != gain) {
+               spin_lock_irq(&chip->lock);
+               set_monitor_gain(chip, out, in, gain);
+               update_output_line_level(chip);
+               spin_unlock_irq(&chip->lock);
+               changed = 1;
+       }
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_monitor_mixer __devinitdata = {
+       .name = "Monitor Mixer Volume",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_mixer_info,
+       .get = snd_echo_mixer_get,
+       .put = snd_echo_mixer_put,
+};
+
+#endif /* ECHOCARD_HAS_MONITOR */
+
+
+
+#ifdef ECHOCARD_HAS_VMIXER
+
+/******************* Vmixer *******************/
+static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = ECHOGAIN_MINOUT;
+       uinfo->value.integer.max = ECHOGAIN_MAXOUT;
+       uinfo->dimen.d[0] = num_busses_out(chip);
+       uinfo->dimen.d[1] = num_pipes_out(chip);
+       return 0;
+}
+
+static int snd_echo_vmixer_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] =
+               chip->vmixer_gain[ucontrol->id.index / num_pipes_out(chip)]
+                       [ucontrol->id.index % num_pipes_out(chip)];
+       return 0;
+}
+
+static int snd_echo_vmixer_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int gain, changed;
+       short vch, out;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       out = ucontrol->id.index / num_pipes_out(chip);
+       vch = ucontrol->id.index % num_pipes_out(chip);
+       gain = ucontrol->value.integer.value[0];
+       if (gain < ECHOGAIN_MINOUT || gain > ECHOGAIN_MAXOUT)
+               return -EINVAL;
+       if (chip->vmixer_gain[out][vch] != ucontrol->value.integer.value[0]) {
+               spin_lock_irq(&chip->lock);
+               set_vmixer_gain(chip, out, vch, ucontrol->value.integer.value[0]);
+               update_vmixer_level(chip);
+               spin_unlock_irq(&chip->lock);
+               changed = 1;
+       }
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_vmixer __devinitdata = {
+       .name = "VMixer Volume",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = snd_echo_vmixer_info,
+       .get = snd_echo_vmixer_get,
+       .put = snd_echo_vmixer_put,
+};
+
+#endif /* ECHOCARD_HAS_VMIXER */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+
+/******************* Digital mode switch *******************/
+static int snd_echo_digital_mode_info(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_info *uinfo)
+{
+       static char *names[4] = {
+               "S/PDIF Coaxial", "S/PDIF Optical", "ADAT Optical",
+               "S/PDIF Cdrom"
+       };
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->value.enumerated.items = chip->num_digital_modes;
+       uinfo->count = 1;
+       if (uinfo->value.enumerated.item >= chip->num_digital_modes)
+               uinfo->value.enumerated.item = chip->num_digital_modes - 1;
+       strcpy(uinfo->value.enumerated.name, names[
+                       chip->digital_mode_list[uinfo->value.enumerated.item]]);
+       return 0;
+}
+
+static int snd_echo_digital_mode_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int i, mode;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       mode = chip->digital_mode;
+       for (i = chip->num_digital_modes - 1; i >= 0; i--)
+               if (mode == chip->digital_mode_list[i]) {
+                       ucontrol->value.enumerated.item[0] = i;
+                       break;
+               }
+       return 0;
+}
+
+static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int changed;
+       unsigned short emode, dmode;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+
+       emode = ucontrol->value.enumerated.item[0];
+       if (emode >= chip->num_digital_modes)
+               return -EINVAL;
+       dmode = chip->digital_mode_list[emode];
+
+       if (dmode != chip->digital_mode) {
+               /* mode_mutex is required to make this operation atomic wrt
+               pcm_digital_*_open() and set_input_clock() functions. */
+               down(&chip->mode_mutex);
+
+               /* Do not allow the user to change the digital mode when a pcm
+               device is open because it also changes the number of channels
+               and the allowed sample rates */
+               if (atomic_read(&chip->opencount)) {
+                       changed = -EAGAIN;
+               } else {
+                       changed = set_digital_mode(chip, dmode);
+                       /* If we had to change the clock source, report it */
+                       if (changed > 0 && chip->clock_src_ctl) {
+                               snd_ctl_notify(chip->card,
+                                              SNDRV_CTL_EVENT_MASK_VALUE,
+                                              &chip->clock_src_ctl->id);
+                               DE_ACT(("SDM() =%d\n", changed));
+                       }
+                       if (changed >= 0)
+                               changed = 1;    /* No errors */
+               }
+               up(&chip->mode_mutex);
+       }
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_digital_mode_switch __devinitdata = {
+       .name = "Digital mode Switch",
+       .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+       .info = snd_echo_digital_mode_info,
+       .get = snd_echo_digital_mode_get,
+       .put = snd_echo_digital_mode_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+
+/******************* S/PDIF mode switch *******************/
+static int snd_echo_spdif_mode_info(struct snd_kcontrol *kcontrol,
+                                   struct snd_ctl_elem_info *uinfo)
+{
+       static char *names[2] = {"Consumer", "Professional"};
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->value.enumerated.items = 2;
+       uinfo->count = 1;
+       if (uinfo->value.enumerated.item)
+               uinfo->value.enumerated.item = 1;
+       strcpy(uinfo->value.enumerated.name,
+              names[uinfo->value.enumerated.item]);
+       return 0;
+}
+
+static int snd_echo_spdif_mode_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.enumerated.item[0] = !!chip->professional_spdif;
+       return 0;
+}
+
+static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int mode;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       mode = !!ucontrol->value.enumerated.item[0];
+       if (mode != chip->professional_spdif) {
+               spin_lock_irq(&chip->lock);
+               set_professional_spdif(chip, mode);
+               spin_unlock_irq(&chip->lock);
+               return 1;
+       }
+       return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_spdif_mode_switch __devinitdata = {
+       .name = "S/PDIF mode Switch",
+       .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+       .info = snd_echo_spdif_mode_info,
+       .get = snd_echo_spdif_mode_get,
+       .put = snd_echo_spdif_mode_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_IO */
+
+
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+
+/******************* Select input clock source *******************/
+static int snd_echo_clock_source_info(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_info *uinfo)
+{
+       static char *names[8] = {
+               "Internal", "Word", "Super", "S/PDIF", "ADAT", "ESync",
+               "ESync96", "MTC"
+       };
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->value.enumerated.items = chip->num_clock_sources;
+       uinfo->count = 1;
+       if (uinfo->value.enumerated.item >= chip->num_clock_sources)
+               uinfo->value.enumerated.item = chip->num_clock_sources - 1;
+       strcpy(uinfo->value.enumerated.name, names[
+                       chip->clock_source_list[uinfo->value.enumerated.item]]);
+       return 0;
+}
+
+static int snd_echo_clock_source_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int i, clock;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       clock = chip->input_clock;
+
+       for (i = 0; i < chip->num_clock_sources; i++)
+               if (clock == chip->clock_source_list[i])
+                       ucontrol->value.enumerated.item[0] = i;
+
+       return 0;
+}
+
+static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int changed;
+       unsigned int eclock, dclock;
+
+       changed = 0;
+       chip = snd_kcontrol_chip(kcontrol);
+       eclock = ucontrol->value.enumerated.item[0];
+       if (eclock >= chip->input_clock_types)
+               return -EINVAL;
+       dclock = chip->clock_source_list[eclock];
+       if (chip->input_clock != dclock) {
+               down(&chip->mode_mutex);
+               spin_lock_irq(&chip->lock);
+               if ((changed = set_input_clock(chip, dclock)) == 0)
+                       changed = 1;    /* no errors */
+               spin_unlock_irq(&chip->lock);
+               up(&chip->mode_mutex);
+       }
+
+       if (changed < 0)
+               DE_ACT(("seticlk val%d err 0x%x\n", dclock, changed));
+
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_clock_source_switch __devinitdata = {
+       .name = "Sample Clock Source",
+       .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+       .info = snd_echo_clock_source_info,
+       .get = snd_echo_clock_source_get,
+       .put = snd_echo_clock_source_put,
+};
+
+#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
+
+
+
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+
+/******************* Phantom power switch *******************/
+static int snd_echo_phantom_power_info(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_echo_phantom_power_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = chip->phantom_power;
+       return 0;
+}
+
+static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+       int power, changed = 0;
+
+       power = !!ucontrol->value.integer.value[0];
+       if (chip->phantom_power != power) {
+               spin_lock_irq(&chip->lock);
+               changed = set_phantom_power(chip, power);
+               spin_unlock_irq(&chip->lock);
+               if (changed == 0)
+                       changed = 1;    /* no errors */
+       }
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_phantom_power_switch __devinitdata = {
+       .name = "Phantom power Switch",
+       .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+       .info = snd_echo_phantom_power_info,
+       .get = snd_echo_phantom_power_get,
+       .put = snd_echo_phantom_power_put,
+};
+
+#endif /* ECHOCARD_HAS_PHANTOM_POWER */
+
+
+
+#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+
+/******************* Digital input automute switch *******************/
+static int snd_echo_automute_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_echo_automute_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = chip->digital_in_automute;
+       return 0;
+}
+
+static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip = snd_kcontrol_chip(kcontrol);
+       int automute, changed = 0;
+
+       automute = !!ucontrol->value.integer.value[0];
+       if (chip->digital_in_automute != automute) {
+               spin_lock_irq(&chip->lock);
+               changed = set_input_auto_mute(chip, automute);
+               spin_unlock_irq(&chip->lock);
+               if (changed == 0)
+                       changed = 1;    /* no errors */
+       }
+       return changed;
+}
+
+static struct snd_kcontrol_new snd_echo_automute_switch __devinitdata = {
+       .name = "Digital Capture Switch (automute)",
+       .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+       .info = snd_echo_automute_info,
+       .get = snd_echo_automute_get,
+       .put = snd_echo_automute_put,
+};
+
+#endif /* ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE */
+
+
+
+/******************* VU-meters switch *******************/
+static int snd_echo_vumeters_switch_info(struct snd_kcontrol *kcontrol,
+                                        struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       spin_lock_irq(&chip->lock);
+       set_meters_on(chip, ucontrol->value.integer.value[0]);
+       spin_unlock_irq(&chip->lock);
+       return 1;
+}
+
+static struct snd_kcontrol_new snd_echo_vumeters_switch __devinitdata = {
+       .name = "VU-meters Switch",
+       .iface = SNDRV_CTL_ELEM_IFACE_CARD,
+       .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+       .info = snd_echo_vumeters_switch_info,
+       .put = snd_echo_vumeters_switch_put,
+};
+
+
+
+/***** Read VU-meters (input, output, analog and digital together) *****/
+static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 96;
+       uinfo->value.integer.min = ECHOGAIN_MINOUT;
+       uinfo->value.integer.max = 0;
+#ifdef ECHOCARD_HAS_VMIXER
+       uinfo->dimen.d[0] = 3;  /* Out, In, Virt */
+#else
+       uinfo->dimen.d[0] = 2;  /* Out, In */
+#endif
+       uinfo->dimen.d[1] = 16; /* 16 channels */
+       uinfo->dimen.d[2] = 2;  /* 0=level, 1=peak */
+       return 0;
+}
+
+static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       get_audio_meters(chip, ucontrol->value.integer.value);
+       return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_vumeters __devinitdata = {
+       .name = "VU-meters",
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info = snd_echo_vumeters_info,
+       .get = snd_echo_vumeters_get,
+};
+
+
+
+/*** Channels info - it exports informations about the number of channels ***/
+static int snd_echo_channels_info_info(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       struct echoaudio *chip;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 6;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1 << ECHO_CLOCK_NUMBER;
+       return 0;
+}
+
+static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct echoaudio *chip;
+       int detected, clocks, bit, src;
+
+       chip = snd_kcontrol_chip(kcontrol);
+       ucontrol->value.integer.value[0] = num_busses_in(chip);
+       ucontrol->value.integer.value[1] = num_analog_busses_in(chip);
+       ucontrol->value.integer.value[2] = num_busses_out(chip);
+       ucontrol->value.integer.value[3] = num_analog_busses_out(chip);
+       ucontrol->value.integer.value[4] = num_pipes_out(chip);
+
+       /* Compute the bitmask of the currently valid input clocks */
+       detected = detect_input_clocks(chip);
+       clocks = 0;
+       src = chip->num_clock_sources - 1;
+       for (bit = ECHO_CLOCK_NUMBER - 1; bit >= 0; bit--)
+               if (detected & (1 << bit))
+                       for (; src >= 0; src--)
+                               if (bit == chip->clock_source_list[src]) {
+                                       clocks |= 1 << src;
+                                       break;
+                               }
+       ucontrol->value.integer.value[5] = clocks;
+
+       return 0;
+}
+
+static struct snd_kcontrol_new snd_echo_channels_info __devinitdata = {
+       .name = "Channels info",
+       .iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
+       .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info = snd_echo_channels_info_info,
+       .get = snd_echo_channels_info_get,
+};
+
+
+
+
+/******************************************************************************
+       IRQ Handler
+******************************************************************************/
+
+static irqreturn_t snd_echo_interrupt(int irq, void *dev_id,
+                                     struct pt_regs *regs)
+{
+       struct echoaudio *chip = dev_id;
+       struct snd_pcm_substream *substream;
+       int period, ss, st;
+
+       spin_lock(&chip->lock);
+       st = service_irq(chip);
+       if (st < 0) {
+               spin_unlock(&chip->lock);
+               return IRQ_NONE;
+       }
+       /* The hardware doesn't tell us which substream caused the irq,
+       thus we have to check all running substreams. */
+       for (ss = 0; ss < DSP_MAXPIPES; ss++) {
+               if ((substream = chip->substream[ss])) {
+                       period = pcm_pointer(substream) /
+                               substream->runtime->period_size;
+                       if (period != chip->last_period[ss]) {
+                               chip->last_period[ss] = period;
+                               spin_unlock(&chip->lock);
+                               snd_pcm_period_elapsed(substream);
+                               spin_lock(&chip->lock);
+                       }
+               }
+       }
+       spin_unlock(&chip->lock);
+
+#ifdef ECHOCARD_HAS_MIDI
+       if (st > 0 && chip->midi_in) {
+               snd_rawmidi_receive(chip->midi_in, chip->midi_buffer, st);
+               DE_MID(("rawmidi_iread=%d\n", st));
+       }
+#endif
+       return IRQ_HANDLED;
+}
+
+
+
+
+/******************************************************************************
+       Module construction / destruction
+******************************************************************************/
+
+static int snd_echo_free(struct echoaudio *chip)
+{
+       DE_INIT(("Stop DSP...\n"));
+       if (chip->comm_page) {
+               rest_in_peace(chip);
+               snd_dma_free_pages(&chip->commpage_dma_buf);
+       }
+       DE_INIT(("Stopped.\n"));
+
+       if (chip->irq >= 0)
+               free_irq(chip->irq, (void *)chip);
+
+       if (chip->dsp_registers)
+               iounmap(chip->dsp_registers);
+
+       if (chip->iores) {
+               release_resource(chip->iores);
+               kfree_nocheck(chip->iores);
+       }
+       DE_INIT(("MMIO freed.\n"));
+
+       pci_disable_device(chip->pci);
+
+       /* release chip data */
+       kfree(chip);
+       DE_INIT(("Chip freed.\n"));
+       return 0;
+}
+
+
+
+static int snd_echo_dev_free(struct snd_device *device)
+{
+       struct echoaudio *chip = device->device_data;
+
+       DE_INIT(("snd_echo_dev_free()...\n"));
+       return snd_echo_free(chip);
+}
+
+
+
+/* <--snd_echo_probe() */
+static __devinit int snd_echo_create(struct snd_card *card,
+                                    struct pci_dev *pci,
+                                    struct echoaudio **rchip)
+{
+       struct echoaudio *chip;
+       int err;
+       size_t sz;
+       static struct snd_device_ops ops = {
+               .dev_free = snd_echo_dev_free,
+       };
+
+       *rchip = NULL;
+
+       pci_write_config_byte(pci, PCI_LATENCY_TIMER, 0xC0);
+
+       if ((err = pci_enable_device(pci)) < 0)
+               return err;
+       pci_set_master(pci);
+
+       /* allocate a chip-specific data */
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip) {
+               pci_disable_device(pci);
+               return -ENOMEM;
+       }
+       DE_INIT(("chip=%p\n", chip));
+
+       spin_lock_init(&chip->lock);
+       chip->card = card;
+       chip->pci = pci;
+       chip->irq = -1;
+
+       /* PCI resource allocation */
+       chip->dsp_registers_phys = pci_resource_start(pci, 0);
+       sz = pci_resource_len(pci, 0);
+       if (sz > PAGE_SIZE)
+               sz = PAGE_SIZE;         /* We map only the required part */
+
+       if ((chip->iores = request_mem_region(chip->dsp_registers_phys, sz,
+                                             ECHOCARD_NAME)) == NULL) {
+               snd_echo_free(chip);
+               snd_printk(KERN_ERR "cannot get memory region\n");
+               return -EBUSY;
+       }
+       chip->dsp_registers = (volatile u32 __iomem *)
+               ioremap_nocache(chip->dsp_registers_phys, sz);
+
+       if (request_irq(pci->irq, snd_echo_interrupt, SA_INTERRUPT | SA_SHIRQ,
+                                               ECHOCARD_NAME, (void *)chip)) {
+               snd_echo_free(chip);
+               snd_printk(KERN_ERR "cannot grab irq\n");
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+       DE_INIT(("pci=%p irq=%d subdev=%04x Init hardware...\n",
+                chip->pci, chip->irq, chip->pci->subsystem_device));
+
+       /* Create the DSP comm page - this is the area of memory used for most
+       of the communication with the DSP, which accesses it via bus mastering */
+       if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci),
+                               sizeof(struct comm_page),
+                               &chip->commpage_dma_buf) < 0) {
+               snd_echo_free(chip);
+               snd_printk(KERN_ERR "cannot allocate the comm page\n");
+               return -ENOMEM;
+       }
+       chip->comm_page_phys = chip->commpage_dma_buf.addr;
+       chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
+
+       err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
+       if (err) {
+               DE_INIT(("init_hw err=%d\n", err));
+               snd_echo_free(chip);
+               return err;
+       }
+       DE_INIT(("Card init OK\n"));
+
+       if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) {
+               snd_echo_free(chip);
+               return err;
+       }
+       atomic_set(&chip->opencount, 0);
+       init_MUTEX(&chip->mode_mutex);
+       chip->can_set_rate = 1;
+       *rchip = chip;
+       /* Init done ! */
+       return 0;
+}
+
+
+
+/* constructor */
+static int __devinit snd_echo_probe(struct pci_dev *pci,
+                                   const struct pci_device_id *pci_id)
+{
+       static int dev;
+       struct snd_card *card;
+       struct echoaudio *chip;
+       char *dsp;
+       int i, err;
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) {
+               dev++;
+               return -ENOENT;
+       }
+
+       DE_INIT(("Echoaudio driver starting...\n"));
+       i = 0;
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL)
+               return -ENOMEM;
+
+       if ((err = snd_echo_create(card, pci, &chip)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+
+       strcpy(card->driver, "Echo_" ECHOCARD_NAME);
+       strcpy(card->shortname, chip->card_name);
+
+       dsp = "56301";
+       if (pci_id->device == 0x3410)
+               dsp = "56361";
+
+       sprintf(card->longname, "%s rev.%d (DSP%s) at 0x%lx irq %i",
+               card->shortname, pci_id->subdevice & 0x000f, dsp,
+               chip->dsp_registers_phys, chip->irq);
+
+       if ((err = snd_echo_new_pcm(chip)) < 0) {
+               snd_printk(KERN_ERR "new pcm error %d\n", err);
+               snd_card_free(card);
+               return err;
+       }
+
+#ifdef ECHOCARD_HAS_MIDI
+       if (chip->has_midi) {   /* Some Mia's do not have midi */
+               if ((err = snd_echo_midi_create(card, chip)) < 0) {
+                       snd_printk(KERN_ERR "new midi error %d\n", err);
+                       snd_card_free(card);
+                       return err;
+               }
+       }
+#endif
+
+#ifdef ECHOCARD_HAS_VMIXER
+       snd_echo_vmixer.count = num_pipes_out(chip) * num_busses_out(chip);
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_output_gain, chip))) < 0)
+               goto ctl_error;
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vmixer, chip))) < 0)
+               goto ctl_error;
+#else
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_pcm_output_gain, chip))) < 0)
+               goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_line_input_gain, chip))) < 0)
+               goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+       if (!chip->hasnt_input_nominal_level)
+               if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_intput_nominal_level, chip))) < 0)
+                       goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_output_nominal_level, chip))) < 0)
+               goto ctl_error;
+#endif
+
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters_switch, chip))) < 0)
+               goto ctl_error;
+
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_vumeters, chip))) < 0)
+               goto ctl_error;
+
+#ifdef ECHOCARD_HAS_MONITOR
+       snd_echo_monitor_mixer.count = num_busses_in(chip) * num_busses_out(chip);
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_monitor_mixer, chip))) < 0)
+               goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_automute_switch, chip))) < 0)
+               goto ctl_error;
+#endif
+
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_channels_info, chip))) < 0)
+               goto ctl_error;
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+       /* Creates a list of available digital modes */
+       chip->num_digital_modes = 0;
+       for (i = 0; i < 6; i++)
+               if (chip->digital_modes & (1 << i))
+                       chip->digital_mode_list[chip->num_digital_modes++] = i;
+
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_digital_mode_switch, chip))) < 0)
+               goto ctl_error;
+#endif /* ECHOCARD_HAS_DIGITAL_MODE_SWITCH */
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+       /* Creates a list of available clock sources */
+       chip->num_clock_sources = 0;
+       for (i = 0; i < 10; i++)
+               if (chip->input_clock_types & (1 << i))
+                       chip->clock_source_list[chip->num_clock_sources++] = i;
+
+       if (chip->num_clock_sources > 1) {
+               chip->clock_src_ctl = snd_ctl_new1(&snd_echo_clock_source_switch, chip);
+               if ((err = snd_ctl_add(chip->card, chip->clock_src_ctl)) < 0)
+                       goto ctl_error;
+       }
+#endif /* ECHOCARD_HAS_EXTERNAL_CLOCK */
+
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+       if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_spdif_mode_switch, chip))) < 0)
+               goto ctl_error;
+#endif
+
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+       if (chip->has_phantom_power)
+               if ((err = snd_ctl_add(chip->card, snd_ctl_new1(&snd_echo_phantom_power_switch, chip))) < 0)
+                       goto ctl_error;
+#endif
+
+       if ((err = snd_card_register(card)) < 0) {
+               snd_card_free(card);
+               goto ctl_error;
+       }
+       snd_printk(KERN_INFO "Card registered: %s\n", card->longname);
+
+       pci_set_drvdata(pci, chip);
+       dev++;
+       return 0;
+
+ctl_error:
+       snd_printk(KERN_ERR "new control error %d\n", err);
+       snd_card_free(card);
+       return err;
+}
+
+
+
+static void __devexit snd_echo_remove(struct pci_dev *pci)
+{
+       struct echoaudio *chip;
+
+       chip = pci_get_drvdata(pci);
+       if (chip)
+               snd_card_free(chip->card);
+       pci_set_drvdata(pci, NULL);
+}
+
+
+
+/******************************************************************************
+       Everything starts and ends here
+******************************************************************************/
+
+/* pci_driver definition */
+static struct pci_driver driver = {
+       .name = "Echoaudio " ECHOCARD_NAME,
+       .id_table = snd_echo_ids,
+       .probe = snd_echo_probe,
+       .remove = __devexit_p(snd_echo_remove),
+};
+
+
+
+/* initialization of the module */
+static int __init alsa_card_echo_init(void)
+{
+       return pci_register_driver(&driver);
+}
+
+
+
+/* clean up the module */
+static void __exit alsa_card_echo_exit(void)
+{
+       pci_unregister_driver(&driver);
+}
+
+
+module_init(alsa_card_echo_init)
+module_exit(alsa_card_echo_exit)
diff --git a/sound/pci/echoaudio/echoaudio.h b/sound/pci/echoaudio/echoaudio.h
new file mode 100644 (file)
index 0000000..7e88c96
--- /dev/null
@@ -0,0 +1,590 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+ ****************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+ ****************************************************************************
+
+
+   Here's a block diagram of how most of the cards work:
+
+                  +-----------+
+           record |           |<-------------------- Inputs
+          <-------|           |        |
+     PCI          | Transport |        |
+     bus          |  engine   |       \|/
+          ------->|           |    +-------+
+            play  |           |--->|monitor|-------> Outputs
+                  +-----------+    | mixer |
+                                   +-------+
+
+   The lines going to and from the PCI bus represent "pipes".  A pipe performs
+   audio transport - moving audio data to and from buffers on the host via
+   bus mastering.
+
+   The inputs and outputs on the right represent input and output "busses."
+   A bus is a physical, real connection to the outside world.  An example
+   of a bus would be the 1/4" analog connectors on the back of Layla or
+   an RCA S/PDIF connector.
+
+   For most cards, there is a one-to-one correspondence between outputs
+   and busses; that is, each individual pipe is hard-wired to a single bus.
+
+   Cards that work this way are Darla20, Gina20, Layla20, Darla24, Gina24,
+   Layla24, Mona, and Indigo.
+
+
+   Mia has a feature called "virtual outputs."
+
+
+                  +-----------+
+           record |           |<----------------------------- Inputs
+          <-------|           |                  |
+     PCI          | Transport |                  |
+     bus          |  engine   |                 \|/
+          ------->|           |   +------+   +-------+
+            play  |           |-->|vmixer|-->|monitor|-------> Outputs
+                  +-----------+   +------+   | mixer |
+                                             +-------+
+
+
+   Obviously, the difference here is the box labeled "vmixer."  Vmixer is
+   short for "virtual output mixer."  For Mia, pipes are *not* hard-wired
+   to a single bus; the vmixer lets you mix any pipe to any bus in any
+   combination.
+
+   Note, however, that the left-hand side of the diagram is unchanged.
+   Transport works exactly the same way - the difference is in the mixer stage.
+
+
+   Pipes and busses are numbered starting at zero.
+
+
+
+   Pipe index
+   ==========
+
+   A number of calls in CEchoGals refer to a "pipe index".  A pipe index is
+   a unique number for a pipe that unambiguously refers to a playback or record
+   pipe.  Pipe indices are numbered starting with analog outputs, followed by
+   digital outputs, then analog inputs, then digital inputs.
+
+   Take Gina24 as an example:
+
+   Pipe index
+
+   0-7            Analog outputs (0 .. FirstDigitalBusOut-1)
+   8-15           Digital outputs (FirstDigitalBusOut .. NumBussesOut-1)
+   16-17          Analog inputs
+   18-25          Digital inputs
+
+
+   You get the pipe index by calling CEchoGals::OpenAudio; the other transport
+   functions take the pipe index as a parameter.  If you need a pipe index for
+   some other reason, use the handy Makepipe_index method.
+
+
+   Some calls take a CChannelMask parameter; CChannelMask is a handy way to
+   group pipe indices.
+
+
+
+   Digital mode switch
+   ===================
+
+   Some cards (right now, Gina24, Layla24, and Mona) have a Digital Mode Switch
+   or DMS.  Cards with a DMS can be set to one of three mutually exclusive
+   digital modes: S/PDIF RCA, S/PDIF optical, or ADAT optical.
+
+   This may create some confusion since ADAT optical is 8 channels wide and
+   S/PDIF is only two channels wide.  Gina24, Layla24, and Mona handle this
+   by acting as if they always have 8 digital outs and ins.  If you are in
+   either S/PDIF mode, the last 6 channels don't do anything - data sent
+   out these channels is thrown away and you will always record zeros.
+
+   Note that with Gina24, Layla24, and Mona, sample rates above 50 kHz are
+   only available if you have the card configured for S/PDIF optical or S/PDIF
+   RCA.
+
+
+
+   Double speed mode
+   =================
+
+   Some of the cards support 88.2 kHz and 96 kHz sampling (Darla24, Gina24,
+   Layla24, Mona, Mia, and Indigo).  For these cards, the driver sometimes has
+   to worry about "double speed mode"; double speed mode applies whenever the
+   sampling rate is above 50 kHz.
+
+   For instance, Mona and Layla24 support word clock sync.  However, they
+   actually support two different word clock modes - single speed (below
+   50 kHz) and double speed (above 50 kHz).  The hardware detects if a single
+   or double speed word clock signal is present; the generic code uses that
+   information to determine which mode to use.
+
+   The generic code takes care of all this for you.
+*/
+
+
+#ifndef _ECHOAUDIO_H_
+#define _ECHOAUDIO_H_
+
+
+#define TRUE 1
+#define FALSE 0
+
+#include "echoaudio_dsp.h"
+
+
+
+/***********************************************************************
+
+       PCI configuration space
+
+***********************************************************************/
+
+/*
+ * PCI vendor ID and device IDs for the hardware
+ */
+#define VENDOR_ID              0x1057
+#define DEVICE_ID_56301                0x1801
+#define DEVICE_ID_56361                0x3410
+#define SUBVENDOR_ID           0xECC0
+
+
+/*
+ * Valid Echo PCI subsystem card IDs
+ */
+#define DARLA20                        0x0010
+#define GINA20                 0x0020
+#define LAYLA20                        0x0030
+#define DARLA24                        0x0040
+#define GINA24                 0x0050
+#define LAYLA24                        0x0060
+#define MONA                   0x0070
+#define MIA                    0x0080
+#define INDIGO                 0x0090
+#define INDIGO_IO              0x00a0
+#define INDIGO_DJ              0x00b0
+#define ECHO3G                 0x0100
+
+
+/************************************************************************
+
+       Array sizes and so forth
+
+***********************************************************************/
+
+/*
+ * Sizes
+ */
+#define ECHO_MAXAUDIOINPUTS    32      /* Max audio input channels */
+#define ECHO_MAXAUDIOOUTPUTS   32      /* Max audio output channels */
+#define ECHO_MAXAUDIOPIPES     32      /* Max number of input and output
+                                        * pipes */
+#define E3G_MAX_OUTPUTS                16
+#define ECHO_MAXMIDIJACKS      1       /* Max MIDI ports */
+#define ECHO_MIDI_QUEUE_SZ     512     /* Max MIDI input queue entries */
+#define ECHO_MTC_QUEUE_SZ      32      /* Max MIDI time code input queue
+                                        * entries */
+
+/*
+ * MIDI activity indicator timeout
+ */
+#define MIDI_ACTIVITY_TIMEOUT_USEC     200000
+
+
+/****************************************************************************
+   Clocks
+
+*****************************************************************************/
+
+/*
+ * Clock numbers
+ */
+#define ECHO_CLOCK_INTERNAL            0
+#define ECHO_CLOCK_WORD                        1
+#define ECHO_CLOCK_SUPER               2
+#define ECHO_CLOCK_SPDIF               3
+#define ECHO_CLOCK_ADAT                        4
+#define ECHO_CLOCK_ESYNC               5
+#define ECHO_CLOCK_ESYNC96             6
+#define ECHO_CLOCK_MTC                 7
+#define ECHO_CLOCK_NUMBER              8
+#define ECHO_CLOCKS                    0xffff
+
+/*
+ * Clock bit numbers - used to report capabilities and whatever clocks
+ * are being detected dynamically.
+ */
+#define ECHO_CLOCK_BIT_INTERNAL                (1 << ECHO_CLOCK_INTERNAL)
+#define ECHO_CLOCK_BIT_WORD            (1 << ECHO_CLOCK_WORD)
+#define ECHO_CLOCK_BIT_SUPER           (1 << ECHO_CLOCK_SUPER)
+#define ECHO_CLOCK_BIT_SPDIF           (1 << ECHO_CLOCK_SPDIF)
+#define ECHO_CLOCK_BIT_ADAT            (1 << ECHO_CLOCK_ADAT)
+#define ECHO_CLOCK_BIT_ESYNC           (1 << ECHO_CLOCK_ESYNC)
+#define ECHO_CLOCK_BIT_ESYNC96         (1 << ECHO_CLOCK_ESYNC96)
+#define ECHO_CLOCK_BIT_MTC             (1<<ECHO_CLOCK_MTC)
+
+
+/***************************************************************************
+
+   Digital modes
+
+****************************************************************************/
+
+/*
+ * Digital modes for Mona, Layla24, and Gina24
+ */
+#define DIGITAL_MODE_NONE                      0xFF
+#define DIGITAL_MODE_SPDIF_RCA                 0
+#define DIGITAL_MODE_SPDIF_OPTICAL             1
+#define DIGITAL_MODE_ADAT                      2
+#define DIGITAL_MODE_SPDIF_CDROM               3
+#define DIGITAL_MODES                          4
+
+/*
+ * Digital mode capability masks
+ */
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA    (1 << DIGITAL_MODE_SPDIF_RCA)
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL        (1 << DIGITAL_MODE_SPDIF_OPTICAL)
+#define ECHOCAPS_HAS_DIGITAL_MODE_ADAT         (1 << DIGITAL_MODE_ADAT)
+#define ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM  (1 << DIGITAL_MODE_SPDIF_CDROM)
+
+
+#define EXT_3GBOX_NC                   0x01    /* 3G box not connected */
+#define EXT_3GBOX_NOT_SET              0x02    /* 3G box not detected yet */
+
+
+#define ECHOGAIN_MUTED         (-128)  /* Minimum possible gain */
+#define ECHOGAIN_MINOUT                (-128)  /* Min output gain (dB) */
+#define ECHOGAIN_MAXOUT                (6)     /* Max output gain (dB) */
+#define ECHOGAIN_MININP                (-50)   /* Min input gain (0.5 dB) */
+#define ECHOGAIN_MAXINP                (50)    /* Max input gain (0.5 dB) */
+
+#define PIPE_STATE_STOPPED     0       /* Pipe has been reset */
+#define PIPE_STATE_PAUSED      1       /* Pipe has been stopped */
+#define PIPE_STATE_STARTED     2       /* Pipe has been started */
+#define PIPE_STATE_PENDING     3       /* Pipe has pending start */
+
+
+/* Debug initialization */
+#ifdef CONFIG_SND_DEBUG
+#define DE_INIT(x) snd_printk x
+#else
+#define DE_INIT(x)
+#endif
+
+/* Debug hw_params callbacks */
+#ifdef CONFIG_SND_DEBUG
+#define DE_HWP(x) snd_printk x
+#else
+#define DE_HWP(x)
+#endif
+
+/* Debug normal activity (open, start, stop...) */
+#ifdef CONFIG_SND_DEBUG
+#define DE_ACT(x) snd_printk x
+#else
+#define DE_ACT(x)
+#endif
+
+/* Debug midi activity */
+#ifdef CONFIG_SND_DEBUG
+#define DE_MID(x) snd_printk x
+#else
+#define DE_MID(x)
+#endif
+
+
+struct audiopipe {
+       volatile u32 *dma_counter;      /* Commpage register that contains
+                                        * the current dma position
+                                        * (lower 32 bits only)
+                                        */
+       u32 last_counter;               /* The last position, which is used
+                                        * to compute...
+                                        */
+       u32 position;                   /* ...the number of bytes tranferred
+                                        * by the DMA engine, modulo the
+                                        * buffer size
+                                        */
+       short index;                    /* Index of the first channel or <0
+                                        * if hw is not configured yet
+                                        */
+       short interleave;
+       struct snd_dma_buffer sgpage;   /* Room for the scatter-gather list */
+       struct snd_pcm_hardware hw;
+       struct snd_pcm_hw_constraint_list constr;
+       short sglist_head;
+       char state;                     /* pipe state */
+};
+
+
+struct audioformat {
+       u8 interleave;                  /* How the data is arranged in memory:
+                                        * mono = 1, stereo = 2, ...
+                                        */
+       u8 bits_per_sample;             /* 8, 16, 24, 32 (24 bits left aligned) */
+       char mono_to_stereo;            /* Only used if interleave is 1 and
+                                        * if this is an output pipe.
+                                        */
+       char data_are_bigendian;        /* 1 = big endian, 0 = little endian */
+};
+
+
+struct echoaudio {
+       spinlock_t lock;
+       struct snd_pcm_substream *substream[DSP_MAXPIPES];
+       int last_period[DSP_MAXPIPES];
+       struct semaphore mode_mutex;
+       u16 num_digital_modes, digital_mode_list[6];
+       u16 num_clock_sources, clock_source_list[10];
+       atomic_t opencount;
+       struct snd_kcontrol *clock_src_ctl;
+       struct snd_pcm *analog_pcm, *digital_pcm;
+       struct snd_card *card;
+       const char *card_name;
+       struct pci_dev *pci;
+       unsigned long dsp_registers_phys;
+       struct resource *iores;
+       struct snd_dma_buffer commpage_dma_buf;
+       int irq;
+#ifdef ECHOCARD_HAS_MIDI
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_substream *midi_in, *midi_out;
+#endif
+       struct timer_list timer;
+       char tinuse;                            /* Timer in use */
+       char midi_full;                         /* MIDI output buffer is full */
+       char can_set_rate;
+       char rate_set;
+
+       /* This stuff is used mainly by the lowlevel code */
+       struct comm_page *comm_page;    /* Virtual address of the memory
+                                        * seen by DSP
+                                        */
+       u32 pipe_alloc_mask;            /* Bitmask of allocated pipes */
+       u32 pipe_cyclic_mask;           /* Bitmask of pipes with cyclic
+                                        * buffers
+                                        */
+       u32 sample_rate;                /* Card sample rate in Hz */
+       u8 digital_mode;                /* Current digital mode
+                                        * (see DIGITAL_MODE_*)
+                                        */
+       u8 spdif_status;                /* Gina20, Darla20, Darla24 - only */
+       u8 clock_state;                 /* Gina20, Darla20, Darla24 - only */
+       u8 input_clock;                 /* Currently selected sample clock
+                                        * source
+                                        */
+       u8 output_clock;                /* Layla20 only */
+       char meters_enabled;            /* VU-meters status */
+       char asic_loaded;               /* Set TRUE when ASIC loaded */
+       char bad_board;                 /* Set TRUE if DSP won't load */
+       char professional_spdif;        /* 0 = consumer; 1 = professional */
+       char non_audio_spdif;           /* 3G - only */
+       char digital_in_automute;       /* Gina24, Layla24, Mona - only */
+       char has_phantom_power;
+       char hasnt_input_nominal_level; /* Gina3G */
+       char phantom_power;             /* Gina3G - only */
+       char has_midi;
+       char midi_input_enabled;
+
+#ifdef ECHOCARD_ECHO3G
+       /* External module -dependent pipe and bus indexes */
+       char px_digital_out, px_analog_in, px_digital_in, px_num;
+       char bx_digital_out, bx_analog_in, bx_digital_in, bx_num;
+#endif
+
+       char nominal_level[ECHO_MAXAUDIOPIPES]; /* True == -10dBV
+                                                * False == +4dBu */
+       s8 input_gain[ECHO_MAXAUDIOINPUTS];     /* Input level -50..+50
+                                                * unit is 0.5dB */
+       s8 output_gain[ECHO_MAXAUDIOOUTPUTS];   /* Output level -128..+6 dB
+                                                * (-128=muted) */
+       s8 monitor_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOINPUTS];
+               /* -128..+6 dB */
+       s8 vmixer_gain[ECHO_MAXAUDIOOUTPUTS][ECHO_MAXAUDIOOUTPUTS];
+               /* -128..+6 dB */
+
+       u16 digital_modes;              /* Bitmask of supported modes
+                                        * (see ECHOCAPS_HAS_DIGITAL_MODE_*) */
+       u16 input_clock_types;          /* Suppoted input clock types */
+       u16 output_clock_types;         /* Suppoted output clock types -
+                                        * Layla20 only */
+       u16 device_id, subdevice_id;
+       u16 *dsp_code;                  /* Current DSP code loaded,
+                                        * NULL if nothing loaded */
+       const struct firmware *dsp_code_to_load;/* DSP code to load */
+       const struct firmware *asic_code;       /* Current ASIC code */
+       u32 comm_page_phys;                     /* Physical address of the
+                                                * memory seen by DSP */
+       volatile u32 __iomem *dsp_registers;    /* DSP's register base */
+       u32 active_mask;                        /* Chs. active mask or
+                                                * punks out */
+
+#ifdef ECHOCARD_HAS_MIDI
+       u16 mtc_state;                          /* State for MIDI input parsing state machine */
+       u8 midi_buffer[MIDI_IN_BUFFER_SIZE];
+#endif
+};
+
+
+static int init_dsp_comm_page(struct echoaudio *chip);
+static int init_line_levels(struct echoaudio *chip);
+static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe);
+static int load_firmware(struct echoaudio *chip);
+static int wait_handshake(struct echoaudio *chip);
+static int send_vector(struct echoaudio *chip, u32 command);
+static int get_firmware(const struct firmware **fw_entry,
+                       const struct firmware *frm, struct echoaudio *chip);
+static void free_firmware(const struct firmware *fw_entry);
+
+#ifdef ECHOCARD_HAS_MIDI
+static int enable_midi_input(struct echoaudio *chip, char enable);
+static int midi_service_irq(struct echoaudio *chip);
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+                                         struct echoaudio *chip);
+#endif
+
+
+static inline void clear_handshake(struct echoaudio *chip)
+{
+       chip->comm_page->handshake = 0;
+}
+
+static inline u32 get_dsp_register(struct echoaudio *chip, u32 index)
+{
+       return readl(&chip->dsp_registers[index]);
+}
+
+static inline void set_dsp_register(struct echoaudio *chip, u32 index,
+                                   u32 value)
+{
+       writel(value, &chip->dsp_registers[index]);
+}
+
+
+/* Pipe and bus indexes. PX_* and BX_* are defined as chip->px_* and chip->bx_*
+for 3G cards because they depend on the external box. They are integer
+constants for all other cards.
+Never use those defines directly, use the following functions instead. */
+
+static inline int px_digital_out(const struct echoaudio *chip)
+{
+       return PX_DIGITAL_OUT;
+}
+
+static inline int px_analog_in(const struct echoaudio *chip)
+{
+       return PX_ANALOG_IN;
+}
+
+static inline int px_digital_in(const struct echoaudio *chip)
+{
+       return PX_DIGITAL_IN;
+}
+
+static inline int px_num(const struct echoaudio *chip)
+{
+       return PX_NUM;
+}
+
+static inline int bx_digital_out(const struct echoaudio *chip)
+{
+       return BX_DIGITAL_OUT;
+}
+
+static inline int bx_analog_in(const struct echoaudio *chip)
+{
+       return BX_ANALOG_IN;
+}
+
+static inline int bx_digital_in(const struct echoaudio *chip)
+{
+       return BX_DIGITAL_IN;
+}
+
+static inline int bx_num(const struct echoaudio *chip)
+{
+       return BX_NUM;
+}
+
+static inline int num_pipes_out(const struct echoaudio *chip)
+{
+       return px_analog_in(chip);
+}
+
+static inline int num_pipes_in(const struct echoaudio *chip)
+{
+       return px_num(chip) - px_analog_in(chip);
+}
+
+static inline int num_busses_out(const struct echoaudio *chip)
+{
+       return bx_analog_in(chip);
+}
+
+static inline int num_busses_in(const struct echoaudio *chip)
+{
+       return bx_num(chip) - bx_analog_in(chip);
+}
+
+static inline int num_analog_busses_out(const struct echoaudio *chip)
+{
+       return bx_digital_out(chip);
+}
+
+static inline int num_analog_busses_in(const struct echoaudio *chip)
+{
+       return bx_digital_in(chip) - bx_analog_in(chip);
+}
+
+static inline int num_digital_busses_out(const struct echoaudio *chip)
+{
+       return num_busses_out(chip) - num_analog_busses_out(chip);
+}
+
+static inline int num_digital_busses_in(const struct echoaudio *chip)
+{
+       return num_busses_in(chip) - num_analog_busses_in(chip);
+}
+
+/* The monitor array is a one-dimensional array; compute the offset
+ * into the array */
+static inline int monitor_index(const struct echoaudio *chip, int out, int in)
+{
+       return out * num_busses_in(chip) + in;
+}
+
+
+#ifndef pci_device
+#define pci_device(chip) (&chip->pci->dev)
+#endif
+
+
+#endif /* _ECHOAUDIO_H_ */
diff --git a/sound/pci/echoaudio/echoaudio_3g.c b/sound/pci/echoaudio/echoaudio_3g.c
new file mode 100644 (file)
index 0000000..9f439ea
--- /dev/null
@@ -0,0 +1,431 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+
+/* These functions are common for all "3G" cards */
+
+
+static int check_asic_status(struct echoaudio *chip)
+{
+       u32 box_status;
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->comm_page->ext_box_status =
+               __constant_cpu_to_le32(E3G_ASIC_NOT_LOADED);
+       chip->asic_loaded = FALSE;
+       clear_handshake(chip);
+       send_vector(chip, DSP_VC_TEST_ASIC);
+
+       if (wait_handshake(chip)) {
+               chip->dsp_code = NULL;
+               return -EIO;
+       }
+
+       box_status = le32_to_cpu(chip->comm_page->ext_box_status);
+       DE_INIT(("box_status=%x\n", box_status));
+       if (box_status == E3G_ASIC_NOT_LOADED)
+               return -ENODEV;
+
+       chip->asic_loaded = TRUE;
+       return box_status & E3G_BOX_TYPE_MASK;
+}
+
+
+
+static inline u32 get_frq_reg(struct echoaudio *chip)
+{
+       return le32_to_cpu(chip->comm_page->e3g_frq_register);
+}
+
+
+
+/* Most configuration of 3G cards is accomplished by writing the control
+register. write_control_reg sends the new control register value to the DSP. */
+static int write_control_reg(struct echoaudio *chip, u32 ctl, u32 frq,
+                            char force)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+
+       DE_ACT(("WriteControlReg: Setting 0x%x, 0x%x\n", ctl, frq));
+
+       ctl = cpu_to_le32(ctl);
+       frq = cpu_to_le32(frq);
+
+       if (ctl != chip->comm_page->control_register ||
+           frq != chip->comm_page->e3g_frq_register || force) {
+               chip->comm_page->e3g_frq_register = frq;
+               chip->comm_page->control_register = ctl;
+               clear_handshake(chip);
+               return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
+       }
+
+       DE_ACT(("WriteControlReg: not written, no change\n"));
+       return 0;
+}
+
+
+
+/* Set the digital mode - currently for Gina24, Layla24, Mona, 3G */
+static int set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+       u8 previous_mode;
+       int err, i, o;
+
+       /* All audio channels must be closed before changing the digital mode */
+       snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+
+       snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+
+       previous_mode = chip->digital_mode;
+       err = dsp_set_digital_mode(chip, mode);
+
+       /* If we successfully changed the digital mode from or to ADAT,
+        * then make sure all output, input and monitor levels are
+        * updated by the DSP comm object. */
+       if (err >= 0 && previous_mode != mode &&
+           (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
+               spin_lock_irq(&chip->lock);
+               for (o = 0; o < num_busses_out(chip); o++)
+                       for (i = 0; i < num_busses_in(chip); i++)
+                               set_monitor_gain(chip, o, i,
+                                                chip->monitor_gain[o][i]);
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+               for (i = 0; i < num_busses_in(chip); i++)
+                       set_input_gain(chip, i, chip->input_gain[i]);
+               update_input_line_level(chip);
+#endif
+
+               for (o = 0; o < num_busses_out(chip); o++)
+                       set_output_gain(chip, o, chip->output_gain[o]);
+               update_output_line_level(chip);
+               spin_unlock_irq(&chip->lock);
+       }
+
+       return err;
+}
+
+
+
+static u32 set_spdif_bits(struct echoaudio *chip, u32 control_reg, u32 rate)
+{
+       control_reg &= E3G_SPDIF_FORMAT_CLEAR_MASK;
+
+       switch (rate) {
+       case 32000 :
+               control_reg |= E3G_SPDIF_SAMPLE_RATE0 | E3G_SPDIF_SAMPLE_RATE1;
+               break;
+       case 44100 :
+               if (chip->professional_spdif)
+                       control_reg |= E3G_SPDIF_SAMPLE_RATE0;
+               break;
+       case 48000 :
+               control_reg |= E3G_SPDIF_SAMPLE_RATE1;
+               break;
+       }
+
+       if (chip->professional_spdif)
+               control_reg |= E3G_SPDIF_PRO_MODE;
+
+       if (chip->non_audio_spdif)
+               control_reg |= E3G_SPDIF_NOT_AUDIO;
+
+       control_reg |= E3G_SPDIF_24_BIT | E3G_SPDIF_TWO_CHANNEL |
+               E3G_SPDIF_COPY_PERMIT;
+
+       return control_reg;
+}
+
+
+
+/* Set the S/PDIF output format */
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+       u32 control_reg;
+
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       chip->professional_spdif = prof;
+       control_reg = set_spdif_bits(chip, control_reg, chip->sample_rate);
+       return write_control_reg(chip, control_reg, get_frq_reg(chip), 0);
+}
+
+
+
+/* detect_input_clocks() returns a bitmask consisting of all the input clocks
+currently connected to the hardware; this changes as the user connects and
+disconnects clock inputs. You should use this information to determine which
+clocks the user is allowed to select. */
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock
+        * detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD)
+               clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+       switch(chip->digital_mode) {
+       case DIGITAL_MODE_SPDIF_RCA:
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+               if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF)
+                       clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+               break;
+       case DIGITAL_MODE_ADAT:
+               if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_ADAT)
+                       clock_bits |= ECHO_CLOCK_BIT_ADAT;
+               break;
+       }
+
+       return clock_bits;
+}
+
+
+
+static int load_asic(struct echoaudio *chip)
+{
+       int box_type, err;
+
+       if (chip->asic_loaded)
+               return 0;
+
+       /* Give the DSP a few milliseconds to settle down */
+       mdelay(2);
+
+       err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC,
+                               &card_fw[FW_3G_ASIC]);
+       if (err < 0)
+               return err;
+
+       chip->asic_code = &card_fw[FW_3G_ASIC];
+
+       /* Now give the new ASIC a little time to set up */
+       mdelay(2);
+       /* See if it worked */
+       box_type = check_asic_status(chip);
+
+       /* Set up the control register if the load succeeded -
+        * 48 kHz, internal clock, S/PDIF RCA mode */
+       if (box_type >= 0) {
+               err = write_control_reg(chip, E3G_48KHZ,
+                                       E3G_FREQ_REG_DEFAULT, TRUE);
+               if (err < 0)
+                       return err;
+       }
+
+       return box_type;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg, clock, base_rate, frq_reg;
+
+       /* Only set the clock for internal mode. */
+       if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+               DE_ACT(("set_sample_rate: Cannot set sample rate - "
+                       "clock not set to CLK_CLOCKININTERNAL\n"));
+               /* Save the rate anyhow */
+               chip->comm_page->sample_rate = cpu_to_le32(rate);
+               chip->sample_rate = rate;
+               set_input_clock(chip, chip->input_clock);
+               return 0;
+       }
+
+       snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+                  return -EINVAL);
+
+       clock = 0;
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= E3G_CLOCK_CLEAR_MASK;
+
+       switch (rate) {
+       case 96000:
+               clock = E3G_96KHZ;
+               break;
+       case 88200:
+               clock = E3G_88KHZ;
+               break;
+       case 48000:
+               clock = E3G_48KHZ;
+               break;
+       case 44100:
+               clock = E3G_44KHZ;
+               break;
+       case 32000:
+               clock = E3G_32KHZ;
+               break;
+       default:
+               clock = E3G_CONTINUOUS_CLOCK;
+               if (rate > 50000)
+                       clock |= E3G_DOUBLE_SPEED_MODE;
+               break;
+       }
+
+       control_reg |= clock;
+       control_reg = set_spdif_bits(chip, control_reg, rate);
+
+       base_rate = rate;
+       if (base_rate > 50000)
+               base_rate /= 2;
+       if (base_rate < 32000)
+               base_rate = 32000;
+
+       frq_reg = E3G_MAGIC_NUMBER / base_rate - 2;
+       if (frq_reg > E3G_FREQ_REG_MAX)
+               frq_reg = E3G_FREQ_REG_MAX;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP */
+       chip->sample_rate = rate;
+       DE_ACT(("SetSampleRate: %d clock %x\n", rate, control_reg));
+
+       /* Tell the DSP about it - DSP reads both control reg & freq reg */
+       return write_control_reg(chip, control_reg, frq_reg, 0);
+}
+
+
+
+/* Set the sample clock source to internal, S/PDIF, ADAT */
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       u32 control_reg, clocks_from_dsp;
+
+       DE_ACT(("set_input_clock:\n"));
+
+       /* Mask off the clock select bits */
+       control_reg = le32_to_cpu(chip->comm_page->control_register) &
+               E3G_CLOCK_CLEAR_MASK;
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       switch (clock) {
+       case ECHO_CLOCK_INTERNAL:
+               DE_ACT(("Set Echo3G clock to INTERNAL\n"));
+               chip->input_clock = ECHO_CLOCK_INTERNAL;
+               return set_sample_rate(chip, chip->sample_rate);
+       case ECHO_CLOCK_SPDIF:
+               if (chip->digital_mode == DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               DE_ACT(("Set Echo3G clock to SPDIF\n"));
+               control_reg |= E3G_SPDIF_CLOCK;
+               if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_SPDIF96)
+                       control_reg |= E3G_DOUBLE_SPEED_MODE;
+               else
+                       control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_ADAT:
+               if (chip->digital_mode != DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               DE_ACT(("Set Echo3G clock to ADAT\n"));
+               control_reg |= E3G_ADAT_CLOCK;
+               control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_WORD:
+               DE_ACT(("Set Echo3G clock to WORD\n"));
+               control_reg |= E3G_WORD_CLOCK;
+               if (clocks_from_dsp & E3G_CLOCK_DETECT_BIT_WORD96)
+                       control_reg |= E3G_DOUBLE_SPEED_MODE;
+               else
+                       control_reg &= ~E3G_DOUBLE_SPEED_MODE;
+               break;
+       default:
+               DE_ACT(("Input clock 0x%x not supported for Echo3G\n", clock));
+               return -EINVAL;
+       }
+
+       chip->input_clock = clock;
+       return write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+       u32 control_reg;
+       int err, incompatible_clock;
+
+       /* Set clock to "internal" if it's not compatible with the new mode */
+       incompatible_clock = FALSE;
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+       case DIGITAL_MODE_SPDIF_RCA:
+               if (chip->input_clock == ECHO_CLOCK_ADAT)
+                       incompatible_clock = TRUE;
+               break;
+       case DIGITAL_MODE_ADAT:
+               if (chip->input_clock == ECHO_CLOCK_SPDIF)
+                       incompatible_clock = TRUE;
+               break;
+       default:
+               DE_ACT(("Digital mode not supported: %d\n", mode));
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&chip->lock);
+
+       if (incompatible_clock) {
+               chip->sample_rate = 48000;
+               set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+       }
+
+       /* Clear the current digital mode */
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= E3G_DIGITAL_MODE_CLEAR_MASK;
+
+       /* Tweak the control reg */
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+               control_reg |= E3G_SPDIF_OPTICAL_MODE;
+               break;
+       case DIGITAL_MODE_SPDIF_RCA:
+               /* E3G_SPDIF_OPTICAL_MODE bit cleared */
+               break;
+       case DIGITAL_MODE_ADAT:
+               control_reg |= E3G_ADAT_MODE;
+               control_reg &= ~E3G_DOUBLE_SPEED_MODE;  /* @@ useless */
+               break;
+       }
+
+       err = write_control_reg(chip, control_reg, get_frq_reg(chip), 1);
+       spin_unlock_irq(&chip->lock);
+       if (err < 0)
+               return err;
+       chip->digital_mode = mode;
+
+       DE_ACT(("set_digital_mode(%d)\n", chip->digital_mode));
+       return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.c b/sound/pci/echoaudio/echoaudio_dsp.c
new file mode 100644 (file)
index 0000000..42afa83
--- /dev/null
@@ -0,0 +1,1125 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+#if PAGE_SIZE < 4096
+#error PAGE_SIZE is < 4k
+#endif
+
+static int restore_dsp_rettings(struct echoaudio *chip);
+
+
+/* Some vector commands involve the DSP reading or writing data to and from the
+comm page; if you send one of these commands to the DSP, it will complete the
+command and then write a non-zero value to the Handshake field in the
+comm page.  This function waits for the handshake to show up. */
+static int wait_handshake(struct echoaudio *chip)
+{
+       int i;
+
+       /* Wait up to 10ms for the handshake from the DSP */
+       for (i = 0; i < HANDSHAKE_TIMEOUT; i++) {
+               /* Look for the handshake value */
+               if (chip->comm_page->handshake) {
+                       /*if (i)  DE_ACT(("Handshake time: %d\n", i));*/
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       snd_printk(KERN_ERR "wait_handshake(): Timeout waiting for DSP\n");
+       return -EBUSY;
+}
+
+
+
+/* Much of the interaction between the DSP and the driver is done via vector
+commands; send_vector writes a vector command to the DSP.  Typically, this
+causes the DSP to read or write fields in the comm page.
+PCI posting is not required thanks to the handshake logic. */
+static int send_vector(struct echoaudio *chip, u32 command)
+{
+       int i;
+
+       wmb();  /* Flush all pending writes before sending the command */
+
+       /* Wait up to 100ms for the "vector busy" bit to be off */
+       for (i = 0; i < VECTOR_BUSY_TIMEOUT; i++) {
+               if (!(get_dsp_register(chip, CHI32_VECTOR_REG) &
+                     CHI32_VECTOR_BUSY)) {
+                       set_dsp_register(chip, CHI32_VECTOR_REG, command);
+                       /*if (i)  DE_ACT(("send_vector time: %d\n", i));*/
+                       return 0;
+               }
+               udelay(1);
+       }
+
+       DE_ACT((KERN_ERR "timeout on send_vector\n"));
+       return -EBUSY;
+}
+
+
+
+/* write_dsp writes a 32-bit value to the DSP; this is used almost
+exclusively for loading the DSP. */
+static int write_dsp(struct echoaudio *chip, u32 data)
+{
+       u32 status, i;
+
+       for (i = 0; i < 10000000; i++) {        /* timeout = 10s */
+               status = get_dsp_register(chip, CHI32_STATUS_REG);
+               if ((status & CHI32_STATUS_HOST_WRITE_EMPTY) != 0) {
+                       set_dsp_register(chip, CHI32_DATA_REG, data);
+                       wmb();                  /* write it immediately */
+                       return 0;
+               }
+               udelay(1);
+               cond_resched();
+       }
+
+       chip->bad_board = TRUE;         /* Set TRUE until DSP re-loaded */
+       DE_ACT((KERN_ERR "write_dsp: Set bad_board to TRUE\n"));
+       return -EIO;
+}
+
+
+
+/* read_dsp reads a 32-bit value from the DSP; this is used almost
+exclusively for loading the DSP and checking the status of the ASIC. */
+static int read_dsp(struct echoaudio *chip, u32 *data)
+{
+       u32 status, i;
+
+       for (i = 0; i < READ_DSP_TIMEOUT; i++) {
+               status = get_dsp_register(chip, CHI32_STATUS_REG);
+               if ((status & CHI32_STATUS_HOST_READ_FULL) != 0) {
+                       *data = get_dsp_register(chip, CHI32_DATA_REG);
+                       return 0;
+               }
+               udelay(1);
+               cond_resched();
+       }
+
+       chip->bad_board = TRUE;         /* Set TRUE until DSP re-loaded */
+       DE_INIT((KERN_ERR "read_dsp: Set bad_board to TRUE\n"));
+       return -EIO;
+}
+
+
+
+/****************************************************************************
+       Firmware loading functions
+ ****************************************************************************/
+
+/* This function is used to read back the serial number from the DSP;
+this is triggered by the SET_COMMPAGE_ADDR command.
+Only some early Echogals products have serial numbers in the ROM;
+the serial number is not used, but you still need to do this as
+part of the DSP load process. */
+static int read_sn(struct echoaudio *chip)
+{
+       int i;
+       u32 sn[6];
+
+       for (i = 0; i < 5; i++) {
+               if (read_dsp(chip, &sn[i])) {
+                       snd_printk(KERN_ERR "Failed to read serial number\n");
+                       return -EIO;
+               }
+       }
+       DE_INIT(("Read serial number %08x %08x %08x %08x %08x\n",
+                sn[0], sn[1], sn[2], sn[3], sn[4]));
+       return 0;
+}
+
+
+
+#ifndef ECHOCARD_HAS_ASIC
+/* This card has no ASIC, just return ok */
+static inline int check_asic_status(struct echoaudio *chip)
+{
+       chip->asic_loaded = TRUE;
+       return 0;
+}
+
+#endif /* !ECHOCARD_HAS_ASIC */
+
+
+
+#ifdef ECHOCARD_HAS_ASIC
+
+/* Load ASIC code - done after the DSP is loaded */
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+                            const struct firmware *asic)
+{
+       const struct firmware *fw;
+       int err;
+       u32 i, size;
+       u8 *code;
+
+       if ((err = get_firmware(&fw, asic, chip)) < 0) {
+               snd_printk(KERN_WARNING "Firmware not found !\n");
+               return err;
+       }
+
+       code = (u8 *)fw->data;
+       size = fw->size;
+
+       /* Send the "Here comes the ASIC" command */
+       if (write_dsp(chip, cmd) < 0)
+               goto la_error;
+
+       /* Write length of ASIC file in bytes */
+       if (write_dsp(chip, size) < 0)
+               goto la_error;
+
+       for (i = 0; i < size; i++) {
+               if (write_dsp(chip, code[i]) < 0)
+                       goto la_error;
+       }
+
+       DE_INIT(("ASIC loaded\n"));
+       free_firmware(fw);
+       return 0;
+
+la_error:
+       DE_INIT(("failed on write_dsp\n"));
+       free_firmware(fw);
+       return -EIO;
+}
+
+#endif /* ECHOCARD_HAS_ASIC */
+
+
+
+#ifdef DSP_56361
+
+/* Install the resident loader for 56361 DSPs;  The resident loader is on
+the EPROM on the board for 56301 DSP. The resident loader is a tiny little
+program that is used to load the real DSP code. */
+static int install_resident_loader(struct echoaudio *chip)
+{
+       u32 address;
+       int index, words, i;
+       u16 *code;
+       u32 status;
+       const struct firmware *fw;
+
+       /* 56361 cards only!  This check is required by the old 56301-based
+       Mona and Gina24 */
+       if (chip->device_id != DEVICE_ID_56361)
+               return 0;
+
+       /* Look to see if the resident loader is present.  If the resident
+       loader is already installed, host flag 5 will be on. */
+       status = get_dsp_register(chip, CHI32_STATUS_REG);
+       if (status & CHI32_STATUS_REG_HF5) {
+               DE_INIT(("Resident loader already installed; status is 0x%x\n",
+                        status));
+               return 0;
+       }
+
+       if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) {
+               snd_printk(KERN_WARNING "Firmware not found !\n");
+               return i;
+       }
+
+       /* The DSP code is an array of 16 bit words.  The array is divided up
+       into sections.  The first word of each section is the size in words,
+       followed by the section type.
+       Since DSP addresses and data are 24 bits wide, they each take up two
+       16 bit words in the array.
+       This is a lot like the other loader loop, but it's not a loop, you
+       don't write the memory type, and you don't write a zero at the end. */
+
+       /* Set DSP format bits for 24 bit mode */
+       set_dsp_register(chip, CHI32_CONTROL_REG,
+                        get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900);
+
+       code = (u16 *)fw->data;
+
+       /* Skip the header section; the first word in the array is the size
+       of the first section, so the first real section of code is pointed
+       to by Code[0]. */
+       index = code[0];
+
+       /* Skip the section size, LRS block type, and DSP memory type */
+       index += 3;
+
+       /* Get the number of DSP words to write */
+       words = code[index++];
+
+       /* Get the DSP address for this block; 24 bits, so build from two words */
+       address = ((u32)code[index] << 16) + code[index + 1];
+       index += 2;
+
+       /* Write the count to the DSP */
+       if (write_dsp(chip, words)) {
+               DE_INIT(("install_resident_loader: Failed to write word count!\n"));
+               goto irl_error;
+       }
+       /* Write the DSP address */
+       if (write_dsp(chip, address)) {
+               DE_INIT(("install_resident_loader: Failed to write DSP address!\n"));
+               goto irl_error;
+       }
+       /* Write out this block of code to the DSP */
+       for (i = 0; i < words; i++) {
+               u32 data;
+
+               data = ((u32)code[index] << 16) + code[index + 1];
+               if (write_dsp(chip, data)) {
+                       DE_INIT(("install_resident_loader: Failed to write DSP code\n"));
+                       goto irl_error;
+               }
+               index += 2;
+       }
+
+       /* Wait for flag 5 to come up */
+       for (i = 0; i < 200; i++) {     /* Timeout is 50us * 200 = 10ms */
+               udelay(50);
+               status = get_dsp_register(chip, CHI32_STATUS_REG);
+               if (status & CHI32_STATUS_REG_HF5)
+                       break;
+       }
+
+       if (i == 200) {
+               DE_INIT(("Resident loader failed to set HF5\n"));
+               goto irl_error;
+       }
+
+       DE_INIT(("Resident loader successfully installed\n"));
+       free_firmware(fw);
+       return 0;
+
+irl_error:
+       free_firmware(fw);
+       return -EIO;
+}
+
+#endif /* DSP_56361 */
+
+
+static int load_dsp(struct echoaudio *chip, u16 *code)
+{
+       u32 address, data;
+       int index, words, i;
+
+       if (chip->dsp_code == code) {
+               DE_INIT(("DSP is already loaded!\n"));
+               return 0;
+       }
+       chip->bad_board = TRUE;         /* Set TRUE until DSP loaded */
+       chip->dsp_code = NULL;          /* Current DSP code not loaded */
+       chip->asic_loaded = FALSE;      /* Loading the DSP code will reset the ASIC */
+
+       DE_INIT(("load_dsp: Set bad_board to TRUE\n"));
+
+       /* If this board requires a resident loader, install it. */
+#ifdef DSP_56361
+       if ((i = install_resident_loader(chip)) < 0)
+               return i;
+#endif
+
+       /* Send software reset command */
+       if (send_vector(chip, DSP_VC_RESET) < 0) {
+               DE_INIT(("LoadDsp: send_vector DSP_VC_RESET failed, Critical Failure\n"));
+               return -EIO;
+       }
+       /* Delay 10us */
+       udelay(10);
+
+       /* Wait 10ms for HF3 to indicate that software reset is complete */
+       for (i = 0; i < 1000; i++) {    /* Timeout is 10us * 1000 = 10ms */
+               if (get_dsp_register(chip, CHI32_STATUS_REG) &
+                   CHI32_STATUS_REG_HF3)
+                       break;
+               udelay(10);
+       }
+
+       if (i == 1000) {
+               DE_INIT(("load_dsp: Timeout waiting for CHI32_STATUS_REG_HF3\n"));
+               return -EIO;
+       }
+
+       /* Set DSP format bits for 24 bit mode now that soft reset is done */
+       set_dsp_register(chip, CHI32_CONTROL_REG,
+                        get_dsp_register(chip, CHI32_CONTROL_REG) | 0x900);
+
+       /* Main loader loop */
+
+       index = code[0];
+       for (;;) {
+               int block_type, mem_type;
+
+               /* Total Block Size */
+               index++;
+
+               /* Block Type */
+               block_type = code[index];
+               if (block_type == 4)    /* We're finished */
+                       break;
+
+               index++;
+
+               /* Memory Type  P=0,X=1,Y=2 */
+               mem_type = code[index++];
+
+               /* Block Code Size */
+               words = code[index++];
+               if (words == 0)         /* We're finished */
+                       break;
+
+               /* Start Address */
+               address = ((u32)code[index] << 16) + code[index + 1];
+               index += 2;
+
+               if (write_dsp(chip, words) < 0) {
+                       DE_INIT(("load_dsp: failed to write number of DSP words\n"));
+                       return -EIO;
+               }
+               if (write_dsp(chip, address) < 0) {
+                       DE_INIT(("load_dsp: failed to write DSP address\n"));
+                       return -EIO;
+               }
+               if (write_dsp(chip, mem_type) < 0) {
+                       DE_INIT(("load_dsp: failed to write DSP memory type\n"));
+                       return -EIO;
+               }
+               /* Code */
+               for (i = 0; i < words; i++, index+=2) {
+                       data = ((u32)code[index] << 16) + code[index + 1];
+                       if (write_dsp(chip, data) < 0) {
+                               DE_INIT(("load_dsp: failed to write DSP data\n"));
+                               return -EIO;
+                       }
+               }
+       }
+
+       if (write_dsp(chip, 0) < 0) {   /* We're done!!! */
+               DE_INIT(("load_dsp: Failed to write final zero\n"));
+               return -EIO;
+       }
+       udelay(10);
+
+       for (i = 0; i < 5000; i++) {    /* Timeout is 100us * 5000 = 500ms */
+               /* Wait for flag 4 - indicates that the DSP loaded OK */
+               if (get_dsp_register(chip, CHI32_STATUS_REG) &
+                   CHI32_STATUS_REG_HF4) {
+                       set_dsp_register(chip, CHI32_CONTROL_REG,
+                                        get_dsp_register(chip, CHI32_CONTROL_REG) & ~0x1b00);
+
+                       if (write_dsp(chip, DSP_FNC_SET_COMMPAGE_ADDR) < 0) {
+                               DE_INIT(("load_dsp: Failed to write DSP_FNC_SET_COMMPAGE_ADDR\n"));
+                               return -EIO;
+                       }
+
+                       if (write_dsp(chip, chip->comm_page_phys) < 0) {
+                               DE_INIT(("load_dsp: Failed to write comm page address\n"));
+                               return -EIO;
+                       }
+
+                       /* Get the serial number via slave mode.
+                       This is triggered by the SET_COMMPAGE_ADDR command.
+                       We don't actually use the serial number but we have to
+                       get it as part of the DSP init voodoo. */
+                       if (read_sn(chip) < 0) {
+                               DE_INIT(("load_dsp: Failed to read serial number\n"));
+                               return -EIO;
+                       }
+
+                       chip->dsp_code = code;          /* Show which DSP code loaded */
+                       chip->bad_board = FALSE;        /* DSP OK */
+                       DE_INIT(("load_dsp: OK!\n"));
+                       return 0;
+               }
+               udelay(100);
+       }
+
+       DE_INIT(("load_dsp: DSP load timed out waiting for HF4\n"));
+       return -EIO;
+}
+
+
+
+/* load_firmware takes care of loading the DSP and any ASIC code. */
+static int load_firmware(struct echoaudio *chip)
+{
+       const struct firmware *fw;
+       int box_type, err;
+
+       snd_assert(chip->dsp_code_to_load && chip->comm_page, return -EPERM);
+
+       /* See if the ASIC is present and working - only if the DSP is already loaded */
+       if (chip->dsp_code) {
+               if ((box_type = check_asic_status(chip)) >= 0)
+                       return box_type;
+               /* ASIC check failed; force the DSP to reload */
+               chip->dsp_code = NULL;
+       }
+
+       if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0)
+               return err;
+       err = load_dsp(chip, (u16 *)fw->data);
+       free_firmware(fw);
+       if (err < 0)
+               return err;
+
+       if ((box_type = load_asic(chip)) < 0)
+               return box_type;        /* error */
+
+       if ((err = restore_dsp_rettings(chip)) < 0)
+               return err;
+
+       return box_type;
+}
+
+
+
+/****************************************************************************
+       Mixer functions
+ ****************************************************************************/
+
+#if defined(ECHOCARD_HAS_INPUT_NOMINAL_LEVEL) || \
+       defined(ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL)
+
+/* Set the nominal level for an input or output bus (true = -10dBV, false = +4dBu) */
+static int set_nominal_level(struct echoaudio *chip, u16 index, char consumer)
+{
+       snd_assert(index < num_busses_out(chip) + num_busses_in(chip),
+                  return -EINVAL);
+
+       /* Wait for the handshake (OK even if ASIC is not loaded) */
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->nominal_level[index] = consumer;
+
+       if (consumer)
+               chip->comm_page->nominal_level_mask |= cpu_to_le32(1 << index);
+       else
+               chip->comm_page->nominal_level_mask &= ~cpu_to_le32(1 << index);
+
+       return 0;
+}
+
+#endif /* ECHOCARD_HAS_*_NOMINAL_LEVEL */
+
+
+
+/* Set the gain for a single physical output channel (dB). */
+static int set_output_gain(struct echoaudio *chip, u16 channel, s8 gain)
+{
+       snd_assert(channel < num_busses_out(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       /* Save the new value */
+       chip->output_gain[channel] = gain;
+       chip->comm_page->line_out_level[channel] = gain;
+       return 0;
+}
+
+
+
+#ifdef ECHOCARD_HAS_MONITOR
+/* Set the monitor level from an input bus to an output bus. */
+static int set_monitor_gain(struct echoaudio *chip, u16 output, u16 input,
+                           s8 gain)
+{
+       snd_assert(output < num_busses_out(chip) &&
+                  input < num_busses_in(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->monitor_gain[output][input] = gain;
+       chip->comm_page->monitors[monitor_index(chip, output, input)] = gain;
+       return 0;
+}
+#endif /* ECHOCARD_HAS_MONITOR */
+
+
+/* Tell the DSP to read and update output, nominal & monitor levels in comm page. */
+static int update_output_line_level(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_OUTVOL);
+}
+
+
+
+/* Tell the DSP to read and update input levels in comm page */
+static int update_input_line_level(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_INGAIN);
+}
+
+
+
+/* set_meters_on turns the meters on or off.  If meters are turned on, the DSP
+will write the meter and clock detect values to the comm page at about 30Hz */
+static void set_meters_on(struct echoaudio *chip, char on)
+{
+       if (on && !chip->meters_enabled) {
+               send_vector(chip, DSP_VC_METERS_ON);
+               chip->meters_enabled = 1;
+       } else if (!on && chip->meters_enabled) {
+               send_vector(chip, DSP_VC_METERS_OFF);
+               chip->meters_enabled = 0;
+               memset((s8 *)chip->comm_page->vu_meter, ECHOGAIN_MUTED,
+                      DSP_MAXPIPES);
+               memset((s8 *)chip->comm_page->peak_meter, ECHOGAIN_MUTED,
+                      DSP_MAXPIPES);
+       }
+}
+
+
+
+/* Fill out an the given array using the current values in the comm page.
+Meters are written in the comm page by the DSP in this order:
+ Output busses
+ Input busses
+ Output pipes (vmixer cards only)
+
+This function assumes there are no more than 16 in/out busses or pipes
+Meters is an array [3][16][2] of long. */
+static void get_audio_meters(struct echoaudio *chip, long *meters)
+{
+       int i, m, n;
+
+       m = 0;
+       n = 0;
+       for (i = 0; i < num_busses_out(chip); i++, m++) {
+               meters[n++] = chip->comm_page->vu_meter[m];
+               meters[n++] = chip->comm_page->peak_meter[m];
+       }
+       for (; n < 32; n++)
+               meters[n] = 0;
+
+#ifdef ECHOCARD_ECHO3G
+       m = E3G_MAX_OUTPUTS;    /* Skip unused meters */
+#endif
+
+       for (i = 0; i < num_busses_in(chip); i++, m++) {
+               meters[n++] = chip->comm_page->vu_meter[m];
+               meters[n++] = chip->comm_page->peak_meter[m];
+       }
+       for (; n < 64; n++)
+               meters[n] = 0;
+
+#ifdef ECHOCARD_HAS_VMIXER
+       for (i = 0; i < num_pipes_out(chip); i++, m++) {
+               meters[n++] = chip->comm_page->vu_meter[m];
+               meters[n++] = chip->comm_page->peak_meter[m];
+       }
+#endif
+       for (; n < 96; n++)
+               meters[n] = 0;
+}
+
+
+
+static int restore_dsp_rettings(struct echoaudio *chip)
+{
+       int err;
+       DE_INIT(("restore_dsp_settings\n"));
+
+       if ((err = check_asic_status(chip)) < 0)
+               return err;
+
+       /* @ Gina20/Darla20 only. Should be harmless for other cards. */
+       chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF;
+       chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF;
+       chip->comm_page->handshake = 0xffffffff;
+
+       if ((err = set_sample_rate(chip, chip->sample_rate)) < 0)
+               return err;
+
+       if (chip->meters_enabled)
+               if (send_vector(chip, DSP_VC_METERS_ON) < 0)
+                       return -EIO;
+
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+       if (set_input_clock(chip, chip->input_clock) < 0)
+               return -EIO;
+#endif
+
+#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+       if (set_output_clock(chip, chip->output_clock) < 0)
+               return -EIO;
+#endif
+
+       if (update_output_line_level(chip) < 0)
+               return -EIO;
+
+       if (update_input_line_level(chip) < 0)
+               return -EIO;
+
+#ifdef ECHOCARD_HAS_VMIXER
+       if (update_vmixer_level(chip) < 0)
+               return -EIO;
+#endif
+
+       if (wait_handshake(chip) < 0)
+               return -EIO;
+       clear_handshake(chip);
+
+       DE_INIT(("restore_dsp_rettings done\n"));
+       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/****************************************************************************
+       Transport functions
+ ****************************************************************************/
+
+/* set_audio_format() sets the format of the audio data in host memory for
+this pipe.  Note that _MS_ (mono-to-stereo) playback modes are not used by ALSA
+but they are here because they are just mono while capturing */
+static void set_audio_format(struct echoaudio *chip, u16 pipe_index,
+                            const struct audioformat *format)
+{
+       u16 dsp_format;
+
+       dsp_format = DSP_AUDIOFORM_SS_16LE;
+
+       /* Look for super-interleave (no big-endian and 8 bits) */
+       if (format->interleave > 2) {
+               switch (format->bits_per_sample) {
+               case 16:
+                       dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE;
+                       break;
+               case 24:
+                       dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE;
+                       break;
+               case 32:
+                       dsp_format = DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE;
+                       break;
+               }
+               dsp_format |= format->interleave;
+       } else if (format->data_are_bigendian) {
+               /* For big-endian data, only 32 bit samples are supported */
+               switch (format->interleave) {
+               case 1:
+                       dsp_format = DSP_AUDIOFORM_MM_32BE;
+                       break;
+#ifdef ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+               case 2:
+                       dsp_format = DSP_AUDIOFORM_SS_32BE;
+                       break;
+#endif
+               }
+       } else if (format->interleave == 1 &&
+                  format->bits_per_sample == 32 && !format->mono_to_stereo) {
+               /* 32 bit little-endian mono->mono case */
+               dsp_format = DSP_AUDIOFORM_MM_32LE;
+       } else {
+               /* Handle the other little-endian formats */
+               switch (format->bits_per_sample) {
+               case 8:
+                       if (format->interleave == 2)
+                               dsp_format = DSP_AUDIOFORM_SS_8;
+                       else
+                               dsp_format = DSP_AUDIOFORM_MS_8;
+                       break;
+               default:
+               case 16:
+                       if (format->interleave == 2)
+                               dsp_format = DSP_AUDIOFORM_SS_16LE;
+                       else
+                               dsp_format = DSP_AUDIOFORM_MS_16LE;
+                       break;
+               case 24:
+                       if (format->interleave == 2)
+                               dsp_format = DSP_AUDIOFORM_SS_24LE;
+                       else
+                               dsp_format = DSP_AUDIOFORM_MS_24LE;
+                       break;
+               case 32:
+                       if (format->interleave == 2)
+                               dsp_format = DSP_AUDIOFORM_SS_32LE;
+                       else
+                               dsp_format = DSP_AUDIOFORM_MS_32LE;
+                       break;
+               }
+       }
+       DE_ACT(("set_audio_format[%d] = %x\n", pipe_index, dsp_format));
+       chip->comm_page->audio_format[pipe_index] = cpu_to_le16(dsp_format);
+}
+
+
+
+/* start_transport starts transport for a set of pipes.
+The bits 1 in channel_mask specify what pipes to start. Only the bit of the
+first channel must be set, regardless its interleave.
+Same thing for pause_ and stop_ -trasport below. */
+static int start_transport(struct echoaudio *chip, u32 channel_mask,
+                          u32 cyclic_mask)
+{
+       DE_ACT(("start_transport %x\n", channel_mask));
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->comm_page->cmd_start |= cpu_to_le32(channel_mask);
+
+       if (chip->comm_page->cmd_start) {
+               clear_handshake(chip);
+               send_vector(chip, DSP_VC_START_TRANSFER);
+               if (wait_handshake(chip))
+                       return -EIO;
+               /* Keep track of which pipes are transporting */
+               chip->active_mask |= channel_mask;
+               chip->comm_page->cmd_start = 0;
+               return 0;
+       }
+
+       DE_ACT(("start_transport: No pipes to start!\n"));
+       return -EINVAL;
+}
+
+
+
+static int pause_transport(struct echoaudio *chip, u32 channel_mask)
+{
+       DE_ACT(("pause_transport %x\n", channel_mask));
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask);
+       chip->comm_page->cmd_reset = 0;
+       if (chip->comm_page->cmd_stop) {
+               clear_handshake(chip);
+               send_vector(chip, DSP_VC_STOP_TRANSFER);
+               if (wait_handshake(chip))
+                       return -EIO;
+               /* Keep track of which pipes are transporting */
+               chip->active_mask &= ~channel_mask;
+               chip->comm_page->cmd_stop = 0;
+               chip->comm_page->cmd_reset = 0;
+               return 0;
+       }
+
+       DE_ACT(("pause_transport: No pipes to stop!\n"));
+       return 0;
+}
+
+
+
+static int stop_transport(struct echoaudio *chip, u32 channel_mask)
+{
+       DE_ACT(("stop_transport %x\n", channel_mask));
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->comm_page->cmd_stop |= cpu_to_le32(channel_mask);
+       chip->comm_page->cmd_reset |= cpu_to_le32(channel_mask);
+       if (chip->comm_page->cmd_reset) {
+               clear_handshake(chip);
+               send_vector(chip, DSP_VC_STOP_TRANSFER);
+               if (wait_handshake(chip))
+                       return -EIO;
+               /* Keep track of which pipes are transporting */
+               chip->active_mask &= ~channel_mask;
+               chip->comm_page->cmd_stop = 0;
+               chip->comm_page->cmd_reset = 0;
+               return 0;
+       }
+
+       DE_ACT(("stop_transport: No pipes to stop!\n"));
+       return 0;
+}
+
+
+
+static inline int is_pipe_allocated(struct echoaudio *chip, u16 pipe_index)
+{
+       return (chip->pipe_alloc_mask & (1 << pipe_index));
+}
+
+
+
+/* Stops everything and turns off the DSP. All pipes should be already
+stopped and unallocated. */
+static int rest_in_peace(struct echoaudio *chip)
+{
+       DE_ACT(("rest_in_peace() open=%x\n", chip->pipe_alloc_mask));
+
+       /* Stops all active pipes (just to be sure) */
+       stop_transport(chip, chip->active_mask);
+
+       set_meters_on(chip, FALSE);
+
+#ifdef ECHOCARD_HAS_MIDI
+       enable_midi_input(chip, FALSE);
+#endif
+
+       /* Go to sleep */
+       if (chip->dsp_code) {
+               /* Make load_firmware do a complete reload */
+               chip->dsp_code = NULL;
+               /* Put the DSP to sleep */
+               return send_vector(chip, DSP_VC_GO_COMATOSE);
+       }
+       return 0;
+}
+
+
+
+/* Fills the comm page with default values */
+static int init_dsp_comm_page(struct echoaudio *chip)
+{
+       /* Check if the compiler added extra padding inside the structure */
+       if (offsetof(struct comm_page, midi_output) != 0xbe0) {
+               DE_INIT(("init_dsp_comm_page() - Invalid struct comm_page structure\n"));
+               return -EPERM;
+       }
+
+       /* Init all the basic stuff */
+       chip->card_name = ECHOCARD_NAME;
+       chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
+       chip->dsp_code = NULL;  /* Current DSP code not loaded */
+       chip->digital_mode = DIGITAL_MODE_NONE;
+       chip->input_clock = ECHO_CLOCK_INTERNAL;
+       chip->output_clock = ECHO_CLOCK_WORD;
+       chip->asic_loaded = FALSE;
+       memset(chip->comm_page, 0, sizeof(struct comm_page));
+
+       /* Init the comm page */
+       chip->comm_page->comm_size =
+               __constant_cpu_to_le32(sizeof(struct comm_page));
+       chip->comm_page->handshake = 0xffffffff;
+       chip->comm_page->midi_out_free_count =
+               __constant_cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
+       chip->comm_page->sample_rate = __constant_cpu_to_le32(44100);
+       chip->sample_rate = 44100;
+
+       /* Set line levels so we don't blast any inputs on startup */
+       memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE);
+       memset(chip->comm_page->vmixer, ECHOGAIN_MUTED, VMIXER_ARRAY_SIZE);
+
+       return 0;
+}
+
+
+
+/* This function initializes the several volume controls for busses and pipes.
+This MUST be called after the DSP is up and running ! */
+static int init_line_levels(struct echoaudio *chip)
+{
+       int st, i, o;
+
+       DE_INIT(("init_line_levels\n"));
+
+       /* Mute output busses */
+       for (i = 0; i < num_busses_out(chip); i++)
+               if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED)))
+                       return st;
+       if ((st = update_output_line_level(chip)))
+               return st;
+
+#ifdef ECHOCARD_HAS_VMIXER
+       /* Mute the Vmixer */
+       for (i = 0; i < num_pipes_out(chip); i++)
+               for (o = 0; o < num_busses_out(chip); o++)
+                       if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED)))
+                               return st;
+       if ((st = update_vmixer_level(chip)))
+               return st;
+#endif /* ECHOCARD_HAS_VMIXER */
+
+#ifdef ECHOCARD_HAS_MONITOR
+       /* Mute the monitor mixer */
+       for (o = 0; o < num_busses_out(chip); o++)
+               for (i = 0; i < num_busses_in(chip); i++)
+                       if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED)))
+                               return st;
+       if ((st = update_output_line_level(chip)))
+               return st;
+#endif /* ECHOCARD_HAS_MONITOR */
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+       for (i = 0; i < num_busses_in(chip); i++)
+               if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED)))
+                       return st;
+       if ((st = update_input_line_level(chip)))
+               return st;
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+       return 0;
+}
+
+
+
+/* This is low level part of the interrupt handler.
+It returns -1 if the IRQ is not ours, or N>=0 if it is, where N is the number
+of midi data in the input queue. */
+static int service_irq(struct echoaudio *chip)
+{
+       int st;
+
+       /* Read the DSP status register and see if this DSP generated this interrupt */
+       if (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_IRQ) {
+               st = 0;
+#ifdef ECHOCARD_HAS_MIDI
+               /* Get and parse midi data if present */
+               if (chip->comm_page->midi_input[0])     /* The count is at index 0 */
+                       st = midi_service_irq(chip);    /* Returns how many midi bytes we received */
+#endif
+               /* Clear the hardware interrupt */
+               chip->comm_page->midi_input[0] = 0;
+               send_vector(chip, DSP_VC_ACK_INT);
+               return st;
+       }
+       return -1;
+}
+
+
+
+
+/******************************************************************************
+       Functions for opening and closing pipes
+ ******************************************************************************/
+
+/* allocate_pipes is used to reserve audio pipes for your exclusive use.
+The call will fail if some pipes are already allocated. */
+static int allocate_pipes(struct echoaudio *chip, struct audiopipe *pipe,
+                         int pipe_index, int interleave)
+{
+       int i;
+       u32 channel_mask;
+       char is_cyclic;
+
+       DE_ACT(("allocate_pipes: ch=%d int=%d\n", pipe_index, interleave));
+
+       if (chip->bad_board)
+               return -EIO;
+
+       is_cyclic = 1;  /* This driver uses cyclic buffers only */
+
+       for (channel_mask = i = 0; i < interleave; i++)
+               channel_mask |= 1 << (pipe_index + i);
+       if (chip->pipe_alloc_mask & channel_mask) {
+               DE_ACT(("allocate_pipes: channel already open\n"));
+               return -EAGAIN;
+       }
+
+       chip->comm_page->position[pipe_index] = 0;
+       chip->pipe_alloc_mask |= channel_mask;
+       if (is_cyclic)
+               chip->pipe_cyclic_mask |= channel_mask;
+       pipe->index = pipe_index;
+       pipe->interleave = interleave;
+       pipe->state = PIPE_STATE_STOPPED;
+
+       /* The counter register is where the DSP writes the 32 bit DMA
+       position for a pipe.  The DSP is constantly updating this value as
+       it moves data. The DMA counter is in units of bytes, not samples. */
+       pipe->dma_counter = &chip->comm_page->position[pipe_index];
+       *pipe->dma_counter = 0;
+       DE_ACT(("allocate_pipes: ok\n"));
+       return pipe_index;
+}
+
+
+
+static int free_pipes(struct echoaudio *chip, struct audiopipe *pipe)
+{
+       u32 channel_mask;
+       int i;
+
+       DE_ACT(("free_pipes: Pipe %d\n", pipe->index));
+       snd_assert(is_pipe_allocated(chip, pipe->index), return -EINVAL);
+       snd_assert(pipe->state == PIPE_STATE_STOPPED, return -EINVAL);
+
+       for (channel_mask = i = 0; i < pipe->interleave; i++)
+               channel_mask |= 1 << (pipe->index + i);
+
+       chip->pipe_alloc_mask &= ~channel_mask;
+       chip->pipe_cyclic_mask &= ~channel_mask;
+       return 0;
+}
+
+
+
+/******************************************************************************
+       Functions for managing the scatter-gather list
+******************************************************************************/
+
+static int sglist_init(struct echoaudio *chip, struct audiopipe *pipe)
+{
+       pipe->sglist_head = 0;
+       memset(pipe->sgpage.area, 0, PAGE_SIZE);
+       chip->comm_page->sglist_addr[pipe->index].addr =
+               cpu_to_le32(pipe->sgpage.addr);
+       return 0;
+}
+
+
+
+static int sglist_add_mapping(struct echoaudio *chip, struct audiopipe *pipe,
+                               dma_addr_t address, size_t length)
+{
+       int head = pipe->sglist_head;
+       struct sg_entry *list = (struct sg_entry *)pipe->sgpage.area;
+
+       if (head < MAX_SGLIST_ENTRIES - 1) {
+               list[head].addr = cpu_to_le32(address);
+               list[head].size = cpu_to_le32(length);
+               pipe->sglist_head++;
+       } else {
+               DE_ACT(("SGlist: too many fragments\n"));
+               return -ENOMEM;
+       }
+       return 0;
+}
+
+
+
+static inline int sglist_add_irq(struct echoaudio *chip, struct audiopipe *pipe)
+{
+       return sglist_add_mapping(chip, pipe, 0, 0);
+}
+
+
+
+static inline int sglist_wrap(struct echoaudio *chip, struct audiopipe *pipe)
+{
+       return sglist_add_mapping(chip, pipe, pipe->sgpage.addr, 0);
+}
diff --git a/sound/pci/echoaudio/echoaudio_dsp.h b/sound/pci/echoaudio/echoaudio_dsp.h
new file mode 100644 (file)
index 0000000..e55ee00
--- /dev/null
@@ -0,0 +1,694 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+#ifndef _ECHO_DSP_
+#define _ECHO_DSP_
+
+
+/**** Echogals: Darla20, Gina20, Layla20, and Darla24 ****/
+#if defined(ECHOGALS_FAMILY)
+
+#define NUM_ASIC_TESTS         5
+#define READ_DSP_TIMEOUT       1000000L        /* one second */
+
+/**** Echo24: Gina24, Layla24, Mona, Mia, Mia-midi ****/
+#elif defined(ECHO24_FAMILY)
+
+#define DSP_56361                      /* Some Echo24 cards use the 56361 DSP */
+#define READ_DSP_TIMEOUT       100000L         /* .1 second */
+
+/**** 3G: Gina3G, Layla3G ****/
+#elif defined(ECHO3G_FAMILY)
+
+#define DSP_56361
+#define READ_DSP_TIMEOUT       100000L         /* .1 second */
+#define MIN_MTC_1X_RATE                32000
+
+/**** Indigo: Indigo, Indigo IO, Indigo DJ ****/
+#elif defined(INDIGO_FAMILY)
+
+#define DSP_56361
+#define READ_DSP_TIMEOUT       100000L         /* .1 second */
+
+#else
+
+#error No family is defined
+
+#endif
+
+
+
+/*
+ *
+ *  Max inputs and outputs
+ *
+ */
+
+#define DSP_MAXAUDIOINPUTS             16      /* Max audio input channels */
+#define DSP_MAXAUDIOOUTPUTS            16      /* Max audio output channels */
+#define DSP_MAXPIPES                   32      /* Max total pipes (input + output) */
+
+
+/*
+ *
+ * These are the offsets for the memory-mapped DSP registers; the DSP base
+ * address is treated as the start of a u32 array.
+ */
+
+#define CHI32_CONTROL_REG              4
+#define CHI32_STATUS_REG               5
+#define CHI32_VECTOR_REG               6
+#define CHI32_DATA_REG                 7
+
+
+/*
+ *
+ * Interesting bits within the DSP registers
+ *
+ */
+
+#define CHI32_VECTOR_BUSY              0x00000001
+#define CHI32_STATUS_REG_HF3           0x00000008
+#define CHI32_STATUS_REG_HF4           0x00000010
+#define CHI32_STATUS_REG_HF5           0x00000020
+#define CHI32_STATUS_HOST_READ_FULL    0x00000004
+#define CHI32_STATUS_HOST_WRITE_EMPTY  0x00000002
+#define CHI32_STATUS_IRQ               0x00000040
+
+
+/* 
+ *
+ * DSP commands sent via slave mode; these are sent to the DSP by write_dsp()
+ *
+ */
+
+#define DSP_FNC_SET_COMMPAGE_ADDR              0x02
+#define DSP_FNC_LOAD_LAYLA_ASIC                        0xa0
+#define DSP_FNC_LOAD_GINA24_ASIC               0xa0
+#define DSP_FNC_LOAD_MONA_PCI_CARD_ASIC                0xa0
+#define DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC     0xa0
+#define DSP_FNC_LOAD_MONA_EXTERNAL_ASIC                0xa1
+#define DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC     0xa1
+#define DSP_FNC_LOAD_3G_ASIC                   0xa0
+
+
+/*
+ *
+ * Defines to handle the MIDI input state engine; these are used to properly
+ * extract MIDI time code bytes and their timestamps from the MIDI input stream.
+ *
+ */
+
+#define MIDI_IN_STATE_NORMAL   0
+#define MIDI_IN_STATE_TS_HIGH  1
+#define MIDI_IN_STATE_TS_LOW   2
+#define MIDI_IN_STATE_F1_DATA  3
+#define MIDI_IN_SKIP_DATA      (-1)
+
+
+/*----------------------------------------------------------------------------
+
+Setting the sample rates on Layla24 is somewhat schizophrenic.
+
+For standard rates, it works exactly like Mona and Gina24.  That is, for
+8, 11.025, 16, 22.05, 32, 44.1, 48, 88.2, and 96 kHz, you just set the
+appropriate bits in the control register and write the control register.
+
+In order to support MIDI time code sync (and possibly SMPTE LTC sync in
+the future), Layla24 also has "continuous sample rate mode".  In this mode,
+Layla24 can generate any sample rate between 25 and 50 kHz inclusive, or
+50 to 100 kHz inclusive for double speed mode.
+
+To use continuous mode:
+
+-Set the clock select bits in the control register to 0xe (see the #define
+ below)
+
+-Set double-speed mode if you want to use sample rates above 50 kHz
+
+-Write the control register as you would normally
+
+-Now, you need to set the frequency register. First, you need to determine the
+ value for the frequency register.  This is given by the following formula:
+
+frequency_reg = (LAYLA24_MAGIC_NUMBER / sample_rate) - 2
+
+Note the #define below for the magic number
+
+-Wait for the DSP handshake
+-Write the frequency_reg value to the .SampleRate field of the comm page
+-Send the vector command SET_LAYLA24_FREQUENCY_REG (see vmonkey.h)
+
+Once you have set the control register up for continuous mode, you can just
+write the frequency register to change the sample rate.  This could be
+used for MIDI time code sync. For MTC sync, the control register is set for
+continuous mode.  The driver then just keeps writing the
+SET_LAYLA24_FREQUENCY_REG command.
+
+-----------------------------------------------------------------------------*/
+
+#define LAYLA24_MAGIC_NUMBER                   677376000
+#define LAYLA24_CONTINUOUS_CLOCK               0x000e
+
+
+/*
+ *
+ * DSP vector commands
+ *
+ */
+
+#define DSP_VC_RESET                           0x80ff
+
+#ifndef DSP_56361
+
+#define DSP_VC_ACK_INT                         0x8073
+#define DSP_VC_SET_VMIXER_GAIN                 0x0000  /* Not used, only for compile */
+#define DSP_VC_START_TRANSFER                  0x0075  /* Handshke rqd. */
+#define DSP_VC_METERS_ON                       0x0079
+#define DSP_VC_METERS_OFF                      0x007b
+#define DSP_VC_UPDATE_OUTVOL                   0x007d  /* Handshke rqd. */
+#define DSP_VC_UPDATE_INGAIN                   0x007f  /* Handshke rqd. */
+#define DSP_VC_ADD_AUDIO_BUFFER                        0x0081  /* Handshke rqd. */
+#define DSP_VC_TEST_ASIC                       0x00eb
+#define DSP_VC_UPDATE_CLOCKS                   0x00ef  /* Handshke rqd. */
+#define DSP_VC_SET_LAYLA_SAMPLE_RATE           0x00f1  /* Handshke rqd. */
+#define DSP_VC_SET_GD_AUDIO_STATE              0x00f1  /* Handshke rqd. */
+#define DSP_VC_WRITE_CONTROL_REG               0x00f1  /* Handshke rqd. */
+#define DSP_VC_MIDI_WRITE                      0x00f5  /* Handshke rqd. */
+#define DSP_VC_STOP_TRANSFER                   0x00f7  /* Handshke rqd. */
+#define DSP_VC_UPDATE_FLAGS                    0x00fd  /* Handshke rqd. */
+#define DSP_VC_GO_COMATOSE                     0x00f9
+
+#else /* !DSP_56361 */
+
+/* Vector commands for families that use either the 56301 or 56361 */
+#define DSP_VC_ACK_INT                         0x80F5
+#define DSP_VC_SET_VMIXER_GAIN                 0x00DB  /* Handshke rqd. */
+#define DSP_VC_START_TRANSFER                  0x00DD  /* Handshke rqd. */
+#define DSP_VC_METERS_ON                       0x00EF
+#define DSP_VC_METERS_OFF                      0x00F1
+#define DSP_VC_UPDATE_OUTVOL                   0x00E3  /* Handshke rqd. */
+#define DSP_VC_UPDATE_INGAIN                   0x00E5  /* Handshke rqd. */
+#define DSP_VC_ADD_AUDIO_BUFFER                        0x00E1  /* Handshke rqd. */
+#define DSP_VC_TEST_ASIC                       0x00ED
+#define DSP_VC_UPDATE_CLOCKS                   0x00E9  /* Handshke rqd. */
+#define DSP_VC_SET_LAYLA24_FREQUENCY_REG       0x00E9  /* Handshke rqd. */
+#define DSP_VC_SET_LAYLA_SAMPLE_RATE           0x00EB  /* Handshke rqd. */
+#define DSP_VC_SET_GD_AUDIO_STATE              0x00EB  /* Handshke rqd. */
+#define DSP_VC_WRITE_CONTROL_REG               0x00EB  /* Handshke rqd. */
+#define DSP_VC_MIDI_WRITE                      0x00E7  /* Handshke rqd. */
+#define DSP_VC_STOP_TRANSFER                   0x00DF  /* Handshke rqd. */
+#define DSP_VC_UPDATE_FLAGS                    0x00FB  /* Handshke rqd. */
+#define DSP_VC_GO_COMATOSE                     0x00d9
+
+#endif /* !DSP_56361 */
+
+
+/*
+ *
+ * Timeouts
+ *
+ */
+
+#define HANDSHAKE_TIMEOUT              20000   /* send_vector command timeout (20ms) */
+#define VECTOR_BUSY_TIMEOUT            100000  /* 100ms */
+#define MIDI_OUT_DELAY_USEC            2000    /* How long to wait after MIDI fills up */
+
+
+/*
+ *
+ * Flags for .Flags field in the comm page
+ *
+ */
+
+#define DSP_FLAG_MIDI_INPUT            0x0001  /* Enable MIDI input */
+#define DSP_FLAG_SPDIF_NONAUDIO                0x0002  /* Sets the "non-audio" bit
+                                                * in the S/PDIF out status
+                                                * bits.  Clear this flag for
+                                                * audio data;
+                                                * set it for AC3 or WMA or
+                                                * some such */
+#define DSP_FLAG_PROFESSIONAL_SPDIF    0x0008  /* 1 Professional, 0 Consumer */
+
+
+/*
+ *
+ * Clock detect bits reported by the DSP for Gina20, Layla20, Darla24, and Mia
+ *
+ */
+
+#define GLDM_CLOCK_DETECT_BIT_WORD     0x0002
+#define GLDM_CLOCK_DETECT_BIT_SUPER    0x0004
+#define GLDM_CLOCK_DETECT_BIT_SPDIF    0x0008
+#define GLDM_CLOCK_DETECT_BIT_ESYNC    0x0010
+
+
+/*
+ *
+ * Clock detect bits reported by the DSP for Gina24, Mona, and Layla24
+ *
+ */
+
+#define GML_CLOCK_DETECT_BIT_WORD96    0x0002
+#define GML_CLOCK_DETECT_BIT_WORD48    0x0004
+#define GML_CLOCK_DETECT_BIT_SPDIF48   0x0008
+#define GML_CLOCK_DETECT_BIT_SPDIF96   0x0010
+#define GML_CLOCK_DETECT_BIT_WORD      (GML_CLOCK_DETECT_BIT_WORD96 | GML_CLOCK_DETECT_BIT_WORD48)
+#define GML_CLOCK_DETECT_BIT_SPDIF     (GML_CLOCK_DETECT_BIT_SPDIF48 | GML_CLOCK_DETECT_BIT_SPDIF96)
+#define GML_CLOCK_DETECT_BIT_ESYNC     0x0020
+#define GML_CLOCK_DETECT_BIT_ADAT      0x0040
+
+
+/*
+ *
+ * Layla clock numbers to send to DSP
+ *
+ */
+
+#define LAYLA20_CLOCK_INTERNAL         0
+#define LAYLA20_CLOCK_SPDIF            1
+#define LAYLA20_CLOCK_WORD             2
+#define LAYLA20_CLOCK_SUPER            3
+
+
+/*
+ *
+ * Gina/Darla clock states
+ *
+ */
+
+#define GD_CLOCK_NOCHANGE              0
+#define GD_CLOCK_44                    1
+#define GD_CLOCK_48                    2
+#define GD_CLOCK_SPDIFIN               3
+#define GD_CLOCK_UNDEF                 0xff
+
+
+/*
+ *
+ * Gina/Darla S/PDIF status bits
+ *
+ */
+
+#define GD_SPDIF_STATUS_NOCHANGE       0
+#define GD_SPDIF_STATUS_44             1
+#define GD_SPDIF_STATUS_48             2
+#define GD_SPDIF_STATUS_UNDEF          0xff
+
+
+/*
+ *
+ * Layla20 output clocks
+ *
+ */
+
+#define LAYLA20_OUTPUT_CLOCK_SUPER     0
+#define LAYLA20_OUTPUT_CLOCK_WORD      1
+
+
+/****************************************************************************
+
+   Magic constants for the Darla24 hardware
+
+ ****************************************************************************/
+
+#define GD24_96000     0x0
+#define GD24_48000     0x1
+#define GD24_44100     0x2
+#define GD24_32000     0x3
+#define GD24_22050     0x4
+#define GD24_16000     0x5
+#define GD24_11025     0x6
+#define GD24_8000      0x7
+#define GD24_88200     0x8
+#define GD24_EXT_SYNC  0x9
+
+
+/*
+ *
+ * Return values from the DSP when ASIC is loaded
+ *
+ */
+
+#define ASIC_ALREADY_LOADED    0x1
+#define ASIC_NOT_LOADED                0x0
+
+
+/*
+ *
+ * DSP Audio formats
+ *
+ * These are the audio formats that the DSP can transfer
+ * via input and output pipes.  LE means little-endian,
+ * BE means big-endian.
+ *
+ * DSP_AUDIOFORM_MS_8   
+ *
+ *    8-bit mono unsigned samples.  For playback,
+ *    mono data is duplicated out the left and right channels
+ *    of the output bus.  The "MS" part of the name
+ *    means mono->stereo.
+ *
+ * DSP_AUDIOFORM_MS_16LE
+ *
+ *    16-bit signed little-endian mono samples.  Playback works
+ *    like the previous code.
+ *
+ * DSP_AUDIOFORM_MS_24LE
+ *
+ *    24-bit signed little-endian mono samples.  Data is packed
+ *    three bytes per sample; if you had two samples 0x112233 and 0x445566
+ *    they would be stored in memory like this: 33 22 11 66 55 44.
+ *
+ * DSP_AUDIOFORM_MS_32LE
+ * 
+ *    24-bit signed little-endian mono samples in a 32-bit 
+ *    container.  In other words, each sample is a 32-bit signed 
+ *    integer, where the actual audio data is left-justified 
+ *    in the 32 bits and only the 24 most significant bits are valid.
+ *
+ * DSP_AUDIOFORM_SS_8
+ * DSP_AUDIOFORM_SS_16LE
+ * DSP_AUDIOFORM_SS_24LE
+ * DSP_AUDIOFORM_SS_32LE
+ *
+ *    Like the previous ones, except now with stereo interleaved
+ *    data.  "SS" means stereo->stereo.
+ *
+ * DSP_AUDIOFORM_MM_32LE
+ *
+ *    Similar to DSP_AUDIOFORM_MS_32LE, except that the mono
+ *    data is not duplicated out both the left and right outputs.
+ *    This mode is used by the ASIO driver.  Here, "MM" means
+ *    mono->mono.
+ *
+ * DSP_AUDIOFORM_MM_32BE
+ *
+ *    Just like DSP_AUDIOFORM_MM_32LE, but now the data is
+ *    in big-endian format.
+ *
+ */
+
+#define DSP_AUDIOFORM_MS_8     0       /* 8 bit mono */
+#define DSP_AUDIOFORM_MS_16LE  1       /* 16 bit mono */
+#define DSP_AUDIOFORM_MS_24LE  2       /* 24 bit mono */
+#define DSP_AUDIOFORM_MS_32LE  3       /* 32 bit mono */
+#define DSP_AUDIOFORM_SS_8     4       /* 8 bit stereo */
+#define DSP_AUDIOFORM_SS_16LE  5       /* 16 bit stereo */
+#define DSP_AUDIOFORM_SS_24LE  6       /* 24 bit stereo */
+#define DSP_AUDIOFORM_SS_32LE  7       /* 32 bit stereo */
+#define DSP_AUDIOFORM_MM_32LE  8       /* 32 bit mono->mono little-endian */
+#define DSP_AUDIOFORM_MM_32BE  9       /* 32 bit mono->mono big-endian */
+#define DSP_AUDIOFORM_SS_32BE  10      /* 32 bit stereo big endian */
+#define DSP_AUDIOFORM_INVALID  0xFF    /* Invalid audio format */
+
+
+/*
+ *
+ * Super-interleave is defined as interleaving by 4 or more.  Darla20 and Gina20
+ * do not support super interleave.
+ *
+ * 16 bit, 24 bit, and 32 bit little endian samples are supported for super 
+ * interleave.  The interleave factor must be even.  16 - way interleave is the 
+ * current maximum, so you can interleave by 4, 6, 8, 10, 12, 14, and 16.
+ *
+ * The actual format code is derived by taking the define below and or-ing with
+ * the interleave factor.  So, 32 bit interleave by 6 is 0x86 and
+ * 16 bit interleave by 16 is (0x40 | 0x10) = 0x50.
+ *
+ */
+
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_16LE    0x40
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_24LE    0xc0
+#define DSP_AUDIOFORM_SUPER_INTERLEAVE_32LE    0x80
+
+
+/*
+ *
+ * Gina24, Mona, and Layla24 control register defines
+ *
+ */
+
+#define GML_CONVERTER_ENABLE   0x0010
+#define GML_SPDIF_PRO_MODE     0x0020  /* Professional S/PDIF == 1,
+                                          consumer == 0 */
+#define GML_SPDIF_SAMPLE_RATE0 0x0040
+#define GML_SPDIF_SAMPLE_RATE1 0x0080
+#define GML_SPDIF_TWO_CHANNEL  0x0100  /* 1 == two channels,
+                                          0 == one channel */
+#define GML_SPDIF_NOT_AUDIO    0x0200
+#define GML_SPDIF_COPY_PERMIT  0x0400
+#define GML_SPDIF_24_BIT       0x0800  /* 1 == 24 bit, 0 == 20 bit */
+#define GML_ADAT_MODE          0x1000  /* 1 == ADAT mode, 0 == S/PDIF mode */
+#define GML_SPDIF_OPTICAL_MODE 0x2000  /* 1 == optical mode, 0 == RCA mode */
+#define GML_SPDIF_CDROM_MODE   0x3000  /* 1 == CDROM mode,
+                                        * 0 == RCA or optical mode */
+#define GML_DOUBLE_SPEED_MODE  0x4000  /* 1 == double speed,
+                                          0 == single speed */
+
+#define GML_DIGITAL_IN_AUTO_MUTE 0x800000
+
+#define GML_96KHZ              (0x0 | GML_DOUBLE_SPEED_MODE)
+#define GML_88KHZ              (0x1 | GML_DOUBLE_SPEED_MODE)
+#define GML_48KHZ              0x2
+#define GML_44KHZ              0x3
+#define GML_32KHZ              0x4
+#define GML_22KHZ              0x5
+#define GML_16KHZ              0x6
+#define GML_11KHZ              0x7
+#define GML_8KHZ               0x8
+#define GML_SPDIF_CLOCK                0x9
+#define GML_ADAT_CLOCK         0xA
+#define GML_WORD_CLOCK         0xB
+#define GML_ESYNC_CLOCK                0xC
+#define GML_ESYNCx2_CLOCK      0xD
+
+#define GML_CLOCK_CLEAR_MASK           0xffffbff0
+#define GML_SPDIF_RATE_CLEAR_MASK      (~(GML_SPDIF_SAMPLE_RATE0|GML_SPDIF_SAMPLE_RATE1))
+#define GML_DIGITAL_MODE_CLEAR_MASK    0xffffcfff
+#define GML_SPDIF_FORMAT_CLEAR_MASK    0xfffff01f
+
+
+/*
+ *
+ * Mia sample rate and clock setting constants
+ *
+ */
+
+#define MIA_32000      0x0040
+#define MIA_44100      0x0042
+#define MIA_48000      0x0041
+#define MIA_88200      0x0142
+#define MIA_96000      0x0141
+
+#define MIA_SPDIF      0x00000044
+#define MIA_SPDIF96    0x00000144
+
+#define MIA_MIDI_REV   1       /* Must be Mia rev 1 for MIDI support */
+
+
+/*
+ *
+ * 3G register bits
+ *
+ */
+
+#define E3G_CONVERTER_ENABLE   0x0010
+#define E3G_SPDIF_PRO_MODE     0x0020  /* Professional S/PDIF == 1,
+                                          consumer == 0 */
+#define E3G_SPDIF_SAMPLE_RATE0 0x0040
+#define E3G_SPDIF_SAMPLE_RATE1 0x0080
+#define E3G_SPDIF_TWO_CHANNEL  0x0100  /* 1 == two channels,
+                                          0 == one channel */
+#define E3G_SPDIF_NOT_AUDIO    0x0200
+#define E3G_SPDIF_COPY_PERMIT  0x0400
+#define E3G_SPDIF_24_BIT       0x0800  /* 1 == 24 bit, 0 == 20 bit */
+#define E3G_DOUBLE_SPEED_MODE  0x4000  /* 1 == double speed,
+                                          0 == single speed */
+#define E3G_PHANTOM_POWER      0x8000  /* 1 == phantom power on,
+                                          0 == phantom power off */
+
+#define E3G_96KHZ              (0x0 | E3G_DOUBLE_SPEED_MODE)
+#define E3G_88KHZ              (0x1 | E3G_DOUBLE_SPEED_MODE)
+#define E3G_48KHZ              0x2
+#define E3G_44KHZ              0x3
+#define E3G_32KHZ              0x4
+#define E3G_22KHZ              0x5
+#define E3G_16KHZ              0x6
+#define E3G_11KHZ              0x7
+#define E3G_8KHZ               0x8
+#define E3G_SPDIF_CLOCK                0x9
+#define E3G_ADAT_CLOCK         0xA
+#define E3G_WORD_CLOCK         0xB
+#define E3G_CONTINUOUS_CLOCK   0xE
+
+#define E3G_ADAT_MODE          0x1000
+#define E3G_SPDIF_OPTICAL_MODE 0x2000
+
+#define E3G_CLOCK_CLEAR_MASK           0xbfffbff0
+#define E3G_DIGITAL_MODE_CLEAR_MASK    0xffffcfff
+#define E3G_SPDIF_FORMAT_CLEAR_MASK    0xfffff01f
+
+/* Clock detect bits reported by the DSP */
+#define E3G_CLOCK_DETECT_BIT_WORD96    0x0001
+#define E3G_CLOCK_DETECT_BIT_WORD48    0x0002
+#define E3G_CLOCK_DETECT_BIT_SPDIF48   0x0004
+#define E3G_CLOCK_DETECT_BIT_ADAT      0x0004
+#define E3G_CLOCK_DETECT_BIT_SPDIF96   0x0008
+#define E3G_CLOCK_DETECT_BIT_WORD      (E3G_CLOCK_DETECT_BIT_WORD96|E3G_CLOCK_DETECT_BIT_WORD48)
+#define E3G_CLOCK_DETECT_BIT_SPDIF     (E3G_CLOCK_DETECT_BIT_SPDIF48|E3G_CLOCK_DETECT_BIT_SPDIF96)
+
+/* Frequency control register */
+#define E3G_MAGIC_NUMBER               677376000
+#define E3G_FREQ_REG_DEFAULT           (E3G_MAGIC_NUMBER / 48000 - 2)
+#define E3G_FREQ_REG_MAX               0xffff
+
+/* 3G external box types */
+#define E3G_GINA3G_BOX_TYPE            0x00
+#define E3G_LAYLA3G_BOX_TYPE           0x10
+#define E3G_ASIC_NOT_LOADED            0xffff
+#define E3G_BOX_TYPE_MASK              0xf0
+
+#define EXT_3GBOX_NC                   0x01
+#define EXT_3GBOX_NOT_SET              0x02
+
+
+/*
+ *
+ * Gina20 & Layla20 have input gain controls for the analog inputs;
+ * this is the magic number for the hardware that gives you 0 dB at -10.
+ *
+ */
+
+#define GL20_INPUT_GAIN_MAGIC_NUMBER   0xC8
+
+
+/*
+ *
+ * Defines how much time must pass between DSP load attempts
+ *
+ */
+
+#define DSP_LOAD_ATTEMPT_PERIOD                1000000L        /* One second */
+
+
+/*
+ *
+ * Size of arrays for the comm page.  MAX_PLAY_TAPS and MAX_REC_TAPS are
+ * no longer used, but the sizes must still be right for the DSP to see
+ * the comm page correctly.
+ *
+ */
+
+#define MONITOR_ARRAY_SIZE     0x180
+#define VMIXER_ARRAY_SIZE      0x40
+#define MIDI_OUT_BUFFER_SIZE   32
+#define MIDI_IN_BUFFER_SIZE    256
+#define MAX_PLAY_TAPS          168
+#define MAX_REC_TAPS           192
+#define DSP_MIDI_OUT_FIFO_SIZE 64
+
+
+/* sg_entry is a single entry for the scatter-gather list.  The array of struct
+sg_entry struct is read by the DSP, so all values must be little-endian. */
+
+#define MAX_SGLIST_ENTRIES 512
+
+struct sg_entry {
+       u32 addr;
+       u32 size;
+};
+
+
+/****************************************************************************
+
+  The comm page.  This structure is read and written by the DSP; the
+  DSP code is a firm believer in the byte offsets written in the comments
+  at the end of each line.  This structure should not be changed.
+
+  Any reads from or writes to this structure should be in little-endian format.
+
+ ****************************************************************************/
+
+struct comm_page {             /*                              Base    Length*/
+       u32 comm_size;          /* size of this object          0x000   4 */
+       u32 flags;              /* See Appendix A below         0x004   4 */
+       u32 unused;             /* Unused entry                 0x008   4 */
+       u32 sample_rate;        /* Card sample rate in Hz       0x00c   4 */
+       volatile u32 handshake; /* DSP command handshake        0x010   4 */
+       u32 cmd_start;          /* Chs. to start mask           0x014   4 */
+       u32 cmd_stop;           /* Chs. to stop mask            0x018   4 */
+       u32 cmd_reset;          /* Chs. to reset mask           0x01c   4 */
+       u16 audio_format[DSP_MAXPIPES]; /* Chs. audio format    0x020   32*2 */
+       struct sg_entry sglist_addr[DSP_MAXPIPES];
+                               /* Chs. Physical sglist addrs   0x060   32*8 */
+       volatile u32 position[DSP_MAXPIPES];
+                               /* Positions for ea. ch.        0x160   32*4 */
+       volatile s8 vu_meter[DSP_MAXPIPES];
+                               /* VU meters                    0x1e0   32*1 */
+       volatile s8 peak_meter[DSP_MAXPIPES];
+                               /* Peak meters                  0x200   32*1 */
+       s8 line_out_level[DSP_MAXAUDIOOUTPUTS];
+                               /* Output gain                  0x220   16*1 */
+       s8 line_in_level[DSP_MAXAUDIOINPUTS];
+                               /* Input gain                   0x230   16*1 */
+       s8 monitors[MONITOR_ARRAY_SIZE];
+                               /* Monitor map                  0x240   0x180 */
+       u32 play_coeff[MAX_PLAY_TAPS];
+                       /* Gina/Darla play filters - obsolete   0x3c0   168*4 */
+       u32 rec_coeff[MAX_REC_TAPS];
+                       /* Gina/Darla record filters - obsolete 0x660   192*4 */
+       volatile u16 midi_input[MIDI_IN_BUFFER_SIZE];
+                       /* MIDI input data transfer buffer      0x960   256*2 */
+       u8 gd_clock_state;      /* Chg Gina/Darla clock state   0xb60   1 */
+       u8 gd_spdif_status;     /* Chg. Gina/Darla S/PDIF state 0xb61   1 */
+       u8 gd_resampler_state;  /* Should always be 3           0xb62   1 */
+       u8 filler2;             /*                              0xb63   1 */
+       u32 nominal_level_mask; /* -10 level enable mask        0xb64   4 */
+       u16 input_clock;        /* Chg. Input clock state       0xb68   2 */
+       u16 output_clock;       /* Chg. Output clock state      0xb6a   2 */
+       volatile u32 status_clocks;
+                               /* Current Input clock state    0xb6c   4 */
+       u32 ext_box_status;     /* External box status          0xb70   4 */
+       u32 cmd_add_buffer;     /* Pipes to add (obsolete)      0xb74   4 */
+       volatile u32 midi_out_free_count;
+                       /* # of bytes free in MIDI output FIFO  0xb78   4 */
+       u32 unused2;            /* Cyclic pipes                 0xb7c   4 */
+       u32 control_register;
+                       /* Mona, Gina24, Layla24, 3G ctrl reg   0xb80   4 */
+       u32 e3g_frq_register;   /* 3G frequency register        0xb84   4 */
+       u8 filler[24];          /* filler                       0xb88   24*1 */
+       s8 vmixer[VMIXER_ARRAY_SIZE];
+                               /* Vmixer levels                0xba0   64*1 */
+       u8 midi_output[MIDI_OUT_BUFFER_SIZE];
+                               /* MIDI output data             0xbe0   32*1 */
+};
+
+#endif /* _ECHO_DSP_ */
diff --git a/sound/pci/echoaudio/echoaudio_gml.c b/sound/pci/echoaudio/echoaudio_gml.c
new file mode 100644 (file)
index 0000000..3aa37e7
--- /dev/null
@@ -0,0 +1,198 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/* These functions are common for Gina24, Layla24 and Mona cards */
+
+
+/* ASIC status check - some cards have one or two ASICs that need to be
+loaded.  Once that load is complete, this function is called to see if
+the load was successful.
+If this load fails, it does not necessarily mean that the hardware is
+defective - the external box may be disconnected or turned off. */
+static int check_asic_status(struct echoaudio *chip)
+{
+       u32 asic_status;
+
+       send_vector(chip, DSP_VC_TEST_ASIC);
+
+       /* The DSP will return a value to indicate whether or not the
+          ASIC is currently loaded */
+       if (read_dsp(chip, &asic_status) < 0) {
+               DE_INIT(("check_asic_status: failed on read_dsp\n"));
+               chip->asic_loaded = FALSE;
+               return -EIO;
+       }
+
+       chip->asic_loaded = (asic_status == ASIC_ALREADY_LOADED);
+       return chip->asic_loaded ? 0 : -EIO;
+}
+
+
+
+/* Most configuration of Gina24, Layla24, or Mona is accomplished by writing
+the control register.  write_control_reg sends the new control register
+value to the DSP. */
+static int write_control_reg(struct echoaudio *chip, u32 value, char force)
+{
+       /* Handle the digital input auto-mute */
+       if (chip->digital_in_automute)
+               value |= GML_DIGITAL_IN_AUTO_MUTE;
+       else
+               value &= ~GML_DIGITAL_IN_AUTO_MUTE;
+
+       DE_ACT(("write_control_reg: 0x%x\n", value));
+
+       /* Write the control register */
+       value = cpu_to_le32(value);
+       if (value != chip->comm_page->control_register || force) {
+               if (wait_handshake(chip))
+                       return -EIO;
+               chip->comm_page->control_register = value;
+               clear_handshake(chip);
+               return send_vector(chip, DSP_VC_WRITE_CONTROL_REG);
+       }
+       return 0;
+}
+
+
+
+/* Gina24, Layla24, and Mona support digital input auto-mute.  If the digital
+input auto-mute is enabled, the DSP will only enable the digital inputs if
+the card is syncing to a valid clock on the ADAT or S/PDIF inputs.
+If the auto-mute is disabled, the digital inputs are enabled regardless of
+what the input clock is set or what is connected. */
+static int set_input_auto_mute(struct echoaudio *chip, int automute)
+{
+       DE_ACT(("set_input_auto_mute %d\n", automute));
+
+       chip->digital_in_automute = automute;
+
+       /* Re-set the input clock to the current value - indirectly causes
+       the auto-mute flag to be sent to the DSP */
+       return set_input_clock(chip, chip->input_clock);
+}
+
+
+
+/* S/PDIF coax / S/PDIF optical / ADAT - switch */
+static int set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+       u8 previous_mode;
+       int err, i, o;
+
+       if (chip->bad_board)
+               return -EIO;
+
+       /* All audio channels must be closed before changing the digital mode */
+       snd_assert(!chip->pipe_alloc_mask, return -EAGAIN);
+
+       snd_assert(chip->digital_modes & (1 << mode), return -EINVAL);
+
+       previous_mode = chip->digital_mode;
+       err = dsp_set_digital_mode(chip, mode);
+
+       /* If we successfully changed the digital mode from or to ADAT,
+          then make sure all output, input and monitor levels are
+          updated by the DSP comm object. */
+       if (err >= 0 && previous_mode != mode &&
+           (previous_mode == DIGITAL_MODE_ADAT || mode == DIGITAL_MODE_ADAT)) {
+               spin_lock_irq(&chip->lock);
+               for (o = 0; o < num_busses_out(chip); o++)
+                       for (i = 0; i < num_busses_in(chip); i++)
+                               set_monitor_gain(chip, o, i,
+                                                chip->monitor_gain[o][i]);
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+               for (i = 0; i < num_busses_in(chip); i++)
+                       set_input_gain(chip, i, chip->input_gain[i]);
+               update_input_line_level(chip);
+#endif
+
+               for (o = 0; o < num_busses_out(chip); o++)
+                       set_output_gain(chip, o, chip->output_gain[o]);
+               update_output_line_level(chip);
+               spin_unlock_irq(&chip->lock);
+       }
+
+       return err;
+}
+
+
+
+/* Set the S/PDIF output format */
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+       u32 control_reg;
+       int err;
+
+       /* Clear the current S/PDIF flags */
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_SPDIF_FORMAT_CLEAR_MASK;
+
+       /* Set the new S/PDIF flags depending on the mode */
+       control_reg |= GML_SPDIF_TWO_CHANNEL | GML_SPDIF_24_BIT |
+               GML_SPDIF_COPY_PERMIT;
+       if (prof) {
+               /* Professional mode */
+               control_reg |= GML_SPDIF_PRO_MODE;
+
+               switch (chip->sample_rate) {
+               case 32000:
+                       control_reg |= GML_SPDIF_SAMPLE_RATE0 |
+                               GML_SPDIF_SAMPLE_RATE1;
+                       break;
+               case 44100:
+                       control_reg |= GML_SPDIF_SAMPLE_RATE0;
+                       break;
+               case 48000:
+                       control_reg |= GML_SPDIF_SAMPLE_RATE1;
+                       break;
+               }
+       } else {
+               /* Consumer mode */
+               switch (chip->sample_rate) {
+               case 32000:
+                       control_reg |= GML_SPDIF_SAMPLE_RATE0 |
+                               GML_SPDIF_SAMPLE_RATE1;
+                       break;
+               case 48000:
+                       control_reg |= GML_SPDIF_SAMPLE_RATE1;
+                       break;
+               }
+       }
+
+       if ((err = write_control_reg(chip, control_reg, FALSE)))
+               return err;
+       chip->professional_spdif = prof;
+       DE_ACT(("set_professional_spdif to %s\n",
+               prof ? "Professional" : "Consumer"));
+       return 0;
+}
diff --git a/sound/pci/echoaudio/gina20.c b/sound/pci/echoaudio/gina20.c
new file mode 100644 (file)
index 0000000..29d6d12
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_GINA20
+#define ECHOCARD_NAME "Gina20"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_GAIN
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT      FALSE
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 2 */
+#define PX_ANALOG_IN   10      /* 2 */
+#define PX_DIGITAL_IN  12      /* 2 */
+#define PX_NUM         14
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 8 */
+#define BX_DIGITAL_OUT 8       /* 2 */
+#define BX_ANALOG_IN   10      /* 2 */
+#define BX_DIGITAL_IN  12      /* 2 */
+#define BX_NUM         14
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_GINA20_DSP  0
+
+static const struct firmware card_fw[] = {
+       {0, "gina20_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0},      /* DSP 56301 Gina20 rev.0 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000,
+       .rate_min = 44100,
+       .rate_max = 48000,
+       .channels_min = 1,
+       .channels_max = 2,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "gina20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/gina20_dsp.c b/sound/pci/echoaudio/gina20_dsp.c
new file mode 100644 (file)
index 0000000..2757c89
--- /dev/null
@@ -0,0 +1,215 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int update_flags(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Gina20\n"));
+       snd_assert((subdevice_id & 0xfff0) == GINA20, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP];
+       chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+       chip->clock_state = GD_CLOCK_UNDEF;
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+               ECHO_CLOCK_BIT_SPDIF;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       err = set_professional_spdif(chip, TRUE);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock
+          detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+               clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+       return clock_bits;
+}
+
+
+
+/* The Gina20 has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u8 clock_state, spdif_status;
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       switch (rate) {
+       case 44100:
+               clock_state = GD_CLOCK_44;
+               spdif_status = GD_SPDIF_STATUS_44;
+               break;
+       case 48000:
+               clock_state = GD_CLOCK_48;
+               spdif_status = GD_SPDIF_STATUS_48;
+               break;
+       default:
+               clock_state = GD_CLOCK_NOCHANGE;
+               spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+               break;
+       }
+
+       if (chip->clock_state == clock_state)
+               clock_state = GD_CLOCK_NOCHANGE;
+       if (spdif_status == chip->spdif_status)
+               spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);
+       chip->comm_page->gd_clock_state = clock_state;
+       chip->comm_page->gd_spdif_status = spdif_status;
+       chip->comm_page->gd_resampler_state = 3;        /* magic number - should always be 3 */
+
+       /* Save the new audio state if it changed */
+       if (clock_state != GD_CLOCK_NOCHANGE)
+               chip->clock_state = clock_state;
+       if (spdif_status != GD_SPDIF_STATUS_NOCHANGE)
+               chip->spdif_status = spdif_status;
+       chip->sample_rate = rate;
+
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       DE_ACT(("set_input_clock:\n"));
+
+       switch (clock) {
+       case ECHO_CLOCK_INTERNAL:
+               /* Reset the audio state to unknown (just in case) */
+               chip->clock_state = GD_CLOCK_UNDEF;
+               chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
+               set_sample_rate(chip, chip->sample_rate);
+               chip->input_clock = clock;
+               DE_ACT(("Set Gina clock to INTERNAL\n"));
+               break;
+       case ECHO_CLOCK_SPDIF:
+               chip->comm_page->gd_clock_state = GD_CLOCK_SPDIFIN;
+               chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_NOCHANGE;
+               clear_handshake(chip);
+               send_vector(chip, DSP_VC_SET_GD_AUDIO_STATE);
+               chip->clock_state = GD_CLOCK_SPDIFIN;
+               DE_ACT(("Set Gina20 clock to SPDIF\n"));
+               chip->input_clock = clock;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+
+
+/* Set input bus gain (one unit is 0.5dB !) */
+static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
+{
+       snd_assert(input < num_busses_in(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->input_gain[input] = gain;
+       gain += GL20_INPUT_GAIN_MAGIC_NUMBER;
+       chip->comm_page->line_in_level[input] = gain;
+       return 0;
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+       DE_ACT(("set_professional_spdif %d\n", prof));
+       if (prof)
+               chip->comm_page->flags |=
+                       __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+       else
+               chip->comm_page->flags &=
+                       ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+       chip->professional_spdif = prof;
+       return update_flags(chip);
+}
diff --git a/sound/pci/echoaudio/gina24.c b/sound/pci/echoaudio/gina24.c
new file mode 100644 (file)
index 0000000..e464d72
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_GINA24
+#define ECHOCARD_NAME "Gina24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT      6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 8 */
+#define PX_ANALOG_IN   16      /* 2 */
+#define PX_DIGITAL_IN  18      /* 8 */
+#define PX_NUM         26
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 8 */
+#define BX_DIGITAL_OUT 8       /* 8 */
+#define BX_ANALOG_IN   16      /* 2 */
+#define BX_DIGITAL_IN  18      /* 8 */
+#define BX_NUM         26
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER          0
+#define FW_GINA24_301_DSP      1
+#define FW_GINA24_361_DSP      2
+#define FW_GINA24_301_ASIC     3
+#define FW_GINA24_361_ASIC     4
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "gina24_301_dsp.fw"},
+       {0, "gina24_361_dsp.fw"},
+       {0, "gina24_301_asic.fw"},
+       {0, "gina24_361_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0},      /* DSP 56301 Gina24 rev.0 */
+       {0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0},      /* DSP 56301 Gina24 rev.1 */
+       {0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0},      /* DSP 56361 Gina24 rev.0 */
+       {0x1057, 0x3410, 0xECC0, 0x0051, 0, 0, 0},      /* DSP 56361 Gina24 rev.1 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_8000_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 8000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions.
+       220 ~= (512 - 1 - (BUFFER_BYTES_MAX / PAGE_SIZE)) / 2 */
+};
+
+#include "gina24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/gina24_dsp.c b/sound/pci/echoaudio/gina24_dsp.c
new file mode 100644 (file)
index 0000000..144fc56
--- /dev/null
@@ -0,0 +1,346 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+                            const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Gina24\n"));
+       snd_assert((subdevice_id & 0xfff0) == GINA24, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->input_clock_types =
+               ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+               ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
+               ECHO_CLOCK_BIT_ADAT;
+       chip->professional_spdif = FALSE;
+       chip->digital_in_automute = TRUE;
+       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+
+       /* Gina24 comes in both '301 and '361 flavors */
+       if (chip->device_id == DEVICE_ID_56361) {
+               chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP];
+               chip->digital_modes =
+                       ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+                       ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+                       ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+       } else {
+               chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP];
+               chip->digital_modes =
+                       ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+                       ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+                       ECHOCAPS_HAS_DIGITAL_MODE_ADAT |
+                       ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_CDROM;
+       }
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+       snd_assert(err >= 0, return err);
+       err = set_professional_spdif(chip, TRUE);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock
+          detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+               clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+               clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ESYNC)
+               clock_bits |= ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96;
+
+       return clock_bits;
+}
+
+
+
+/* Gina24 has an ASIC on the PCI card which must be loaded for anything
+interesting to happen. */
+static int load_asic(struct echoaudio *chip)
+{
+       u32 control_reg;
+       int err;
+       const struct firmware *fw;
+
+       if (chip->asic_loaded)
+               return 1;
+
+       /* Give the DSP a few milliseconds to settle down */
+       mdelay(10);
+
+       /* Pick the correct ASIC for '301 or '361 Gina24 */
+       if (chip->device_id == DEVICE_ID_56361)
+               fw = &card_fw[FW_GINA24_361_ASIC];
+       else
+               fw = &card_fw[FW_GINA24_301_ASIC];
+
+       if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0)
+               return err;
+
+       chip->asic_code = fw;
+
+       /* Now give the new ASIC a little time to set up */
+       mdelay(10);
+       /* See if it worked */
+       err = check_asic_status(chip);
+
+       /* Set up the control register if the load succeeded -
+          48 kHz, internal clock, S/PDIF RCA mode */
+       if (!err) {
+               control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
+               err = write_control_reg(chip, control_reg, TRUE);
+       }
+       DE_INIT(("load_asic() done\n"));
+       return err;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg, clock;
+
+       snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+                  return -EINVAL);
+
+       /* Only set the clock for internal mode. */
+       if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+               DE_ACT(("set_sample_rate: Cannot set sample rate - "
+                       "clock not set to CLK_CLOCKININTERNAL\n"));
+               /* Save the rate anyhow */
+               chip->comm_page->sample_rate = cpu_to_le32(rate);
+               chip->sample_rate = rate;
+               return 0;
+       }
+
+       clock = 0;
+
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK;
+
+       switch (rate) {
+       case 96000:
+               clock = GML_96KHZ;
+               break;
+       case 88200:
+               clock = GML_88KHZ;
+               break;
+       case 48000:
+               clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+               break;
+       case 44100:
+               clock = GML_44KHZ;
+               /* Professional mode ? */
+               if (control_reg & GML_SPDIF_PRO_MODE)
+                       clock |= GML_SPDIF_SAMPLE_RATE0;
+               break;
+       case 32000:
+               clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+                       GML_SPDIF_SAMPLE_RATE1;
+               break;
+       case 22050:
+               clock = GML_22KHZ;
+               break;
+       case 16000:
+               clock = GML_16KHZ;
+               break;
+       case 11025:
+               clock = GML_11KHZ;
+               break;
+       case 8000:
+               clock = GML_8KHZ;
+               break;
+       default:
+               DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+               return -EINVAL;
+       }
+
+       control_reg |= clock;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP */
+       chip->sample_rate = rate;
+       DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+
+       return write_control_reg(chip, control_reg, FALSE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       u32 control_reg, clocks_from_dsp;
+
+       DE_ACT(("set_input_clock:\n"));
+
+       /* Mask off the clock select bits */
+       control_reg = le32_to_cpu(chip->comm_page->control_register) &
+               GML_CLOCK_CLEAR_MASK;
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       switch (clock) {
+       case ECHO_CLOCK_INTERNAL:
+               DE_ACT(("Set Gina24 clock to INTERNAL\n"));
+               chip->input_clock = ECHO_CLOCK_INTERNAL;
+               return set_sample_rate(chip, chip->sample_rate);
+       case ECHO_CLOCK_SPDIF:
+               if (chip->digital_mode == DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               DE_ACT(("Set Gina24 clock to SPDIF\n"));
+               control_reg |= GML_SPDIF_CLOCK;
+               if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
+                       control_reg |= GML_DOUBLE_SPEED_MODE;
+               else
+                       control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_ADAT:
+               if (chip->digital_mode != DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               DE_ACT(("Set Gina24 clock to ADAT\n"));
+               control_reg |= GML_ADAT_CLOCK;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_ESYNC:
+               DE_ACT(("Set Gina24 clock to ESYNC\n"));
+               control_reg |= GML_ESYNC_CLOCK;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_ESYNC96:
+               DE_ACT(("Set Gina24 clock to ESYNC96\n"));
+               control_reg |= GML_ESYNC_CLOCK | GML_DOUBLE_SPEED_MODE;
+               break;
+       default:
+               DE_ACT(("Input clock 0x%x not supported for Gina24\n", clock));
+               return -EINVAL;
+       }
+
+       chip->input_clock = clock;
+       return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+       u32 control_reg;
+       int err, incompatible_clock;
+
+       /* Set clock to "internal" if it's not compatible with the new mode */
+       incompatible_clock = FALSE;
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+       case DIGITAL_MODE_SPDIF_CDROM:
+       case DIGITAL_MODE_SPDIF_RCA:
+               if (chip->input_clock == ECHO_CLOCK_ADAT)
+                       incompatible_clock = TRUE;
+               break;
+       case DIGITAL_MODE_ADAT:
+               if (chip->input_clock == ECHO_CLOCK_SPDIF)
+                       incompatible_clock = TRUE;
+               break;
+       default:
+               DE_ACT(("Digital mode not supported: %d\n", mode));
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&chip->lock);
+
+       if (incompatible_clock) {       /* Switch to 48KHz, internal */
+               chip->sample_rate = 48000;
+               set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+       }
+
+       /* Clear the current digital mode */
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+       /* Tweak the control reg */
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+               control_reg |= GML_SPDIF_OPTICAL_MODE;
+               break;
+       case DIGITAL_MODE_SPDIF_CDROM:
+               /* '361 Gina24 cards do not have the S/PDIF CD-ROM mode */
+               if (chip->device_id == DEVICE_ID_56301)
+                       control_reg |= GML_SPDIF_CDROM_MODE;
+               break;
+       case DIGITAL_MODE_SPDIF_RCA:
+               /* GML_SPDIF_OPTICAL_MODE bit cleared */
+               break;
+       case DIGITAL_MODE_ADAT:
+               control_reg |= GML_ADAT_MODE;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       }
+
+       err = write_control_reg(chip, control_reg, TRUE);
+       spin_unlock_irq(&chip->lock);
+       if (err < 0)
+               return err;
+       chip->digital_mode = mode;
+
+       DE_ACT(("set_digital_mode to %d\n", chip->digital_mode));
+       return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/indigo.c b/sound/pci/echoaudio/indigo.c
new file mode 100644 (file)
index 0000000..bfd2467
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO
+#define ECHOCARD_NAME "Indigo"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 0 */
+#define PX_ANALOG_IN   8       /* 0 */
+#define PX_DIGITAL_IN  8       /* 0 */
+#define PX_NUM         8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 2 */
+#define BX_DIGITAL_OUT 2       /* 0 */
+#define BX_ANALOG_IN   2       /* 0 */
+#define BX_DIGITAL_IN  2       /* 0 */
+#define BX_NUM         2
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER  0
+#define FW_INDIGO_DSP  1
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "indigo_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0},      /* Indigo */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 32000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+};
+
+#include "indigo_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigo_dsp.c b/sound/pci/echoaudio/indigo_dsp.c
new file mode 100644 (file)
index 0000000..d6ac773
--- /dev/null
@@ -0,0 +1,170 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Indigo\n"));
+       snd_assert((subdevice_id & 0xfff0) == INDIGO, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP];
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       /* Default routing of the virtual channels: all vchannels are routed
+       to the stereo output */
+       set_vmixer_gain(chip, 0, 0, 0);
+       set_vmixer_gain(chip, 1, 1, 0);
+       set_vmixer_gain(chip, 0, 2, 0);
+       set_vmixer_gain(chip, 1, 3, 0);
+       set_vmixer_gain(chip, 0, 4, 0);
+       set_vmixer_gain(chip, 1, 5, 0);
+       set_vmixer_gain(chip, 0, 6, 0);
+       set_vmixer_gain(chip, 1, 7, 0);
+       err = update_vmixer_level(chip);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The Indigo has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg;
+
+       switch (rate) {
+       case 96000:
+               control_reg = MIA_96000;
+               break;
+       case 88200:
+               control_reg = MIA_88200;
+               break;
+       case 48000:
+               control_reg = MIA_48000;
+               break;
+       case 44100:
+               control_reg = MIA_44100;
+               break;
+       case 32000:
+               control_reg = MIA_32000;
+               break;
+       default:
+               DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+               return -EINVAL;
+       }
+
+       /* Set the control register if it has changed */
+       if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+               if (wait_handshake(chip))
+                       return -EIO;
+
+               chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP */
+               chip->comm_page->control_register = cpu_to_le32(control_reg);
+               chip->sample_rate = rate;
+
+               clear_handshake(chip);
+               return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+       }
+       return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain)
+{
+       int index;
+
+       snd_assert(pipe < num_pipes_out(chip) &&
+                  output < num_busses_out(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->vmixer_gain[output][pipe] = gain;
+       index = output * num_pipes_out(chip) + pipe;
+       chip->comm_page->vmixer[index] = gain;
+
+       DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+       return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/indigodj.c b/sound/pci/echoaudio/indigodj.c
new file mode 100644 (file)
index 0000000..8ed7ff1
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_DJ
+#define ECHOCARD_NAME "Indigo DJ"
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 0 */
+#define PX_ANALOG_IN   8       /* 0 */
+#define PX_DIGITAL_IN  8       /* 0 */
+#define PX_NUM         8
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 4 */
+#define BX_DIGITAL_OUT 4       /* 0 */
+#define BX_ANALOG_IN   4       /* 0 */
+#define BX_DIGITAL_IN  4       /* 0 */
+#define BX_NUM         4
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER          0
+#define FW_INDIGO_DJ_DSP       1
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "indigo_dj_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0},      /* Indigo DJ*/
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 32000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 4,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+};
+
+#include "indigodj_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigodj_dsp.c b/sound/pci/echoaudio/indigodj_dsp.c
new file mode 100644 (file)
index 0000000..500e150
--- /dev/null
@@ -0,0 +1,170 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Indigo DJ\n"));
+       snd_assert((subdevice_id & 0xfff0) == INDIGO_DJ, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP];
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       /* Default routing of the virtual channels: vchannels 0-3 and
+       vchannels 4-7 are routed to real channels 0-4 */
+       set_vmixer_gain(chip, 0, 0, 0);
+       set_vmixer_gain(chip, 1, 1, 0);
+       set_vmixer_gain(chip, 2, 2, 0);
+       set_vmixer_gain(chip, 3, 3, 0);
+       set_vmixer_gain(chip, 0, 4, 0);
+       set_vmixer_gain(chip, 1, 5, 0);
+       set_vmixer_gain(chip, 2, 6, 0);
+       set_vmixer_gain(chip, 3, 7, 0);
+       err = update_vmixer_level(chip);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoDJ has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg;
+
+       switch (rate) {
+       case 96000:
+               control_reg = MIA_96000;
+               break;
+       case 88200:
+               control_reg = MIA_88200;
+               break;
+       case 48000:
+               control_reg = MIA_48000;
+               break;
+       case 44100:
+               control_reg = MIA_44100;
+               break;
+       case 32000:
+               control_reg = MIA_32000;
+               break;
+       default:
+               DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+               return -EINVAL;
+       }
+
+       /* Set the control register if it has changed */
+       if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+               if (wait_handshake(chip))
+                       return -EIO;
+
+               chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP */
+               chip->comm_page->control_register = cpu_to_le32(control_reg);
+               chip->sample_rate = rate;
+
+               clear_handshake(chip);
+               return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+       }
+       return 0;
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain)
+{
+       int index;
+
+       snd_assert(pipe < num_pipes_out(chip) &&
+                  output < num_busses_out(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->vmixer_gain[output][pipe] = gain;
+       index = output * num_pipes_out(chip) + pipe;
+       chip->comm_page->vmixer[index] = gain;
+
+       DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+       return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/indigoio.c b/sound/pci/echoaudio/indigoio.c
new file mode 100644 (file)
index 0000000..a8788e9
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define INDIGO_FAMILY
+#define ECHOCARD_INDIGO_IO
+#define ECHOCARD_NAME "Indigo IO"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 0 */
+#define PX_ANALOG_IN   8       /* 2 */
+#define PX_DIGITAL_IN  10      /* 0 */
+#define PX_NUM         10
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 2 */
+#define BX_DIGITAL_OUT 2       /* 0 */
+#define BX_ANALOG_IN   2       /* 2 */
+#define BX_DIGITAL_IN  4       /* 0 */
+#define BX_NUM         4
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER          0
+#define FW_INDIGO_IO_DSP       1
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "indigo_io_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0},      /* Indigo IO*/
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 32000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+};
+
+#include "indigoio_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+
diff --git a/sound/pci/echoaudio/indigoio_dsp.c b/sound/pci/echoaudio/indigoio_dsp.c
new file mode 100644 (file)
index 0000000..f3ad13d
--- /dev/null
@@ -0,0 +1,141 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Indigo IO\n"));
+       snd_assert((subdevice_id & 0xfff0) == INDIGO_IO, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP];
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       /* Default routing of the virtual channels: all vchannels are routed
+       to the stereo output */
+       set_vmixer_gain(chip, 0, 0, 0);
+       set_vmixer_gain(chip, 1, 1, 0);
+       set_vmixer_gain(chip, 0, 2, 0);
+       set_vmixer_gain(chip, 1, 3, 0);
+       set_vmixer_gain(chip, 0, 4, 0);
+       set_vmixer_gain(chip, 1, 5, 0);
+       set_vmixer_gain(chip, 0, 6, 0);
+       set_vmixer_gain(chip, 1, 7, 0);
+       err = update_vmixer_level(chip);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       return ECHO_CLOCK_BIT_INTERNAL;
+}
+
+
+
+/* The IndigoIO has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->sample_rate = rate;
+       chip->comm_page->sample_rate = cpu_to_le32(rate);
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain)
+{
+       int index;
+
+       snd_assert(pipe < num_pipes_out(chip) &&
+                  output < num_busses_out(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->vmixer_gain[output][pipe] = gain;
+       index = output * num_pipes_out(chip) + pipe;
+       chip->comm_page->vmixer[index] = gain;
+
+       DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+       return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
diff --git a/sound/pci/echoaudio/layla20.c b/sound/pci/echoaudio/layla20.c
new file mode 100644 (file)
index 0000000..e503d74
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHOGALS_FAMILY
+#define ECHOCARD_LAYLA20
+#define ECHOCARD_NAME "Layla20"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_GAIN
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT      FALSE
+#define ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 10 */
+#define PX_DIGITAL_OUT 10      /*  2 */
+#define PX_ANALOG_IN   12      /*  8 */
+#define PX_DIGITAL_IN  20      /*  2 */
+#define PX_NUM         22
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 10 */
+#define BX_DIGITAL_OUT 10      /*  2 */
+#define BX_ANALOG_IN   12      /*  8 */
+#define BX_DIGITAL_IN  20      /*  2 */
+#define BX_NUM         22
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_LAYLA20_DSP 0
+#define FW_LAYLA20_ASIC        1
+
+static const struct firmware card_fw[] = {
+       {0, "layla20_dsp.fw"},
+       {0, "layla20_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0},      /* DSP 56301 Layla20 rev.0 */
+       {0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0},      /* DSP 56301 Layla20 rev.1 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_CONTINUOUS,
+       .rate_min = 8000,
+       .rate_max = 50000,
+       .channels_min = 1,
+       .channels_max = 10,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+#include "layla20_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/layla20_dsp.c b/sound/pci/echoaudio/layla20_dsp.c
new file mode 100644 (file)
index 0000000..990c9a6
--- /dev/null
@@ -0,0 +1,290 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int read_dsp(struct echoaudio *chip, u32 *data);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+                            const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+static int update_flags(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Layla20\n"));
+       snd_assert((subdevice_id & 0xfff0) == LAYLA20, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->has_midi = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP];
+       chip->input_clock_types =
+               ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+               ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
+       chip->output_clock_types =
+               ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       err = set_professional_spdif(chip, TRUE);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+               clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+       if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_WORD) {
+               if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SUPER)
+                       clock_bits |= ECHO_CLOCK_BIT_SUPER;
+               else
+                       clock_bits |= ECHO_CLOCK_BIT_WORD;
+       }
+
+       return clock_bits;
+}
+
+
+
+/* ASIC status check - some cards have one or two ASICs that need to be
+loaded.  Once that load is complete, this function is called to see if
+the load was successful.
+If this load fails, it does not necessarily mean that the hardware is
+defective - the external box may be disconnected or turned off.
+This routine sometimes fails for Layla20; for Layla20, the loop runs
+5 times and succeeds if it wins on three of the loops. */
+static int check_asic_status(struct echoaudio *chip)
+{
+       u32 asic_status;
+       int goodcnt, i;
+
+       chip->asic_loaded = FALSE;
+       for (i = goodcnt = 0; i < 5; i++) {
+               send_vector(chip, DSP_VC_TEST_ASIC);
+
+               /* The DSP will return a value to indicate whether or not
+                  the ASIC is currently loaded */
+               if (read_dsp(chip, &asic_status) < 0) {
+                       DE_ACT(("check_asic_status: failed on read_dsp\n"));
+                       return -EIO;
+               }
+
+               if (asic_status == ASIC_ALREADY_LOADED) {
+                       if (++goodcnt == 3) {
+                               chip->asic_loaded = TRUE;
+                               return 0;
+                       }
+               }
+       }
+       return -EIO;
+}
+
+
+
+/* Layla20 has an ASIC in the external box */
+static int load_asic(struct echoaudio *chip)
+{
+       int err;
+
+       if (chip->asic_loaded)
+               return 0;
+
+       err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC,
+                               &card_fw[FW_LAYLA20_ASIC]);
+       if (err < 0)
+               return err;
+
+       /* Check if ASIC is alive and well. */
+       return check_asic_status(chip);
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       snd_assert(rate >= 8000 && rate <= 50000, return -EINVAL);
+
+       /* Only set the clock for internal mode. Do not return failure,
+          simply treat it as a non-event. */
+       if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+               DE_ACT(("set_sample_rate: Cannot set sample rate - "
+                       "clock not set to CLK_CLOCKININTERNAL\n"));
+               chip->comm_page->sample_rate = cpu_to_le32(rate);
+               chip->sample_rate = rate;
+               return 0;
+       }
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       DE_ACT(("set_sample_rate(%d)\n", rate));
+       chip->sample_rate = rate;
+       chip->comm_page->sample_rate = cpu_to_le32(rate);
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_LAYLA_SAMPLE_RATE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock_source)
+{
+       u16 clock;
+       u32 rate;
+
+       DE_ACT(("set_input_clock:\n"));
+       rate = 0;
+       switch (clock_source) {
+       case ECHO_CLOCK_INTERNAL:
+               DE_ACT(("Set Layla20 clock to INTERNAL\n"));
+               rate = chip->sample_rate;
+               clock = LAYLA20_CLOCK_INTERNAL;
+               break;
+       case ECHO_CLOCK_SPDIF:
+               DE_ACT(("Set Layla20 clock to SPDIF\n"));
+               clock = LAYLA20_CLOCK_SPDIF;
+               break;
+       case ECHO_CLOCK_WORD:
+               DE_ACT(("Set Layla20 clock to WORD\n"));
+               clock = LAYLA20_CLOCK_WORD;
+               break;
+       case ECHO_CLOCK_SUPER:
+               DE_ACT(("Set Layla20 clock to SUPER\n"));
+               clock = LAYLA20_CLOCK_SUPER;
+               break;
+       default:
+               DE_ACT(("Input clock 0x%x not supported for Layla24\n",
+                       clock_source));
+               return -EINVAL;
+       }
+       chip->input_clock = clock_source;
+
+       chip->comm_page->input_clock = cpu_to_le16(clock);
+       clear_handshake(chip);
+       send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+
+       if (rate)
+               set_sample_rate(chip, rate);
+
+       return 0;
+}
+
+
+
+static int set_output_clock(struct echoaudio *chip, u16 clock)
+{
+       DE_ACT(("set_output_clock: %d\n", clock));
+       switch (clock) {
+       case ECHO_CLOCK_SUPER:
+               clock = LAYLA20_OUTPUT_CLOCK_SUPER;
+               break;
+       case ECHO_CLOCK_WORD:
+               clock = LAYLA20_OUTPUT_CLOCK_WORD;
+               break;
+       default:
+               DE_ACT(("set_output_clock wrong clock\n"));
+               return -EINVAL;
+       }
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->comm_page->output_clock = cpu_to_le16(clock);
+       chip->output_clock = clock;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+}
+
+
+
+/* Set input bus gain (one unit is 0.5dB !) */
+static int set_input_gain(struct echoaudio *chip, u16 input, int gain)
+{
+       snd_assert(input < num_busses_in(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->input_gain[input] = gain;
+       gain += GL20_INPUT_GAIN_MAGIC_NUMBER;
+       chip->comm_page->line_in_level[input] = gain;
+       return 0;
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+       DE_ACT(("set_professional_spdif %d\n", prof));
+       if (prof)
+               chip->comm_page->flags |=
+                       __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+       else
+               chip->comm_page->flags &=
+                       ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+       chip->professional_spdif = prof;
+       return update_flags(chip);
+}
diff --git a/sound/pci/echoaudio/layla24.c b/sound/pci/echoaudio/layla24.c
new file mode 100644 (file)
index 0000000..d4581fd
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_LAYLA24
+#define ECHOCARD_NAME "Layla24"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT      6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 8 */
+#define PX_ANALOG_IN   16      /* 8 */
+#define PX_DIGITAL_IN  24      /* 8 */
+#define PX_NUM         32
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 8 */
+#define BX_DIGITAL_OUT 8       /* 8 */
+#define BX_ANALOG_IN   16      /* 8 */
+#define BX_DIGITAL_IN  24      /* 8 */
+#define BX_NUM         32
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER          0
+#define FW_LAYLA24_DSP         1
+#define FW_LAYLA24_1_ASIC      2
+#define FW_LAYLA24_2A_ASIC     3
+#define FW_LAYLA24_2S_ASIC     4
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "layla24_dsp.fw"},
+       {0, "layla24_1_asic.fw"},
+       {0, "layla24_2A_asic.fw"},
+       {0, "layla24_2S_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0},      /* DSP 56361 Layla24 rev.0 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_8000_96000,
+       .rate_min = 8000,
+       .rate_max = 100000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "layla24_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/layla24_dsp.c b/sound/pci/echoaudio/layla24_dsp.c
new file mode 100644 (file)
index 0000000..7ec5b63
--- /dev/null
@@ -0,0 +1,394 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+                            const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Layla24\n"));
+       snd_assert((subdevice_id & 0xfff0) == LAYLA24, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->has_midi = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP];
+       chip->input_clock_types =
+               ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+               ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
+       chip->digital_modes =
+               ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+               ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+               ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+       chip->digital_mode =            DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->digital_in_automute = TRUE;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+       snd_assert(err >= 0, return err);
+       err = set_professional_spdif(chip, TRUE);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+               clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+               clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD)
+               clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+       return clock_bits;
+}
+
+
+
+/* Layla24 has an ASIC on the PCI card and another ASIC in the external box;
+both need to be loaded. */
+static int load_asic(struct echoaudio *chip)
+{
+       int err;
+
+       if (chip->asic_loaded)
+               return 1;
+
+       DE_INIT(("load_asic\n"));
+
+       /* Give the DSP a few milliseconds to settle down */
+       mdelay(10);
+
+       /* Load the ASIC for the PCI card */
+       err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
+                               &card_fw[FW_LAYLA24_1_ASIC]);
+       if (err < 0)
+               return err;
+
+       chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC];
+
+       /* Now give the new ASIC a little time to set up */
+       mdelay(10);
+
+       /* Do the external one */
+       err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
+                               &card_fw[FW_LAYLA24_2S_ASIC]);
+       if (err < 0)
+               return FALSE;
+
+       /* Now give the external ASIC a little time to set up */
+       mdelay(10);
+
+       /* See if it worked */
+       err = check_asic_status(chip);
+
+       /* Set up the control register if the load succeeded -
+          48 kHz, internal clock, S/PDIF RCA mode */
+       if (!err)
+               err = write_control_reg(chip, GML_CONVERTER_ENABLE | GML_48KHZ,
+                                       TRUE);
+       
+       DE_INIT(("load_asic() done\n"));
+       return err;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg, clock, base_rate;
+
+       snd_assert(rate < 50000 || chip->digital_mode != DIGITAL_MODE_ADAT,
+                  return -EINVAL);
+
+       /* Only set the clock for internal mode. */
+       if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+               DE_ACT(("set_sample_rate: Cannot set sample rate - "
+                       "clock not set to CLK_CLOCKININTERNAL\n"));
+               /* Save the rate anyhow */
+               chip->comm_page->sample_rate = cpu_to_le32(rate);
+               chip->sample_rate = rate;
+               return 0;
+       }
+
+       /* Get the control register & clear the appropriate bits */
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_CLOCK_CLEAR_MASK & GML_SPDIF_RATE_CLEAR_MASK;
+
+       clock = 0;
+
+       switch (rate) {
+       case 96000:
+               clock = GML_96KHZ;
+               break;
+       case 88200:
+               clock = GML_88KHZ;
+               break;
+       case 48000:
+               clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+               break;
+       case 44100:
+               clock = GML_44KHZ;
+               /* Professional mode */
+               if (control_reg & GML_SPDIF_PRO_MODE)
+                       clock |= GML_SPDIF_SAMPLE_RATE0;
+               break;
+       case 32000:
+               clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+                       GML_SPDIF_SAMPLE_RATE1;
+               break;
+       case 22050:
+               clock = GML_22KHZ;
+               break;
+       case 16000:
+               clock = GML_16KHZ;
+               break;
+       case 11025:
+               clock = GML_11KHZ;
+               break;
+       case 8000:
+               clock = GML_8KHZ;
+               break;
+       default:
+               /* If this is a non-standard rate, then the driver needs to
+               use Layla24's special "continuous frequency" mode */
+               clock = LAYLA24_CONTINUOUS_CLOCK;
+               if (rate > 50000) {
+                       base_rate = rate >> 1;
+                       control_reg |= GML_DOUBLE_SPEED_MODE;
+               } else {
+                       base_rate = rate;
+               }
+
+               if (base_rate < 25000)
+                       base_rate = 25000;
+
+               if (wait_handshake(chip))
+                       return -EIO;
+
+               chip->comm_page->sample_rate =
+                       cpu_to_le32(LAYLA24_MAGIC_NUMBER / base_rate - 2);
+
+               clear_handshake(chip);
+               send_vector(chip, DSP_VC_SET_LAYLA24_FREQUENCY_REG);
+       }
+
+       control_reg |= clock;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP ? */
+       chip->sample_rate = rate;
+       DE_ACT(("set_sample_rate: %d clock %d\n", rate, control_reg));
+
+       return write_control_reg(chip, control_reg, FALSE);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       u32 control_reg, clocks_from_dsp;
+
+       /* Mask off the clock select bits */
+       control_reg = le32_to_cpu(chip->comm_page->control_register) &
+               GML_CLOCK_CLEAR_MASK;
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       /* Pick the new clock */
+       switch (clock) {
+       case ECHO_CLOCK_INTERNAL:
+               DE_ACT(("Set Layla24 clock to INTERNAL\n"));
+               chip->input_clock = ECHO_CLOCK_INTERNAL;
+               return set_sample_rate(chip, chip->sample_rate);
+       case ECHO_CLOCK_SPDIF:
+               if (chip->digital_mode == DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               control_reg |= GML_SPDIF_CLOCK;
+               /* Layla24 doesn't support 96KHz S/PDIF */
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               DE_ACT(("Set Layla24 clock to SPDIF\n"));
+               break;
+       case ECHO_CLOCK_WORD:
+               control_reg |= GML_WORD_CLOCK;
+               if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96)
+                       control_reg |= GML_DOUBLE_SPEED_MODE;
+               else
+                       control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               DE_ACT(("Set Layla24 clock to WORD\n"));
+               break;
+       case ECHO_CLOCK_ADAT:
+               if (chip->digital_mode != DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               control_reg |= GML_ADAT_CLOCK;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               DE_ACT(("Set Layla24 clock to ADAT\n"));
+               break;
+       default:
+               DE_ACT(("Input clock 0x%x not supported for Layla24\n", clock));
+               return -EINVAL;
+       }
+
+       chip->input_clock = clock;
+       return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+/* Depending on what digital mode you want, Layla24 needs different ASICs
+loaded.  This function checks the ASIC needed for the new mode and sees
+if it matches the one already loaded. */
+static int switch_asic(struct echoaudio *chip, const struct firmware *asic)
+{
+       s8 *monitors;
+
+       /*  Check to see if this is already loaded */
+       if (asic != chip->asic_code) {
+               monitors = kmalloc(MONITOR_ARRAY_SIZE, GFP_KERNEL);
+               if (! monitors)
+                       return -ENOMEM;
+
+               memcpy(monitors, chip->comm_page->monitors, MONITOR_ARRAY_SIZE);
+               memset(chip->comm_page->monitors, ECHOGAIN_MUTED,
+                      MONITOR_ARRAY_SIZE);
+
+               /* Load the desired ASIC */
+               if (load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
+                                     asic) < 0) {
+                       memcpy(chip->comm_page->monitors, monitors,
+                              MONITOR_ARRAY_SIZE);
+                       kfree(monitors);
+                       return -EIO;
+               }
+               chip->asic_code = asic;
+               memcpy(chip->comm_page->monitors, monitors, MONITOR_ARRAY_SIZE);
+               kfree(monitors);
+       }
+
+       return 0;
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+       u32 control_reg;
+       int err, incompatible_clock;
+       const struct firmware *asic;
+
+       /* Set clock to "internal" if it's not compatible with the new mode */
+       incompatible_clock = FALSE;
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+       case DIGITAL_MODE_SPDIF_RCA:
+               if (chip->input_clock == ECHO_CLOCK_ADAT)
+                       incompatible_clock = TRUE;
+               asic = &card_fw[FW_LAYLA24_2S_ASIC];
+               break;
+       case DIGITAL_MODE_ADAT:
+               if (chip->input_clock == ECHO_CLOCK_SPDIF)
+                       incompatible_clock = TRUE;
+               asic = &card_fw[FW_LAYLA24_2A_ASIC];
+               break;
+       default:
+               DE_ACT(("Digital mode not supported: %d\n", mode));
+               return -EINVAL;
+       }
+
+       if (incompatible_clock) {       /* Switch to 48KHz, internal */
+               chip->sample_rate = 48000;
+               spin_lock_irq(&chip->lock);
+               set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+               spin_unlock_irq(&chip->lock);
+       }
+
+       /* switch_asic() can sleep */
+       if (switch_asic(chip, asic) < 0)
+               return -EIO;
+
+       spin_lock_irq(&chip->lock);
+
+       /* Tweak the control register */
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+               control_reg |= GML_SPDIF_OPTICAL_MODE;
+               break;
+       case DIGITAL_MODE_SPDIF_RCA:
+               /* GML_SPDIF_OPTICAL_MODE bit cleared */
+               break;
+       case DIGITAL_MODE_ADAT:
+               control_reg |= GML_ADAT_MODE;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       }
+
+       err = write_control_reg(chip, control_reg, TRUE);
+       spin_unlock_irq(&chip->lock);
+       if (err < 0)
+               return err;
+       chip->digital_mode = mode;
+
+       DE_ACT(("set_digital_mode to %d\n", mode));
+       return incompatible_clock;
+}
diff --git a/sound/pci/echoaudio/mia.c b/sound/pci/echoaudio/mia.c
new file mode 100644 (file)
index 0000000..be40c64
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_MIA
+#define ECHOCARD_NAME "Mia"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_INPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_OUTPUT_NOMINAL_LEVEL
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_VMIXER
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT      FALSE
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+#define ECHOCARD_HAS_MIDI
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 8 */
+#define PX_DIGITAL_OUT 8       /* 0 */
+#define PX_ANALOG_IN   8       /* 2 */
+#define PX_DIGITAL_IN  10      /* 2 */
+#define PX_NUM         12
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 2 */
+#define BX_DIGITAL_OUT 2       /* 2 */
+#define BX_ANALOG_IN   4       /* 2 */
+#define BX_DIGITAL_IN  6       /* 2 */
+#define BX_NUM         8
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER  0
+#define FW_MIA_DSP     1
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "mia_dsp.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0},      /* DSP 56361 Mia rev.0 */
+       {0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0},      /* DSP 56361 Mia rev.1 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_32000 |
+                       SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 8000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "mia_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio.c"
+#include "midi.c"
diff --git a/sound/pci/echoaudio/mia_dsp.c b/sound/pci/echoaudio/mia_dsp.c
new file mode 100644 (file)
index 0000000..891c705
--- /dev/null
@@ -0,0 +1,229 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int update_flags(struct echoaudio *chip);
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain);
+static int update_vmixer_level(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Mia\n"));
+       snd_assert((subdevice_id & 0xfff0) == MIA, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->dsp_code_to_load = &card_fw[FW_MIA_DSP];
+       /* Since this card has no ASIC, mark it as loaded so everything
+          works OK */
+       chip->asic_loaded = TRUE;
+       if ((subdevice_id & 0x0000f) == MIA_MIDI_REV)
+               chip->has_midi = TRUE;
+       chip->input_clock_types = ECHO_CLOCK_BIT_INTERNAL |
+               ECHO_CLOCK_BIT_SPDIF;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)))
+               return err;
+
+       /* Default routing of the virtual channels: vchannels 0-3 go to analog
+       outputs and vchannels 4-7 go to S/PDIF outputs */
+       set_vmixer_gain(chip, 0, 0, 0);
+       set_vmixer_gain(chip, 1, 1, 0);
+       set_vmixer_gain(chip, 0, 2, 0);
+       set_vmixer_gain(chip, 1, 3, 0);
+       set_vmixer_gain(chip, 2, 4, 0);
+       set_vmixer_gain(chip, 3, 5, 0);
+       set_vmixer_gain(chip, 2, 6, 0);
+       set_vmixer_gain(chip, 3, 7, 0);
+       err = update_vmixer_level(chip);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock
+          detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GLDM_CLOCK_DETECT_BIT_SPDIF)
+               clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+       return clock_bits;
+}
+
+
+
+/* The Mia has no ASIC. Just do nothing */
+static int load_asic(struct echoaudio *chip)
+{
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg;
+
+       switch (rate) {
+       case 96000:
+               control_reg = MIA_96000;
+               break;
+       case 88200:
+               control_reg = MIA_88200;
+               break;
+       case 48000:
+               control_reg = MIA_48000;
+               break;
+       case 44100:
+               control_reg = MIA_44100;
+               break;
+       case 32000:
+               control_reg = MIA_32000;
+               break;
+       default:
+               DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+               return -EINVAL;
+       }
+
+       /* Override the clock setting if this Mia is set to S/PDIF clock */
+       if (chip->input_clock == ECHO_CLOCK_SPDIF)
+               control_reg |= MIA_SPDIF;
+
+       /* Set the control register if it has changed */
+       if (control_reg != le32_to_cpu(chip->comm_page->control_register)) {
+               if (wait_handshake(chip))
+                       return -EIO;
+
+               chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP */
+               chip->comm_page->control_register = cpu_to_le32(control_reg);
+               chip->sample_rate = rate;
+
+               clear_handshake(chip);
+               return send_vector(chip, DSP_VC_UPDATE_CLOCKS);
+       }
+       return 0;
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       DE_ACT(("set_input_clock(%d)\n", clock));
+       snd_assert(clock == ECHO_CLOCK_INTERNAL || clock == ECHO_CLOCK_SPDIF,
+                  return -EINVAL);
+
+       chip->input_clock = clock;
+       return set_sample_rate(chip, chip->sample_rate);
+}
+
+
+
+/* This function routes the sound from a virtual channel to a real output */
+static int set_vmixer_gain(struct echoaudio *chip, u16 output, u16 pipe,
+                          int gain)
+{
+       int index;
+
+       snd_assert(pipe < num_pipes_out(chip) &&
+                  output < num_busses_out(chip), return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       chip->vmixer_gain[output][pipe] = gain;
+       index = output * num_pipes_out(chip) + pipe;
+       chip->comm_page->vmixer[index] = gain;
+
+       DE_ACT(("set_vmixer_gain: pipe %d, out %d = %d\n", pipe, output, gain));
+       return 0;
+}
+
+
+
+/* Tell the DSP to read and update virtual mixer levels in comm page. */
+static int update_vmixer_level(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_SET_VMIXER_GAIN);
+}
+
+
+
+/* Tell the DSP to reread the flags from the comm page */
+static int update_flags(struct echoaudio *chip)
+{
+       if (wait_handshake(chip))
+               return -EIO;
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+static int set_professional_spdif(struct echoaudio *chip, char prof)
+{
+       DE_ACT(("set_professional_spdif %d\n", prof));
+       if (prof)
+               chip->comm_page->flags |=
+                       __constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+       else
+               chip->comm_page->flags &=
+                       ~__constant_cpu_to_le32(DSP_FLAG_PROFESSIONAL_SPDIF);
+       chip->professional_spdif = prof;
+       return update_flags(chip);
+}
+
diff --git a/sound/pci/echoaudio/midi.c b/sound/pci/echoaudio/midi.c
new file mode 100644 (file)
index 0000000..5919b5c
--- /dev/null
@@ -0,0 +1,327 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+/******************************************************************************
+       MIDI lowlevel code
+******************************************************************************/
+
+/* Start and stop Midi input */
+static int enable_midi_input(struct echoaudio *chip, char enable)
+{
+       DE_MID(("enable_midi_input(%d)\n", enable));
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       if (enable) {
+               chip->mtc_state = MIDI_IN_STATE_NORMAL;
+               chip->comm_page->flags |=
+                       _constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+       } else
+               chip->comm_page->flags &=
+                       ~__constant_cpu_to_le32(DSP_FLAG_MIDI_INPUT);
+
+       clear_handshake(chip);
+       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+}
+
+
+
+/* Send a buffer full of MIDI data to the DSP
+Returns how many actually written or < 0 on error */
+static int write_midi(struct echoaudio *chip, u8 *data, int bytes)
+{
+       snd_assert(bytes > 0 && bytes < MIDI_OUT_BUFFER_SIZE, return -EINVAL);
+
+       if (wait_handshake(chip))
+               return -EIO;
+
+       /* HF4 indicates that it is safe to write MIDI output data */
+       if (! (get_dsp_register(chip, CHI32_STATUS_REG) & CHI32_STATUS_REG_HF4))
+               return 0;
+
+       chip->comm_page->midi_output[0] = bytes;
+       memcpy(&chip->comm_page->midi_output[1], data, bytes);
+       chip->comm_page->midi_out_free_count = 0;
+       clear_handshake(chip);
+       send_vector(chip, DSP_VC_MIDI_WRITE);
+       DE_MID(("write_midi: %d\n", bytes));
+       return bytes;
+}
+
+
+
+/* Run the state machine for MIDI input data
+MIDI time code sync isn't supported by this code right now, but you still need
+this state machine to parse the incoming MIDI data stream.  Every time the DSP
+sees a 0xF1 byte come in, it adds the DSP sample position to the MIDI data
+stream. The DSP sample position is represented as a 32 bit unsigned value,
+with the high 16 bits first, followed by the low 16 bits. Since these aren't
+real MIDI bytes, the following logic is needed to skip them. */
+static inline int mtc_process_data(struct echoaudio *chip, short midi_byte)
+{
+       switch (chip->mtc_state) {
+       case MIDI_IN_STATE_NORMAL:
+               if (midi_byte == 0xF1)
+                       chip->mtc_state = MIDI_IN_STATE_TS_HIGH;
+               break;
+       case MIDI_IN_STATE_TS_HIGH:
+               chip->mtc_state = MIDI_IN_STATE_TS_LOW;
+               return MIDI_IN_SKIP_DATA;
+               break;
+       case MIDI_IN_STATE_TS_LOW:
+               chip->mtc_state = MIDI_IN_STATE_F1_DATA;
+               return MIDI_IN_SKIP_DATA;
+               break;
+       case MIDI_IN_STATE_F1_DATA:
+               chip->mtc_state = MIDI_IN_STATE_NORMAL;
+               break;
+       }
+       return 0;
+}
+
+
+
+/* This function is called from the IRQ handler and it reads the midi data
+from the DSP's buffer.  It returns the number of bytes received. */
+static int midi_service_irq(struct echoaudio *chip)
+{
+       short int count, midi_byte, i, received;
+
+       /* The count is at index 0, followed by actual data */
+       count = le16_to_cpu(chip->comm_page->midi_input[0]);
+
+       snd_assert(count < MIDI_IN_BUFFER_SIZE, return 0);
+
+       /* Get the MIDI data from the comm page */
+       i = 1;
+       received = 0;
+       for (i = 1; i <= count; i++) {
+               /* Get the MIDI byte */
+               midi_byte = le16_to_cpu(chip->comm_page->midi_input[i]);
+
+               /* Parse the incoming MIDI stream. The incoming MIDI data
+               consists of MIDI bytes and timestamps for the MIDI time code
+               0xF1 bytes. mtc_process_data() is a little state machine that
+               parses the stream. If you get MIDI_IN_SKIP_DATA back, then
+               this is a timestamp byte, not a MIDI byte, so don't store it
+               in the MIDI input buffer. */
+               if (mtc_process_data(chip, midi_byte) == MIDI_IN_SKIP_DATA)
+                       continue;
+
+               chip->midi_buffer[received++] = (u8)midi_byte;
+       }
+
+       return received;
+}
+
+
+
+
+/******************************************************************************
+       MIDI interface
+******************************************************************************/
+
+static int snd_echo_midi_input_open(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->midi_in = substream;
+       DE_MID(("rawmidi_iopen\n"));
+       return 0;
+}
+
+
+
+static void snd_echo_midi_input_trigger(struct snd_rawmidi_substream *substream,
+                                       int up)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       if (up != chip->midi_input_enabled) {
+               spin_lock_irq(&chip->lock);
+               enable_midi_input(chip, up);
+               spin_unlock_irq(&chip->lock);
+               chip->midi_input_enabled = up;
+       }
+}
+
+
+
+static int snd_echo_midi_input_close(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->midi_in = NULL;
+       DE_MID(("rawmidi_iclose\n"));
+       return 0;
+}
+
+
+
+static int snd_echo_midi_output_open(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->tinuse = 0;
+       chip->midi_full = 0;
+       chip->midi_out = substream;
+       DE_MID(("rawmidi_oopen\n"));
+       return 0;
+}
+
+
+
+static void snd_echo_midi_output_write(unsigned long data)
+{
+       struct echoaudio *chip = (struct echoaudio *)data;
+       unsigned long flags;
+       int bytes, sent, time;
+       unsigned char buf[MIDI_OUT_BUFFER_SIZE - 1];
+
+       DE_MID(("snd_echo_midi_output_write\n"));
+       /* No interrupts are involved: we have to check at regular intervals
+       if the card's output buffer has room for new data. */
+       sent = bytes = 0;
+       spin_lock_irqsave(&chip->lock, flags);
+       chip->midi_full = 0;
+       if (chip->midi_out && !snd_rawmidi_transmit_empty(chip->midi_out)) {
+               bytes = snd_rawmidi_transmit_peek(chip->midi_out, buf,
+                                                 MIDI_OUT_BUFFER_SIZE - 1);
+               DE_MID(("Try to send %d bytes...\n", bytes));
+               sent = write_midi(chip, buf, bytes);
+               if (sent < 0) {
+                       snd_printk(KERN_ERR "write_midi() error %d\n", sent);
+                       /* retry later */
+                       sent = 9000;
+                       chip->midi_full = 1;
+               } else if (sent > 0) {
+                       DE_MID(("%d bytes sent\n", sent));
+                       snd_rawmidi_transmit_ack(chip->midi_out, sent);
+               } else {
+                       /* Buffer is full. DSP's internal buffer is 64 (128 ?)
+                       bytes long. Let's wait until half of them are sent */
+                       DE_MID(("Full\n"));
+                       sent = 32;
+                       chip->midi_full = 1;
+               }
+       }
+
+       /* We restart the timer only if there is some data left to send */
+       if (!snd_rawmidi_transmit_empty(chip->midi_out) && chip->tinuse) {
+               /* The timer will expire slightly after the data has been
+                  sent */
+               time = (sent << 3) / 25 + 1;    /* 8/25=0.32ms to send a byte */
+               mod_timer(&chip->timer, jiffies + (time * HZ + 999) / 1000);
+               DE_MID(("Timer armed(%d)\n", ((time * HZ + 999) / 1000)));
+       }
+       spin_unlock_irqrestore(&chip->lock, flags);
+}
+
+
+
+static void snd_echo_midi_output_trigger(struct snd_rawmidi_substream *substream,
+                                        int up)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       DE_MID(("snd_echo_midi_output_trigger(%d)\n", up));
+       spin_lock_irq(&chip->lock);
+       if (up) {
+               if (!chip->tinuse) {
+                       init_timer(&chip->timer);
+                       chip->timer.function = snd_echo_midi_output_write;
+                       chip->timer.data = (unsigned long)chip;
+                       chip->tinuse = 1;
+               }
+       } else {
+               if (chip->tinuse) {
+                       del_timer(&chip->timer);
+                       chip->tinuse = 0;
+                       DE_MID(("Timer removed\n"));
+               }
+       }
+       spin_unlock_irq(&chip->lock);
+
+       if (up && !chip->midi_full)
+               snd_echo_midi_output_write((unsigned long)chip);
+}
+
+
+
+static int snd_echo_midi_output_close(struct snd_rawmidi_substream *substream)
+{
+       struct echoaudio *chip = substream->rmidi->private_data;
+
+       chip->midi_out = NULL;
+       DE_MID(("rawmidi_oclose\n"));
+       return 0;
+}
+
+
+
+static struct snd_rawmidi_ops snd_echo_midi_input = {
+       .open = snd_echo_midi_input_open,
+       .close = snd_echo_midi_input_close,
+       .trigger = snd_echo_midi_input_trigger,
+};
+
+static struct snd_rawmidi_ops snd_echo_midi_output = {
+       .open = snd_echo_midi_output_open,
+       .close = snd_echo_midi_output_close,
+       .trigger = snd_echo_midi_output_trigger,
+};
+
+
+
+/* <--snd_echo_probe() */
+static int __devinit snd_echo_midi_create(struct snd_card *card,
+                                         struct echoaudio *chip)
+{
+       int err;
+
+       if ((err = snd_rawmidi_new(card, card->shortname, 0, 1, 1,
+                                  &chip->rmidi)) < 0)
+               return err;
+
+       strcpy(chip->rmidi->name, card->shortname);
+       chip->rmidi->private_data = chip;
+
+       snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                           &snd_echo_midi_input);
+       snd_rawmidi_set_ops(chip->rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &snd_echo_midi_output);
+
+       chip->rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT |
+               SNDRV_RAWMIDI_INFO_INPUT | SNDRV_RAWMIDI_INFO_DUPLEX;
+       DE_INIT(("MIDI ok\n"));
+       return 0;
+}
diff --git a/sound/pci/echoaudio/mona.c b/sound/pci/echoaudio/mona.c
new file mode 100644 (file)
index 0000000..5dc512a
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ *  ALSA driver for Echoaudio soundcards.
+ *  Copyright (C) 2003-2004 Giuliano Pochini <pochini@shiny.it>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#define ECHO24_FAMILY
+#define ECHOCARD_MONA
+#define ECHOCARD_NAME "Mona"
+#define ECHOCARD_HAS_MONITOR
+#define ECHOCARD_HAS_ASIC
+#define ECHOCARD_HAS_SUPER_INTERLEAVE
+#define ECHOCARD_HAS_DIGITAL_IO
+#define ECHOCARD_HAS_DIGITAL_IN_AUTOMUTE
+#define ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+#define ECHOCARD_HAS_EXTERNAL_CLOCK
+#define ECHOCARD_HAS_ADAT      6
+#define ECHOCARD_HAS_STEREO_BIG_ENDIAN32
+
+/* Pipe indexes */
+#define PX_ANALOG_OUT  0       /* 6 */
+#define PX_DIGITAL_OUT 6       /* 8 */
+#define PX_ANALOG_IN   14      /* 4 */
+#define PX_DIGITAL_IN  18      /* 8 */
+#define PX_NUM         26
+
+/* Bus indexes */
+#define BX_ANALOG_OUT  0       /* 6 */
+#define BX_DIGITAL_OUT 6       /* 8 */
+#define BX_ANALOG_IN   14      /* 4 */
+#define BX_DIGITAL_IN  18      /* 8 */
+#define BX_NUM         26
+
+
+#include <sound/driver.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/asoundef.h>
+#include <sound/initval.h>
+#include <asm/io.h>
+#include <asm/atomic.h>
+#include "echoaudio.h"
+
+#define FW_361_LOADER          0
+#define FW_MONA_301_DSP                1
+#define FW_MONA_361_DSP                2
+#define FW_MONA_301_1_ASIC48   3
+#define FW_MONA_301_1_ASIC96   4
+#define FW_MONA_361_1_ASIC48   5
+#define FW_MONA_361_1_ASIC96   6
+#define FW_MONA_2_ASIC         7
+
+static const struct firmware card_fw[] = {
+       {0, "loader_dsp.fw"},
+       {0, "mona_301_dsp.fw"},
+       {0, "mona_361_dsp.fw"},
+       {0, "mona_301_1_asic_48.fw"},
+       {0, "mona_301_1_asic_96.fw"},
+       {0, "mona_361_1_asic_48.fw"},
+       {0, "mona_361_1_asic_96.fw"},
+       {0, "mona_2_asic.fw"}
+};
+
+static struct pci_device_id snd_echo_ids[] = {
+       {0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0},      /* DSP 56301 Mona rev.0 */
+       {0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0},      /* DSP 56301 Mona rev.1 */
+       {0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0},      /* DSP 56301 Mona rev.2 */
+       {0x1057, 0x3410, 0xECC0, 0x0070, 0, 0, 0},      /* DSP 56361 Mona rev.0 */
+       {0x1057, 0x3410, 0xECC0, 0x0071, 0, 0, 0},      /* DSP 56361 Mona rev.1 */
+       {0x1057, 0x3410, 0xECC0, 0x0072, 0, 0, 0},      /* DSP 56361 Mona rev.2 */
+       {0,}
+};
+
+static struct snd_pcm_hardware pcm_hardware_skel = {
+       .info = SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_PAUSE |
+               SNDRV_PCM_INFO_SYNC_START,
+       .formats =      SNDRV_PCM_FMTBIT_U8 |
+                       SNDRV_PCM_FMTBIT_S16_LE |
+                       SNDRV_PCM_FMTBIT_S24_3LE |
+                       SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S32_BE,
+       .rates =        SNDRV_PCM_RATE_8000_48000 |
+                       SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000,
+       .rate_min = 8000,
+       .rate_max = 96000,
+       .channels_min = 1,
+       .channels_max = 8,
+       .buffer_bytes_max = 262144,
+       .period_bytes_min = 32,
+       .period_bytes_max = 131072,
+       .periods_min = 2,
+       .periods_max = 220,
+       /* One page (4k) contains 512 instructions. I don't know if the hw
+       supports lists longer than this. In this case periods_max=220 is a
+       safe limit to make sure the list never exceeds 512 instructions. */
+};
+
+
+#include "mona_dsp.c"
+#include "echoaudio_dsp.c"
+#include "echoaudio_gml.c"
+#include "echoaudio.c"
diff --git a/sound/pci/echoaudio/mona_dsp.c b/sound/pci/echoaudio/mona_dsp.c
new file mode 100644 (file)
index 0000000..c0b4bf0
--- /dev/null
@@ -0,0 +1,428 @@
+/****************************************************************************
+
+   Copyright Echo Digital Audio Corporation (c) 1998 - 2004
+   All rights reserved
+   www.echoaudio.com
+
+   This file is part of Echo Digital Audio's generic driver library.
+
+   Echo Digital Audio's generic driver library is free software;
+   you can redistribute it and/or modify it under the terms of
+   the GNU General Public License as published by the Free Software
+   Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA  02111-1307, USA.
+
+   *************************************************************************
+
+ Translation from C++ and adaptation for use in ALSA-Driver
+ were made by Giuliano Pochini <pochini@shiny.it>
+
+****************************************************************************/
+
+
+static int write_control_reg(struct echoaudio *chip, u32 value, char force);
+static int set_input_clock(struct echoaudio *chip, u16 clock);
+static int set_professional_spdif(struct echoaudio *chip, char prof);
+static int set_digital_mode(struct echoaudio *chip, u8 mode);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd,
+                            const struct firmware *asic);
+static int check_asic_status(struct echoaudio *chip);
+
+
+static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
+{
+       int err;
+
+       DE_INIT(("init_hw() - Mona\n"));
+       snd_assert((subdevice_id & 0xfff0) == MONA, return -ENODEV);
+
+       if ((err = init_dsp_comm_page(chip))) {
+               DE_INIT(("init_hw - could not initialize DSP comm page\n"));
+               return err;
+       }
+
+       chip->device_id = device_id;
+       chip->subdevice_id = subdevice_id;
+       chip->bad_board = TRUE;
+       chip->input_clock_types =
+               ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
+               ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
+       chip->digital_modes =
+               ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
+               ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
+               ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
+
+       /* Mona comes in both '301 and '361 flavors */
+       if (chip->device_id == DEVICE_ID_56361)
+               chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP];
+       else
+               chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP];
+
+       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->digital_in_automute = TRUE;
+
+       if ((err = load_firmware(chip)) < 0)
+               return err;
+       chip->bad_board = FALSE;
+
+       if ((err = init_line_levels(chip)) < 0)
+               return err;
+
+       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
+       snd_assert(err >= 0, return err);
+       err = set_professional_spdif(chip, TRUE);
+
+       DE_INIT(("init_hw done\n"));
+       return err;
+}
+
+
+
+static u32 detect_input_clocks(const struct echoaudio *chip)
+{
+       u32 clocks_from_dsp, clock_bits;
+
+       /* Map the DSP clock detect bits to the generic driver clock
+          detect bits */
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       clock_bits = ECHO_CLOCK_BIT_INTERNAL;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF)
+               clock_bits |= ECHO_CLOCK_BIT_SPDIF;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_ADAT)
+               clock_bits |= ECHO_CLOCK_BIT_ADAT;
+
+       if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD)
+               clock_bits |= ECHO_CLOCK_BIT_WORD;
+
+       return clock_bits;
+}
+
+
+
+/* Mona has an ASIC on the PCI card and another ASIC in the external box; 
+both need to be loaded. */
+static int load_asic(struct echoaudio *chip)
+{
+       u32 control_reg;
+       int err;
+       const struct firmware *asic;
+
+       if (chip->asic_loaded)
+               return 0;
+
+       mdelay(10);
+
+       if (chip->device_id == DEVICE_ID_56361)
+               asic = &card_fw[FW_MONA_361_1_ASIC48];
+       else
+               asic = &card_fw[FW_MONA_301_1_ASIC48];
+
+       err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic);
+       if (err < 0)
+               return err;
+
+       chip->asic_code = asic;
+       mdelay(10);
+
+       /* Do the external one */
+       err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC,
+                               &card_fw[FW_MONA_2_ASIC]);
+       if (err < 0)
+               return err;
+
+       mdelay(10);
+       err = check_asic_status(chip);
+
+       /* Set up the control register if the load succeeded -
+          48 kHz, internal clock, S/PDIF RCA mode */
+       if (!err) {
+               control_reg = GML_CONVERTER_ENABLE | GML_48KHZ;
+               err = write_control_reg(chip, control_reg, TRUE);
+       }
+
+       return err;
+}
+
+
+
+/* Depending on what digital mode you want, Mona needs different ASICs
+loaded.  This function checks the ASIC needed for the new mode and sees
+if it matches the one already loaded. */
+static int switch_asic(struct echoaudio *chip, char double_speed)
+{
+       const struct firmware *asic;
+       int err;
+
+       /* Check the clock detect bits to see if this is
+       a single-speed clock or a double-speed clock; load
+       a new ASIC if necessary. */
+       if (chip->device_id == DEVICE_ID_56361) {
+               if (double_speed)
+                       asic = &card_fw[FW_MONA_361_1_ASIC96];
+               else
+                       asic = &card_fw[FW_MONA_361_1_ASIC48];
+       } else {
+               if (double_speed)
+                       asic = &card_fw[FW_MONA_301_1_ASIC96];
+               else
+                       asic = &card_fw[FW_MONA_301_1_ASIC48];
+       }
+
+       if (asic != chip->asic_code) {
+               /* Load the desired ASIC */
+               err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
+                                       asic);
+               if (err < 0)
+                       return err;
+               chip->asic_code = asic;
+       }
+
+       return 0;
+}
+
+
+
+static int set_sample_rate(struct echoaudio *chip, u32 rate)
+{
+       u32 control_reg, clock;
+       const struct firmware *asic;
+       char force_write;
+
+       /* Only set the clock for internal mode. */
+       if (chip->input_clock != ECHO_CLOCK_INTERNAL) {
+               DE_ACT(("set_sample_rate: Cannot set sample rate - "
+                       "clock not set to CLK_CLOCKININTERNAL\n"));
+               /* Save the rate anyhow */
+               chip->comm_page->sample_rate = cpu_to_le32(rate);
+               chip->sample_rate = rate;
+               return 0;
+       }
+
+       /* Now, check to see if the required ASIC is loaded */
+       if (rate >= 88200) {
+               if (chip->digital_mode == DIGITAL_MODE_ADAT)
+                       return -EINVAL;
+               if (chip->device_id == DEVICE_ID_56361)
+                       asic = &card_fw[FW_MONA_361_1_ASIC96];
+               else
+                       asic = &card_fw[FW_MONA_301_1_ASIC96];
+       } else {
+               if (chip->device_id == DEVICE_ID_56361)
+                       asic = &card_fw[FW_MONA_361_1_ASIC48];
+               else
+                       asic = &card_fw[FW_MONA_301_1_ASIC48];
+       }
+
+       force_write = 0;
+       if (asic != chip->asic_code) {
+               int err;
+               /* Load the desired ASIC (load_asic_generic() can sleep) */
+               spin_unlock_irq(&chip->lock);
+               err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC,
+                                       asic);
+               spin_lock_irq(&chip->lock);
+
+               if (err < 0)
+                       return err;
+               chip->asic_code = asic;
+               force_write = 1;
+       }
+
+       /* Compute the new control register value */
+       clock = 0;
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_CLOCK_CLEAR_MASK;
+       control_reg &= GML_SPDIF_RATE_CLEAR_MASK;
+
+       switch (rate) {
+       case 96000:
+               clock = GML_96KHZ;
+               break;
+       case 88200:
+               clock = GML_88KHZ;
+               break;
+       case 48000:
+               clock = GML_48KHZ | GML_SPDIF_SAMPLE_RATE1;
+               break;
+       case 44100:
+               clock = GML_44KHZ;
+               /* Professional mode */
+               if (control_reg & GML_SPDIF_PRO_MODE)
+                       clock |= GML_SPDIF_SAMPLE_RATE0;
+               break;
+       case 32000:
+               clock = GML_32KHZ | GML_SPDIF_SAMPLE_RATE0 |
+                       GML_SPDIF_SAMPLE_RATE1;
+               break;
+       case 22050:
+               clock = GML_22KHZ;
+               break;
+       case 16000:
+               clock = GML_16KHZ;
+               break;
+       case 11025:
+               clock = GML_11KHZ;
+               break;
+       case 8000:
+               clock = GML_8KHZ;
+               break;
+       default:
+               DE_ACT(("set_sample_rate: %d invalid!\n", rate));
+               return -EINVAL;
+       }
+
+       control_reg |= clock;
+
+       chip->comm_page->sample_rate = cpu_to_le32(rate);       /* ignored by the DSP */
+       chip->sample_rate = rate;
+       DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
+
+       return write_control_reg(chip, control_reg, force_write);
+}
+
+
+
+static int set_input_clock(struct echoaudio *chip, u16 clock)
+{
+       u32 control_reg, clocks_from_dsp;
+       int err;
+
+       DE_ACT(("set_input_clock:\n"));
+
+       /* Prevent two simultaneous calls to switch_asic() */
+       if (atomic_read(&chip->opencount))
+               return -EAGAIN;
+
+       /* Mask off the clock select bits */
+       control_reg = le32_to_cpu(chip->comm_page->control_register) &
+               GML_CLOCK_CLEAR_MASK;
+       clocks_from_dsp = le32_to_cpu(chip->comm_page->status_clocks);
+
+       switch (clock) {
+       case ECHO_CLOCK_INTERNAL:
+               DE_ACT(("Set Mona clock to INTERNAL\n"));
+               chip->input_clock = ECHO_CLOCK_INTERNAL;
+               return set_sample_rate(chip, chip->sample_rate);
+       case ECHO_CLOCK_SPDIF:
+               if (chip->digital_mode == DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               spin_unlock_irq(&chip->lock);
+               err = switch_asic(chip, clocks_from_dsp &
+                                 GML_CLOCK_DETECT_BIT_SPDIF96);
+               spin_lock_irq(&chip->lock);
+               if (err < 0)
+                       return err;
+               DE_ACT(("Set Mona clock to SPDIF\n"));
+               control_reg |= GML_SPDIF_CLOCK;
+               if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_SPDIF96)
+                       control_reg |= GML_DOUBLE_SPEED_MODE;
+               else
+                       control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_WORD:
+               DE_ACT(("Set Mona clock to WORD\n"));
+               spin_unlock_irq(&chip->lock);
+               err = switch_asic(chip, clocks_from_dsp &
+                                 GML_CLOCK_DETECT_BIT_WORD96);
+               spin_lock_irq(&chip->lock);
+               if (err < 0)
+                       return err;
+               control_reg |= GML_WORD_CLOCK;
+               if (clocks_from_dsp & GML_CLOCK_DETECT_BIT_WORD96)
+                       control_reg |= GML_DOUBLE_SPEED_MODE;
+               else
+                       control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       case ECHO_CLOCK_ADAT:
+               DE_ACT(("Set Mona clock to ADAT\n"));
+               if (chip->digital_mode != DIGITAL_MODE_ADAT)
+                       return -EAGAIN;
+               control_reg |= GML_ADAT_CLOCK;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       default:
+               DE_ACT(("Input clock 0x%x not supported for Mona\n", clock));
+               return -EINVAL;
+       }
+
+       chip->input_clock = clock;
+       return write_control_reg(chip, control_reg, TRUE);
+}
+
+
+
+static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
+{
+       u32 control_reg;
+       int err, incompatible_clock;
+
+       /* Set clock to "internal" if it's not compatible with the new mode */
+       incompatible_clock = FALSE;
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+       case DIGITAL_MODE_SPDIF_RCA:
+               if (chip->input_clock == ECHO_CLOCK_ADAT)
+                       incompatible_clock = TRUE;
+               break;
+       case DIGITAL_MODE_ADAT:
+               if (chip->input_clock == ECHO_CLOCK_SPDIF)
+                       incompatible_clock = TRUE;
+               break;
+       default:
+               DE_ACT(("Digital mode not supported: %d\n", mode));
+               return -EINVAL;
+       }
+
+       spin_lock_irq(&chip->lock);
+
+       if (incompatible_clock) {       /* Switch to 48KHz, internal */
+               chip->sample_rate = 48000;
+               set_input_clock(chip, ECHO_CLOCK_INTERNAL);
+       }
+
+       /* Clear the current digital mode */
+       control_reg = le32_to_cpu(chip->comm_page->control_register);
+       control_reg &= GML_DIGITAL_MODE_CLEAR_MASK;
+
+       /* Tweak the control reg */
+       switch (mode) {
+       case DIGITAL_MODE_SPDIF_OPTICAL:
+               control_reg |= GML_SPDIF_OPTICAL_MODE;
+               break;
+       case DIGITAL_MODE_SPDIF_RCA:
+               /* GML_SPDIF_OPTICAL_MODE bit cleared */
+               break;
+       case DIGITAL_MODE_ADAT:
+               /* If the current ASIC is the 96KHz ASIC, switch the ASIC
+                  and set to 48 KHz */
+               if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] ||
+                   chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) {
+                       set_sample_rate(chip, 48000);
+               }
+               control_reg |= GML_ADAT_MODE;
+               control_reg &= ~GML_DOUBLE_SPEED_MODE;
+               break;
+       }
+
+       err = write_control_reg(chip, control_reg, FALSE);
+       spin_unlock_irq(&chip->lock);
+       if (err < 0)
+               return err;
+       chip->digital_mode = mode;
+
+       DE_ACT(("set_digital_mode to %d\n", mode));
+       return incompatible_clock;
+}