Merge branch 'fix/misc' into topic/misc
authorTakashi Iwai <tiwai@suse.de>
Wed, 18 Aug 2010 13:17:52 +0000 (15:17 +0200)
committerTakashi Iwai <tiwai@suse.de>
Wed, 18 Aug 2010 13:17:52 +0000 (15:17 +0200)
17 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
sound/drivers/Kconfig
sound/drivers/Makefile
sound/drivers/aloop.c [new file with mode: 0644]
sound/isa/Kconfig
sound/isa/Makefile
sound/isa/galaxy/Makefile [new file with mode: 0644]
sound/isa/galaxy/azt1605.c [new file with mode: 0644]
sound/isa/galaxy/azt2316.c [new file with mode: 0644]
sound/isa/galaxy/galaxy.c [new file with mode: 0644]
sound/isa/sgalaxy.c [deleted file]
sound/usb/card.c
sound/usb/helper.c
sound/usb/midi.c
sound/usb/pcm.c
sound/usb/proc.c
sound/usb/urb.c

index 7f4dceb..d0eb696 100644 (file)
@@ -300,6 +300,74 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
            control correctly. If you have problems regarding this, try
            another ALSA compliant mixer (alsamixer works).
 
+  Module snd-azt1605
+  ------------------
+
+    Module for Aztech Sound Galaxy soundcards based on the Aztech AZT1605
+    chipset.
+
+    port       - port # for BASE (0x220,0x240,0x260,0x280)
+    wss_port   - port # for WSS (0x530,0x604,0xe80,0xf40)
+    irq                - IRQ # for WSS (7,9,10,11)
+    dma1       - DMA # for WSS playback (0,1,3)
+    dma2       - DMA # for WSS capture (0,1), -1 = disabled (default)
+    mpu_port   - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
+    mpu_irq    - IRQ # for MPU-401 UART (3,5,7,9), -1 = disabled (default)
+    fm_port    - port # for OPL3 (0x388), -1 = disabled (default)
+
+    This module supports multiple cards. It does not support autoprobe: port,
+    wss_port, irq and dma1 have to be specified. The other values are
+    optional.
+
+    "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
+    or the value stored in the card's EEPROM for cards that have an EEPROM and
+    their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
+    be choosen freely from the options enumerated above.
+
+    If dma2 is specified and different from dma1, the card will operate in
+    full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to
+    enable capture since only channels 0 and 1 are available for capture.
+
+    Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
+    mpu_port=0x330 mpu_irq=9 fm_port=0x388".
+
+    Whatever IRQ and DMA channels you pick, be sure to reserve them for
+    legacy ISA in your BIOS.
+
+  Module snd-azt2316
+  ------------------
+
+    Module for Aztech Sound Galaxy soundcards based on the Aztech AZT2316
+    chipset.
+
+    port       - port # for BASE (0x220,0x240,0x260,0x280)
+    wss_port   - port # for WSS (0x530,0x604,0xe80,0xf40)
+    irq                - IRQ # for WSS (7,9,10,11)
+    dma1       - DMA # for WSS playback (0,1,3)
+    dma2       - DMA # for WSS capture (0,1), -1 = disabled (default)
+    mpu_port   - port # for MPU-401 UART (0x300,0x330), -1 = disabled (default)
+    mpu_irq    - IRQ # for MPU-401 UART (5,7,9,10), -1 = disabled (default)
+    fm_port    - port # for OPL3 (0x388), -1 = disabled (default)
+
+    This module supports multiple cards. It does not support autoprobe: port,
+    wss_port, irq and dma1 have to be specified. The other values are
+    optional.
+
+    "port" needs to match the BASE ADDRESS jumper on the card (0x220 or 0x240)
+    or the value stored in the card's EEPROM for cards that have an EEPROM and
+    their "CONFIG MODE" jumper set to "EEPROM SETTING". The other values can
+    be choosen freely from the options enumerated above.
+
+    If dma2 is specified and different from dma1, the card will operate in
+    full-duplex mode. When dma1=3, only dma2=0 is valid and the only way to
+    enable capture since only channels 0 and 1 are available for capture.
+
+    Generic settings are "port=0x220 wss_port=0x530 irq=10 dma1=1 dma2=0
+    mpu_port=0x330 mpu_irq=9 fm_port=0x388".
+
+    Whatever IRQ and DMA channels you pick, be sure to reserve them for
+    legacy ISA in your BIOS.
+
   Module snd-aw2
   --------------
 
@@ -1641,20 +1709,6 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     This card is also known as Audio Excel DSP 16 or Zoltrix AV302.
 
-  Module snd-sgalaxy
-  ------------------
-
-    Module for Aztech Sound Galaxy sound card.
-
-    sbport     - Port # for SB16 interface (0x220,0x240)
-    wssport    - Port # for WSS interface (0x530,0xe80,0xf40,0x604)
-    irq                - IRQ # (7,9,10,11)
-    dma1       - DMA #
-
-    This module supports multiple cards.
-
-    The power-management is supported.
-
   Module snd-sscape
   -----------------
 
index 480c386..c896116 100644 (file)
@@ -74,6 +74,25 @@ config SND_DUMMY
          To compile this driver as a module, choose M here: the module
          will be called snd-dummy.
 
+config SND_ALOOP
+        tristate "Generic loopback driver (PCM)"
+        select SND_PCM
+        help
+          Say 'Y' or 'M' to include support for the PCM loopback device.
+         This module returns played samples back to the user space using
+         the standard ALSA PCM device. The devices are routed 0->1 and
+          1->0, where first number is the playback PCM device and second
+         number is the capture device. Module creates two PCM devices and
+         configured number of substreams (see the pcm_substreams module
+          parameter).
+
+         The looback device allow time sychronization with an external
+         timing source using the time shift universal control (+-20%
+         of system time).
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-aloop.
+
 config SND_VIRMIDI
        tristate "Virtual MIDI soundcard"
        depends on SND_SEQUENCER
index d4a07f9..1a8440c 100644 (file)
@@ -4,6 +4,7 @@
 #
 
 snd-dummy-objs := dummy.o
+snd-aloop-objs := aloop.o
 snd-mtpav-objs := mtpav.o
 snd-mts64-objs := mts64.o
 snd-portman2x4-objs := portman2x4.o
@@ -13,6 +14,7 @@ snd-ml403-ac97cr-objs := ml403-ac97cr.o pcm-indirect2.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
+obj-$(CONFIG_SND_ALOOP) += snd-aloop.o
 obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
 obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
 obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
new file mode 100644 (file)
index 0000000..3123a15
--- /dev/null
@@ -0,0 +1,1055 @@
+/*
+ *  Loopback soundcard
+ *
+ *  Original code:
+ *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
+ *
+ *  More accurate positioning and full-duplex support:
+ *  Copyright (c) Ahmet İnan <ainan at mathematik.uni-freiburg.de>
+ *
+ *  Major (almost complete) rewrite:
+ *  Copyright (c) by Takashi Iwai <tiwai@suse.de>
+ *
+ *  A next major update in 2010 (separate timers for playback and capture):
+ *  Copyright (c) Jaroslav Kysela <perex@perex.cz>
+ *
+ *   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; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   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
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+MODULE_DESCRIPTION("A loopback soundcard");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ALSA,Loopback soundcard}}");
+
+#define MAX_PCM_SUBSTREAMS     8
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
+static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 0};
+static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
+static int pcm_notify[SNDRV_CARDS];
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for loopback soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for loopback soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable this loopback soundcard.");
+module_param_array(pcm_substreams, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_substreams, "PCM substreams # (1-8) for loopback driver.");
+module_param_array(pcm_notify, int, NULL, 0444);
+MODULE_PARM_DESC(pcm_notify, "Break capture when PCM format/rate/channels changes.");
+
+#define NO_PITCH 100000
+
+struct loopback_pcm;
+
+struct loopback_cable {
+       spinlock_t lock;
+       struct loopback_pcm *streams[2];
+       struct snd_pcm_hardware hw;
+       /* flags */
+       unsigned int valid;
+       unsigned int running;
+};
+
+struct loopback_setup {
+       unsigned int notify: 1;
+       unsigned int rate_shift;
+       unsigned int format;
+       unsigned int rate;
+       unsigned int channels;
+       struct snd_ctl_elem_id active_id;
+       struct snd_ctl_elem_id format_id;
+       struct snd_ctl_elem_id rate_id;
+       struct snd_ctl_elem_id channels_id;
+};
+
+struct loopback {
+       struct snd_card *card;
+       struct mutex cable_lock;
+       struct loopback_cable *cables[MAX_PCM_SUBSTREAMS][2];
+       struct snd_pcm *pcm[2];
+       struct loopback_setup setup[MAX_PCM_SUBSTREAMS][2];
+};
+
+struct loopback_pcm {
+       struct loopback *loopback;
+       struct snd_pcm_substream *substream;
+       struct loopback_cable *cable;
+       unsigned int pcm_buffer_size;
+       unsigned int buf_pos;   /* position in buffer */
+       unsigned int silent_size;
+       /* PCM parameters */
+       unsigned int pcm_period_size;
+       unsigned int pcm_bps;           /* bytes per second */
+       unsigned int pcm_salign;        /* bytes per sample * channels */
+       unsigned int pcm_rate_shift;    /* rate shift value */
+       /* flags */
+       unsigned int period_update_pending :1;
+       /* timer stuff */
+       unsigned int irq_pos;           /* fractional IRQ position */
+       unsigned int period_size_frac;
+       unsigned long last_jiffies;
+       struct timer_list timer;
+};
+
+static struct platform_device *devices[SNDRV_CARDS];
+
+static inline unsigned int byte_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+       if (dpcm->pcm_rate_shift == NO_PITCH) {
+               x /= HZ;
+       } else {
+               x = div_u64(NO_PITCH * (unsigned long long)x,
+                           HZ * (unsigned long long)dpcm->pcm_rate_shift);
+       }
+       return x - (x % dpcm->pcm_salign);
+}
+
+static inline unsigned int frac_pos(struct loopback_pcm *dpcm, unsigned int x)
+{
+       if (dpcm->pcm_rate_shift == NO_PITCH) { /* no pitch */
+               return x * HZ;
+       } else {
+               x = div_u64(dpcm->pcm_rate_shift * (unsigned long long)x * HZ,
+                           NO_PITCH);
+       }
+       return x;
+}
+
+static inline struct loopback_setup *get_setup(struct loopback_pcm *dpcm)
+{
+       int device = dpcm->substream->pstr->pcm->device;
+       
+       if (dpcm->substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               device ^= 1;
+       return &dpcm->loopback->setup[dpcm->substream->number][device];
+}
+
+static inline unsigned int get_notify(struct loopback_pcm *dpcm)
+{
+       return get_setup(dpcm)->notify;
+}
+
+static inline unsigned int get_rate_shift(struct loopback_pcm *dpcm)
+{
+       return get_setup(dpcm)->rate_shift;
+}
+
+static void loopback_timer_start(struct loopback_pcm *dpcm)
+{
+       unsigned long tick;
+       unsigned int rate_shift = get_rate_shift(dpcm);
+
+       if (rate_shift != dpcm->pcm_rate_shift) {
+               dpcm->pcm_rate_shift = rate_shift;
+               dpcm->period_size_frac = frac_pos(dpcm, dpcm->pcm_period_size);
+       }
+       tick = dpcm->period_size_frac - dpcm->irq_pos;
+       tick = (tick + dpcm->pcm_bps - 1) / dpcm->pcm_bps;
+       dpcm->timer.expires = jiffies + tick;
+       add_timer(&dpcm->timer);
+}
+
+static inline void loopback_timer_stop(struct loopback_pcm *dpcm)
+{
+       del_timer(&dpcm->timer);
+}
+
+#define CABLE_VALID_PLAYBACK   (1 << SNDRV_PCM_STREAM_PLAYBACK)
+#define CABLE_VALID_CAPTURE    (1 << SNDRV_PCM_STREAM_CAPTURE)
+#define CABLE_VALID_BOTH       (CABLE_VALID_PLAYBACK|CABLE_VALID_CAPTURE)
+
+static int loopback_check_format(struct loopback_cable *cable, int stream)
+{
+       struct snd_pcm_runtime *runtime;
+       struct loopback_setup *setup;
+       struct snd_card *card;
+       int check;
+
+       if (cable->valid != CABLE_VALID_BOTH) {
+               if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       goto __notify;
+               return 0;
+       }
+       runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+                                                       substream->runtime;
+       check = cable->hw.formats != (1ULL << runtime->format) ||
+               cable->hw.rate_min != runtime->rate ||
+               cable->hw.rate_max != runtime->rate ||
+               cable->hw.channels_min != runtime->channels ||
+               cable->hw.channels_max != runtime->channels;
+       if (!check)
+               return 0;
+       if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+               return -EIO;
+       } else {
+               snd_pcm_stop(cable->streams[SNDRV_PCM_STREAM_CAPTURE]->
+                                       substream, SNDRV_PCM_STATE_DRAINING);
+             __notify:
+               runtime = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->
+                                                       substream->runtime;
+               setup = get_setup(cable->streams[SNDRV_PCM_STREAM_PLAYBACK]);
+               card = cable->streams[SNDRV_PCM_STREAM_PLAYBACK]->loopback->card;
+               if (setup->format != runtime->format) {
+                       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &setup->format_id);
+                       setup->format = runtime->format;
+               }
+               if (setup->rate != runtime->rate) {
+                       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &setup->rate_id);
+                       setup->rate = runtime->rate;
+               }
+               if (setup->channels != runtime->channels) {
+                       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_VALUE,
+                                                       &setup->channels_id);
+                       setup->channels = runtime->channels;
+               }
+       }
+       return 0;
+}
+
+static void loopback_active_notify(struct loopback_pcm *dpcm)
+{
+       snd_ctl_notify(dpcm->loopback->card,
+                      SNDRV_CTL_EVENT_MASK_VALUE,
+                      &get_setup(dpcm)->active_id);
+}
+
+static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct loopback_pcm *dpcm = runtime->private_data;
+       struct loopback_cable *cable = dpcm->cable;
+       int err;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               err = loopback_check_format(cable, substream->stream);
+               if (err < 0)
+                       return err;
+               dpcm->last_jiffies = jiffies;
+               dpcm->pcm_rate_shift = 0;
+               loopback_timer_start(dpcm);
+               cable->running |= (1 << substream->stream);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       loopback_active_notify(dpcm);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               cable->running &= ~(1 << substream->stream);
+               loopback_timer_stop(dpcm);
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       loopback_active_notify(dpcm);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int loopback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct loopback_pcm *dpcm = runtime->private_data;
+       struct loopback_cable *cable = dpcm->cable;
+       unsigned int bps, salign;
+
+       salign = (snd_pcm_format_width(runtime->format) *
+                                               runtime->channels) / 8;
+       bps = salign * runtime->rate;
+       if (bps <= 0 || salign <= 0)
+               return -EINVAL;
+
+       dpcm->buf_pos = 0;
+       dpcm->pcm_buffer_size = frames_to_bytes(runtime, runtime->buffer_size);
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               /* clear capture buffer */
+               dpcm->silent_size = dpcm->pcm_buffer_size;
+               snd_pcm_format_set_silence(runtime->format, runtime->dma_area,
+                                          runtime->buffer_size * runtime->channels);
+       }
+
+       dpcm->irq_pos = 0;
+       dpcm->period_update_pending = 0;
+       dpcm->pcm_bps = bps;
+       dpcm->pcm_salign = salign;
+       dpcm->pcm_period_size = frames_to_bytes(runtime, runtime->period_size);
+
+       mutex_lock(&dpcm->loopback->cable_lock);
+       if (!(cable->valid & ~(1 << substream->stream))) {
+               cable->hw.formats = (1ULL << runtime->format);
+               cable->hw.rate_min = runtime->rate;
+               cable->hw.rate_max = runtime->rate;
+               cable->hw.channels_min = runtime->channels;
+               cable->hw.channels_max = runtime->channels;
+       }
+       cable->valid |= 1 << substream->stream;
+       mutex_unlock(&dpcm->loopback->cable_lock);
+
+       return 0;
+}
+
+static void clear_capture_buf(struct loopback_pcm *dpcm, unsigned int bytes)
+{
+       struct snd_pcm_runtime *runtime = dpcm->substream->runtime;
+       char *dst = runtime->dma_area;
+       unsigned int dst_off = dpcm->buf_pos;
+
+       if (dpcm->silent_size >= dpcm->pcm_buffer_size)
+               return;
+       if (dpcm->silent_size + bytes > dpcm->pcm_buffer_size)
+               bytes = dpcm->pcm_buffer_size - dpcm->silent_size;
+
+       for (;;) {
+               unsigned int size = bytes;
+               if (dst_off + size > dpcm->pcm_buffer_size)
+                       size = dpcm->pcm_buffer_size - dst_off;
+               snd_pcm_format_set_silence(runtime->format, dst + dst_off,
+                                          bytes_to_frames(runtime, size) *
+                                               runtime->channels);
+               dpcm->silent_size += size;
+               bytes -= size;
+               if (!bytes)
+                       break;
+               dst_off = 0;
+       }
+}
+
+static void copy_play_buf(struct loopback_pcm *play,
+                         struct loopback_pcm *capt,
+                         unsigned int bytes)
+{
+       struct snd_pcm_runtime *runtime = play->substream->runtime;
+       char *src = play->substream->runtime->dma_area;
+       char *dst = capt->substream->runtime->dma_area;
+       unsigned int src_off = play->buf_pos;
+       unsigned int dst_off = capt->buf_pos;
+       unsigned int clear_bytes = 0;
+
+       /* check if playback is draining, trim the capture copy size
+        * when our pointer is at the end of playback ring buffer */
+       if (runtime->status->state == SNDRV_PCM_STATE_DRAINING &&
+           snd_pcm_playback_hw_avail(runtime) < runtime->buffer_size) { 
+               snd_pcm_uframes_t appl_ptr, appl_ptr1, diff;
+               appl_ptr = appl_ptr1 = runtime->control->appl_ptr;
+               appl_ptr1 -= appl_ptr1 % runtime->buffer_size;
+               appl_ptr1 += play->buf_pos / play->pcm_salign;
+               if (appl_ptr < appl_ptr1)
+                       appl_ptr1 -= runtime->buffer_size;
+               diff = (appl_ptr - appl_ptr1) * play->pcm_salign;
+               if (diff < bytes) {
+                       clear_bytes = bytes - diff;
+                       bytes = diff;
+               }
+       }
+
+       for (;;) {
+               unsigned int size = bytes;
+               if (src_off + size > play->pcm_buffer_size)
+                       size = play->pcm_buffer_size - src_off;
+               if (dst_off + size > capt->pcm_buffer_size)
+                       size = capt->pcm_buffer_size - dst_off;
+               memcpy(dst + dst_off, src + src_off, size);
+               capt->silent_size = 0;
+               bytes -= size;
+               if (!bytes)
+                       break;
+               src_off = (src_off + size) % play->pcm_buffer_size;
+               dst_off = (dst_off + size) % capt->pcm_buffer_size;
+       }
+
+       if (clear_bytes > 0)
+               clear_capture_buf(capt, clear_bytes);
+}
+
+#define BYTEPOS_UPDATE_POSONLY 0
+#define BYTEPOS_UPDATE_CLEAR   1
+#define BYTEPOS_UPDATE_COPY    2
+
+static void loopback_bytepos_update(struct loopback_pcm *dpcm,
+                                   unsigned int delta,
+                                   unsigned int cmd)
+{
+       unsigned int count;
+       unsigned long last_pos;
+
+       last_pos = byte_pos(dpcm, dpcm->irq_pos);
+       dpcm->irq_pos += delta * dpcm->pcm_bps;
+       count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+       if (!count)
+               return;
+       if (cmd == BYTEPOS_UPDATE_CLEAR)
+               clear_capture_buf(dpcm, count);
+       else if (cmd == BYTEPOS_UPDATE_COPY)
+               copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
+                             dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
+                             count);
+       dpcm->buf_pos += count;
+       dpcm->buf_pos %= dpcm->pcm_buffer_size;
+       if (dpcm->irq_pos >= dpcm->period_size_frac) {
+               dpcm->irq_pos %= dpcm->period_size_frac;
+               dpcm->period_update_pending = 1;
+       }
+}
+
+static void loopback_pos_update(struct loopback_cable *cable)
+{
+       struct loopback_pcm *dpcm_play =
+                       cable->streams[SNDRV_PCM_STREAM_PLAYBACK];
+       struct loopback_pcm *dpcm_capt =
+                       cable->streams[SNDRV_PCM_STREAM_CAPTURE];
+       unsigned long delta_play = 0, delta_capt = 0;
+
+       spin_lock(&cable->lock);        
+       if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) {
+               delta_play = jiffies - dpcm_play->last_jiffies;
+               dpcm_play->last_jiffies += delta_play;
+       }
+
+       if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) {
+               delta_capt = jiffies - dpcm_capt->last_jiffies;
+               dpcm_capt->last_jiffies += delta_capt;
+       }
+
+       if (delta_play == 0 && delta_capt == 0) {
+               spin_unlock(&cable->lock);
+               return;
+       }
+               
+       if (delta_play > delta_capt) {
+               loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
+                                       BYTEPOS_UPDATE_POSONLY);
+               delta_play = delta_capt;
+       } else if (delta_play < delta_capt) {
+               loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
+                                       BYTEPOS_UPDATE_CLEAR);
+               delta_capt = delta_play;
+       }
+
+       if (delta_play == 0 && delta_capt == 0) {
+               spin_unlock(&cable->lock);
+               return;
+       }
+       /* note delta_capt == delta_play at this moment */
+       loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
+       loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+       spin_unlock(&cable->lock);
+}
+
+static void loopback_timer_function(unsigned long data)
+{
+       struct loopback_pcm *dpcm = (struct loopback_pcm *)data;
+       int stream;
+
+       loopback_pos_update(dpcm->cable);
+       stream = dpcm->substream->stream;
+       if (dpcm->cable->running & (1 << stream))
+               loopback_timer_start(dpcm);
+       if (dpcm->period_update_pending) {
+               dpcm->period_update_pending = 0;
+               if (dpcm->cable->running & (1 << stream))
+                       snd_pcm_period_elapsed(dpcm->substream);
+       }
+}
+
+static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct loopback_pcm *dpcm = runtime->private_data;
+
+       loopback_pos_update(dpcm->cable);
+       return bytes_to_frames(runtime, dpcm->buf_pos);
+}
+
+static struct snd_pcm_hardware loopback_pcm_hardware =
+{
+       .info =         (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP |
+                        SNDRV_PCM_INFO_MMAP_VALID),
+       .formats =      (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |
+                        SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE |
+                        SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE),
+       .rates =        SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_192000,
+       .rate_min =             8000,
+       .rate_max =             192000,
+       .channels_min =         1,
+       .channels_max =         32,
+       .buffer_bytes_max =     2 * 1024 * 1024,
+       .period_bytes_min =     64,
+       .period_bytes_max =     2 * 1024 * 1024,
+       .periods_min =          1,
+       .periods_max =          1024,
+       .fifo_size =            0,
+};
+
+static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
+{
+       struct loopback_pcm *dpcm = runtime->private_data;
+       kfree(dpcm);
+}
+
+static int loopback_hw_params(struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *params)
+{
+       return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+}
+
+static int loopback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct loopback_pcm *dpcm = runtime->private_data;
+       struct loopback_cable *cable = dpcm->cable;
+
+       mutex_lock(&dpcm->loopback->cable_lock);
+       cable->valid &= ~(1 << substream->stream);
+       mutex_unlock(&dpcm->loopback->cable_lock);
+       return snd_pcm_lib_free_pages(substream);
+}
+
+static unsigned int get_cable_index(struct snd_pcm_substream *substream)
+{
+       if (!substream->pcm->device)
+               return substream->stream;
+       else
+               return !substream->stream;
+}
+
+static int loopback_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct loopback *loopback = substream->private_data;
+       struct loopback_pcm *dpcm;
+       struct loopback_cable *cable;
+       int err = 0;
+       int dev = get_cable_index(substream);
+
+       mutex_lock(&loopback->cable_lock);
+       dpcm = kzalloc(sizeof(*dpcm), GFP_KERNEL);
+       if (!dpcm) {
+               err = -ENOMEM;
+               goto unlock;
+       }
+       dpcm->loopback = loopback;
+       dpcm->substream = substream;
+       setup_timer(&dpcm->timer, loopback_timer_function,
+                   (unsigned long)dpcm);
+
+       cable = loopback->cables[substream->number][dev];
+       if (!cable) {
+               cable = kzalloc(sizeof(*cable), GFP_KERNEL);
+               if (!cable) {
+                       kfree(dpcm);
+                       err = -ENOMEM;
+                       goto unlock;
+               }
+               spin_lock_init(&cable->lock);
+               cable->hw = loopback_pcm_hardware;
+               loopback->cables[substream->number][dev] = cable;
+       }
+       dpcm->cable = cable;
+       cable->streams[substream->stream] = dpcm;
+
+       snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+       runtime->private_data = dpcm;
+       runtime->private_free = loopback_runtime_free;
+       if (get_notify(dpcm) &&
+           substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               runtime->hw = loopback_pcm_hardware;
+       } else {
+               runtime->hw = cable->hw;
+       }
+ unlock:
+       mutex_unlock(&loopback->cable_lock);
+       return err;
+}
+
+static int loopback_close(struct snd_pcm_substream *substream)
+{
+       struct loopback *loopback = substream->private_data;
+       struct loopback_pcm *dpcm = substream->runtime->private_data;
+       struct loopback_cable *cable;
+       int dev = get_cable_index(substream);
+
+       loopback_timer_stop(dpcm);
+       mutex_lock(&loopback->cable_lock);
+       cable = loopback->cables[substream->number][dev];
+       if (cable->streams[!substream->stream]) {
+               /* other stream is still alive */
+               cable->streams[substream->stream] = NULL;
+       } else {
+               /* free the cable */
+               loopback->cables[substream->number][dev] = NULL;
+               kfree(cable);
+       }
+       mutex_unlock(&loopback->cable_lock);
+       return 0;
+}
+
+static struct snd_pcm_ops loopback_playback_ops = {
+       .open =         loopback_open,
+       .close =        loopback_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    loopback_hw_params,
+       .hw_free =      loopback_hw_free,
+       .prepare =      loopback_prepare,
+       .trigger =      loopback_trigger,
+       .pointer =      loopback_pointer,
+};
+
+static struct snd_pcm_ops loopback_capture_ops = {
+       .open =         loopback_open,
+       .close =        loopback_close,
+       .ioctl =        snd_pcm_lib_ioctl,
+       .hw_params =    loopback_hw_params,
+       .hw_free =      loopback_hw_free,
+       .prepare =      loopback_prepare,
+       .trigger =      loopback_trigger,
+       .pointer =      loopback_pointer,
+};
+
+static int __devinit loopback_pcm_new(struct loopback *loopback,
+                                     int device, int substreams)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(loopback->card, "Loopback PCM", device,
+                         substreams, substreams, &pcm);
+       if (err < 0)
+               return err;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &loopback_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &loopback_capture_ops);
+
+       pcm->private_data = loopback;
+       pcm->info_flags = 0;
+       strcpy(pcm->name, "Loopback PCM");
+
+       loopback->pcm[device] = pcm;
+
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
+                       snd_dma_continuous_data(GFP_KERNEL),
+                       0, 2 * 1024 * 1024);
+       return 0;
+}
+
+static int loopback_rate_shift_info(struct snd_kcontrol *kcontrol,   
+                                   struct snd_ctl_elem_info *uinfo) 
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 80000;
+       uinfo->value.integer.max = 120000;
+       uinfo->value.integer.step = 1;
+       return 0;
+}                                  
+
+static int loopback_rate_shift_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       
+       ucontrol->value.integer.value[0] =
+               loopback->setup[kcontrol->id.subdevice]
+                              [kcontrol->id.device].rate_shift;
+       return 0;
+}
+
+static int loopback_rate_shift_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+
+       val = ucontrol->value.integer.value[0];
+       if (val < 80000)
+               val = 80000;
+       if (val > 120000)
+               val = 120000;   
+       mutex_lock(&loopback->cable_lock);
+       if (val != loopback->setup[kcontrol->id.subdevice]
+                                 [kcontrol->id.device].rate_shift) {
+               loopback->setup[kcontrol->id.subdevice]
+                              [kcontrol->id.device].rate_shift = val;
+               change = 1;
+       }
+       mutex_unlock(&loopback->cable_lock);
+       return change;
+}
+
+static int loopback_notify_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       
+       ucontrol->value.integer.value[0] =
+               loopback->setup[kcontrol->id.subdevice]
+                              [kcontrol->id.device].notify;
+       return 0;
+}
+
+static int loopback_notify_put(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       unsigned int val;
+       int change = 0;
+
+       val = ucontrol->value.integer.value[0] ? 1 : 0;
+       if (val != loopback->setup[kcontrol->id.subdevice]
+                               [kcontrol->id.device].notify) {
+               loopback->setup[kcontrol->id.subdevice]
+                       [kcontrol->id.device].notify = val;
+               change = 1;
+       }
+       return change;
+}
+
+static int loopback_active_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       struct loopback_cable *cable = loopback->cables
+                               [kcontrol->id.subdevice][kcontrol->id.device];
+       unsigned int val = 0;
+
+       if (cable != NULL)
+               val = (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) ?
+                                                                       1 : 0;
+       ucontrol->value.integer.value[0] = val;
+       return 0;
+}
+
+static int loopback_format_info(struct snd_kcontrol *kcontrol,   
+                               struct snd_ctl_elem_info *uinfo) 
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = SNDRV_PCM_FORMAT_LAST;
+       uinfo->value.integer.step = 1;
+       return 0;
+}                                  
+
+static int loopback_format_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       
+       ucontrol->value.integer.value[0] =
+               loopback->setup[kcontrol->id.subdevice]
+                              [kcontrol->id.device].format;
+       return 0;
+}
+
+static int loopback_rate_info(struct snd_kcontrol *kcontrol,   
+                             struct snd_ctl_elem_info *uinfo) 
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 192000;
+       uinfo->value.integer.step = 1;
+       return 0;
+}                                  
+
+static int loopback_rate_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       
+       ucontrol->value.integer.value[0] =
+               loopback->setup[kcontrol->id.subdevice]
+                              [kcontrol->id.device].rate;
+       return 0;
+}
+
+static int loopback_channels_info(struct snd_kcontrol *kcontrol,   
+                                 struct snd_ctl_elem_info *uinfo) 
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 1;
+       uinfo->value.integer.max = 1024;
+       uinfo->value.integer.step = 1;
+       return 0;
+}                                  
+
+static int loopback_channels_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct loopback *loopback = snd_kcontrol_chip(kcontrol);
+       
+       ucontrol->value.integer.value[0] =
+               loopback->setup[kcontrol->id.subdevice]
+                              [kcontrol->id.device].rate;
+       return 0;
+}
+
+static struct snd_kcontrol_new loopback_controls[]  __devinitdata = {
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "PCM Rate Shift 100000",
+       .info =         loopback_rate_shift_info,
+       .get =          loopback_rate_shift_get,
+       .put =          loopback_rate_shift_put,
+},
+{
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "PCM Notify",
+       .info =         snd_ctl_boolean_mono_info,
+       .get =          loopback_notify_get,
+       .put =          loopback_notify_put,
+},
+#define ACTIVE_IDX 2
+{
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "PCM Slave Active",
+       .info =         snd_ctl_boolean_mono_info,
+       .get =          loopback_active_get,
+},
+#define FORMAT_IDX 3
+{
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "PCM Slave Format",
+       .info =         loopback_format_info,
+       .get =          loopback_format_get
+},
+#define RATE_IDX 4
+{
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "PCM Slave Rate",
+       .info =         loopback_rate_info,
+       .get =          loopback_rate_get
+},
+#define CHANNELS_IDX 5
+{
+       .access =       SNDRV_CTL_ELEM_ACCESS_READ,
+       .iface =        SNDRV_CTL_ELEM_IFACE_PCM,
+       .name =         "PCM Slave Channels",
+       .info =         loopback_channels_info,
+       .get =          loopback_channels_get
+}
+};
+
+static int __devinit loopback_mixer_new(struct loopback *loopback, int notify)
+{
+       struct snd_card *card = loopback->card;
+       struct snd_pcm *pcm;
+       struct snd_kcontrol *kctl;
+       struct loopback_setup *setup;
+       int err, dev, substr, substr_count, idx;
+
+       strcpy(card->mixername, "Loopback Mixer");
+       for (dev = 0; dev < 2; dev++) {
+               pcm = loopback->pcm[dev];
+               substr_count =
+                   pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream_count;
+               for (substr = 0; substr < substr_count; substr++) {
+                       setup = &loopback->setup[substr][dev];
+                       setup->notify = notify;
+                       setup->rate_shift = NO_PITCH;
+                       setup->format = SNDRV_PCM_FORMAT_S16_LE;
+                       setup->rate = 48000;
+                       setup->channels = 2;
+                       for (idx = 0; idx < ARRAY_SIZE(loopback_controls);
+                                                                       idx++) {
+                               kctl = snd_ctl_new1(&loopback_controls[idx],
+                                                   loopback);
+                               if (!kctl)
+                                       return -ENOMEM;
+                               kctl->id.device = dev;
+                               kctl->id.subdevice = substr;
+                               switch (idx) {
+                               case ACTIVE_IDX:
+                                       setup->active_id = kctl->id;
+                                       break;
+                               case FORMAT_IDX:
+                                       setup->format_id = kctl->id;
+                                       break;
+                               case RATE_IDX:
+                                       setup->rate_id = kctl->id;
+                                       break;
+                               case CHANNELS_IDX:
+                                       setup->channels_id = kctl->id;
+                                       break;
+                               default:
+                                       break;
+                               }
+                               err = snd_ctl_add(card, kctl);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
+       return 0;
+}
+
+static int __devinit loopback_probe(struct platform_device *devptr)
+{
+       struct snd_card *card;
+       struct loopback *loopback;
+       int dev = devptr->id;
+       int err;
+
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+                             sizeof(struct loopback), &card);
+       if (err < 0)
+               return err;
+       loopback = card->private_data;
+
+       if (pcm_substreams[dev] < 1)
+               pcm_substreams[dev] = 1;
+       if (pcm_substreams[dev] > MAX_PCM_SUBSTREAMS)
+               pcm_substreams[dev] = MAX_PCM_SUBSTREAMS;
+       
+       loopback->card = card;
+       mutex_init(&loopback->cable_lock);
+
+       err = loopback_pcm_new(loopback, 0, pcm_substreams[dev]);
+       if (err < 0)
+               goto __nodev;
+       err = loopback_pcm_new(loopback, 1, pcm_substreams[dev]);
+       if (err < 0)
+               goto __nodev;
+       err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0);
+       if (err < 0)
+               goto __nodev;
+       strcpy(card->driver, "Loopback");
+       strcpy(card->shortname, "Loopback");
+       sprintf(card->longname, "Loopback %i", dev + 1);
+       err = snd_card_register(card);
+       if (!err) {
+               platform_set_drvdata(devptr, card);
+               return 0;
+       }
+      __nodev:
+       snd_card_free(card);
+       return err;
+}
+
+static int __devexit loopback_remove(struct platform_device *devptr)
+{
+       snd_card_free(platform_get_drvdata(devptr));
+       platform_set_drvdata(devptr, NULL);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int loopback_suspend(struct platform_device *pdev,
+                               pm_message_t state)
+{
+       struct snd_card *card = platform_get_drvdata(pdev);
+       struct loopback *loopback = card->private_data;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+
+       snd_pcm_suspend_all(loopback->pcm[0]);
+       snd_pcm_suspend_all(loopback->pcm[1]);
+       return 0;
+}
+       
+static int loopback_resume(struct platform_device *pdev)
+{
+       struct snd_card *card = platform_get_drvdata(pdev);
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       return 0;
+}
+#endif
+
+#define SND_LOOPBACK_DRIVER    "snd_aloop"
+
+static struct platform_driver loopback_driver = {
+       .probe          = loopback_probe,
+       .remove         = __devexit_p(loopback_remove),
+#ifdef CONFIG_PM
+       .suspend        = loopback_suspend,
+       .resume         = loopback_resume,
+#endif
+       .driver         = {
+               .name   = SND_LOOPBACK_DRIVER
+       },
+};
+
+static void loopback_unregister_all(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(devices); ++i)
+               platform_device_unregister(devices[i]);
+       platform_driver_unregister(&loopback_driver);
+}
+
+static int __init alsa_card_loopback_init(void)
+{
+       int i, err, cards;
+
+       err = platform_driver_register(&loopback_driver);
+       if (err < 0)
+               return err;
+
+
+       cards = 0;
+       for (i = 0; i < SNDRV_CARDS; i++) {
+               struct platform_device *device;
+               if (!enable[i])
+                       continue;
+               device = platform_device_register_simple(SND_LOOPBACK_DRIVER,
+                                                        i, NULL, 0);
+               if (IS_ERR(device))
+                       continue;
+               if (!platform_get_drvdata(device)) {
+                       platform_device_unregister(device);
+                       continue;
+               }
+               devices[i] = device;
+               cards++;
+       }
+       if (!cards) {
+#ifdef MODULE
+               printk(KERN_ERR "aloop: No loopback enabled\n");
+#endif
+               loopback_unregister_all();
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static void __exit alsa_card_loopback_exit(void)
+{
+       loopback_unregister_all();
+}
+
+module_init(alsa_card_loopback_init)
+module_exit(alsa_card_loopback_exit)
index c6990c6..52064cf 100644 (file)
@@ -77,6 +77,32 @@ config SND_ALS100
          To compile this driver as a module, choose M here: the module
          will be called snd-als100.
 
+config SND_AZT1605
+       tristate "Aztech AZT1605 Driver"
+       depends on SND
+       select SND_WSS_LIB
+       select SND_MPU401_UART
+       select SND_OPL3_LIB
+       help
+         Say Y here to include support for Aztech Sound Galaxy cards
+         based on the AZT1605 chipset.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-azt1605.
+
+config SND_AZT2316
+       tristate "Aztech AZT2316 Driver"
+       depends on SND
+       select SND_WSS_LIB
+       select SND_MPU401_UART
+       select SND_OPL3_LIB
+       help
+         Say Y here to include support for Aztech Sound Galaxy cards
+         based on the AZT2316 chipset.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-azt2316.
+
 config SND_AZT2320
        tristate "Aztech Systems AZT2320"
        depends on PNP
@@ -351,16 +377,6 @@ config SND_SB16_CSP
          coprocessor can do variable tasks like various compression and
          decompression algorithms.
 
-config SND_SGALAXY
-       tristate "Aztech Sound Galaxy"
-       select SND_WSS_LIB
-       help
-         Say Y here to include support for Aztech Sound Galaxy
-         soundcards.
-
-         To compile this driver as a module, choose M here: the module
-         will be called snd-sgalaxy.
-
 config SND_SSCAPE
        tristate "Ensoniq SoundScape driver"
        select SND_MPU401_UART
index c73d30c..8d781e4 100644 (file)
@@ -10,7 +10,6 @@ snd-cmi8330-objs := cmi8330.o
 snd-es18xx-objs := es18xx.o
 snd-opl3sa2-objs := opl3sa2.o
 snd-sc6000-objs := sc6000.o
-snd-sgalaxy-objs := sgalaxy.o
 snd-sscape-objs := sscape.o
 
 # Toplevel Module Dependency
@@ -21,8 +20,7 @@ obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
 obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
 obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
 obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
-obj-$(CONFIG_SND_SGALAXY) += snd-sgalaxy.o
 obj-$(CONFIG_SND_SSCAPE) += snd-sscape.o
 
-obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ gus/ msnd/ opti9xx/ \
+obj-$(CONFIG_SND) += ad1816a/ ad1848/ cs423x/ es1688/ galaxy/ gus/ msnd/ opti9xx/ \
                     sb/ wavefront/ wss/
diff --git a/sound/isa/galaxy/Makefile b/sound/isa/galaxy/Makefile
new file mode 100644 (file)
index 0000000..e307066
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# Makefile for ALSA
+# Copyright (c) 2001 by Jaroslav Kysela <perex@suse.cz>
+#
+
+snd-azt1605-objs := azt1605.o
+snd-azt2316-objs := azt2316.o
+
+obj-$(CONFIG_SND_AZT1605) += snd-azt1605.o
+obj-$(CONFIG_SND_AZT2316) += snd-azt2316.o
diff --git a/sound/isa/galaxy/azt1605.c b/sound/isa/galaxy/azt1605.c
new file mode 100644 (file)
index 0000000..9a97643
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Aztech AZT1605 Driver
+ * Copyright (C) 2007,2010  Rene Herman
+ *
+ * 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT1605
+
+#define CRD_NAME "Aztech AZT1605"
+#define DRV_NAME "AZT1605"
+#define DEV_NAME "azt1605"
+
+#define GALAXY_DSP_MAJOR               2
+#define GALAXY_DSP_MINOR               1
+
+#define GALAXY_CONFIG_SIZE             3
+
+/*
+ * 24-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220          (0 << 0)
+#define GALAXY_CONFIG_SBA_240          (1 << 0)
+#define GALAXY_CONFIG_SBA_260          (2 << 0)
+#define GALAXY_CONFIG_SBA_280          (3 << 0)
+#define GALAXY_CONFIG_SBA_MASK         GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_MPUA_300         (0 << 2)
+#define GALAXY_CONFIG_MPUA_330         (1 << 2)
+
+#define GALAXY_CONFIG_MPU_ENABLE       (1 << 3)
+
+#define GALAXY_CONFIG_GAME_ENABLE      (1 << 4)
+
+#define GALAXY_CONFIG_CD_PANASONIC     (1 << 5)
+#define GALAXY_CONFIG_CD_MITSUMI       (1 << 6)
+#define GALAXY_CONFIG_CD_MASK          (\
+       GALAXY_CONFIG_CD_PANASONIC | GALAXY_CONFIG_CD_MITSUMI)
+
+#define GALAXY_CONFIG_UNUSED           (1 << 7)
+#define GALAXY_CONFIG_UNUSED_MASK      GALAXY_CONFIG_UNUSED
+
+#define GALAXY_CONFIG_SBIRQ_2          (1 << 8)
+#define GALAXY_CONFIG_SBIRQ_3          (1 << 9)
+#define GALAXY_CONFIG_SBIRQ_5          (1 << 10)
+#define GALAXY_CONFIG_SBIRQ_7          (1 << 11)
+
+#define GALAXY_CONFIG_MPUIRQ_2         (1 << 12)
+#define GALAXY_CONFIG_MPUIRQ_3         (1 << 13)
+#define GALAXY_CONFIG_MPUIRQ_5         (1 << 14)
+#define GALAXY_CONFIG_MPUIRQ_7         (1 << 15)
+
+#define GALAXY_CONFIG_WSSA_530         (0 << 16)
+#define GALAXY_CONFIG_WSSA_604         (1 << 16)
+#define GALAXY_CONFIG_WSSA_E80         (2 << 16)
+#define GALAXY_CONFIG_WSSA_F40         (3 << 16)
+
+#define GALAXY_CONFIG_WSS_ENABLE       (1 << 18)
+
+#define GALAXY_CONFIG_CDIRQ_11         (1 << 19)
+#define GALAXY_CONFIG_CDIRQ_12         (1 << 20)
+#define GALAXY_CONFIG_CDIRQ_15         (1 << 21)
+#define GALAXY_CONFIG_CDIRQ_MASK       (\
+       GALAXY_CONFIG_CDIRQ_11 | GALAXY_CONFIG_CDIRQ_12 |\
+       GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_CDDMA_DISABLE    (0 << 22)
+#define GALAXY_CONFIG_CDDMA_0          (1 << 22)
+#define GALAXY_CONFIG_CDDMA_1          (2 << 22)
+#define GALAXY_CONFIG_CDDMA_3          (3 << 22)
+#define GALAXY_CONFIG_CDDMA_MASK       GALAXY_CONFIG_CDDMA_3
+
+#define GALAXY_CONFIG_MASK             (\
+       GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CD_MASK |\
+       GALAXY_CONFIG_UNUSED_MASK | GALAXY_CONFIG_CDIRQ_MASK |\
+       GALAXY_CONFIG_CDDMA_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/azt2316.c b/sound/isa/galaxy/azt2316.c
new file mode 100644 (file)
index 0000000..1894411
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Aztech AZT2316 Driver
+ * Copyright (C) 2007,2010  Rene Herman
+ *
+ * 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define AZT2316
+
+#define CRD_NAME "Aztech AZT2316"
+#define DRV_NAME "AZT2316"
+#define DEV_NAME "azt2316"
+
+#define GALAXY_DSP_MAJOR               3
+#define GALAXY_DSP_MINOR               1
+
+#define GALAXY_CONFIG_SIZE             4
+
+/*
+ * 32-bit config register
+ */
+
+#define GALAXY_CONFIG_SBA_220          (0 << 0)
+#define GALAXY_CONFIG_SBA_240          (1 << 0)
+#define GALAXY_CONFIG_SBA_260          (2 << 0)
+#define GALAXY_CONFIG_SBA_280          (3 << 0)
+#define GALAXY_CONFIG_SBA_MASK         GALAXY_CONFIG_SBA_280
+
+#define GALAXY_CONFIG_SBIRQ_2          (1 << 2)
+#define GALAXY_CONFIG_SBIRQ_5          (1 << 3)
+#define GALAXY_CONFIG_SBIRQ_7          (1 << 4)
+#define GALAXY_CONFIG_SBIRQ_10         (1 << 5)
+
+#define GALAXY_CONFIG_SBDMA_DISABLE    (0 << 6)
+#define GALAXY_CONFIG_SBDMA_0          (1 << 6)
+#define GALAXY_CONFIG_SBDMA_1          (2 << 6)
+#define GALAXY_CONFIG_SBDMA_3          (3 << 6)
+
+#define GALAXY_CONFIG_WSSA_530         (0 << 8)
+#define GALAXY_CONFIG_WSSA_604         (1 << 8)
+#define GALAXY_CONFIG_WSSA_E80         (2 << 8)
+#define GALAXY_CONFIG_WSSA_F40         (3 << 8)
+
+#define GALAXY_CONFIG_WSS_ENABLE       (1 << 10)
+
+#define GALAXY_CONFIG_GAME_ENABLE      (1 << 11)
+
+#define GALAXY_CONFIG_MPUA_300         (0 << 12)
+#define GALAXY_CONFIG_MPUA_330         (1 << 12)
+
+#define GALAXY_CONFIG_MPU_ENABLE       (1 << 13)
+
+#define GALAXY_CONFIG_CDA_310          (0 << 14)
+#define GALAXY_CONFIG_CDA_320          (1 << 14)
+#define GALAXY_CONFIG_CDA_340          (2 << 14)
+#define GALAXY_CONFIG_CDA_350          (3 << 14)
+#define GALAXY_CONFIG_CDA_MASK         GALAXY_CONFIG_CDA_350
+
+#define GALAXY_CONFIG_CD_DISABLE       (0 << 16)
+#define GALAXY_CONFIG_CD_PANASONIC     (1 << 16)
+#define GALAXY_CONFIG_CD_SONY          (2 << 16)
+#define GALAXY_CONFIG_CD_MITSUMI       (3 << 16)
+#define GALAXY_CONFIG_CD_AZTECH                (4 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_5      (5 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_6      (6 << 16)
+#define GALAXY_CONFIG_CD_UNUSED_7      (7 << 16)
+#define GALAXY_CONFIG_CD_MASK          GALAXY_CONFIG_CD_UNUSED_7
+
+#define GALAXY_CONFIG_CDDMA8_DISABLE   (0 << 20)
+#define GALAXY_CONFIG_CDDMA8_0         (1 << 20)
+#define GALAXY_CONFIG_CDDMA8_1         (2 << 20)
+#define GALAXY_CONFIG_CDDMA8_3         (3 << 20)
+#define GALAXY_CONFIG_CDDMA8_MASK      GALAXY_CONFIG_CDDMA8_3
+
+#define GALAXY_CONFIG_CDDMA16_DISABLE  (0 << 22)
+#define GALAXY_CONFIG_CDDMA16_5                (1 << 22)
+#define GALAXY_CONFIG_CDDMA16_6                (2 << 22)
+#define GALAXY_CONFIG_CDDMA16_7                (3 << 22)
+#define GALAXY_CONFIG_CDDMA16_MASK     GALAXY_CONFIG_CDDMA16_7
+
+#define GALAXY_CONFIG_MPUIRQ_2         (1 << 24)
+#define GALAXY_CONFIG_MPUIRQ_5         (1 << 25)
+#define GALAXY_CONFIG_MPUIRQ_7         (1 << 26)
+#define GALAXY_CONFIG_MPUIRQ_10                (1 << 27)
+
+#define GALAXY_CONFIG_CDIRQ_5          (1 << 28)
+#define GALAXY_CONFIG_CDIRQ_11         (1 << 29)
+#define GALAXY_CONFIG_CDIRQ_12         (1 << 30)
+#define GALAXY_CONFIG_CDIRQ_15         (1 << 31)
+#define GALAXY_CONFIG_CDIRQ_MASK       (\
+       GALAXY_CONFIG_CDIRQ_5 | GALAXY_CONFIG_CDIRQ_11 |\
+       GALAXY_CONFIG_CDIRQ_12 | GALAXY_CONFIG_CDIRQ_15)
+
+#define GALAXY_CONFIG_MASK             (\
+       GALAXY_CONFIG_SBA_MASK | GALAXY_CONFIG_CDA_MASK |\
+       GALAXY_CONFIG_CD_MASK | GALAXY_CONFIG_CDDMA16_MASK |\
+       GALAXY_CONFIG_CDDMA8_MASK | GALAXY_CONFIG_CDIRQ_MASK)
+
+#include "galaxy.c"
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
new file mode 100644 (file)
index 0000000..ee54df0
--- /dev/null
@@ -0,0 +1,652 @@
+/*
+ * Aztech AZT1605/AZT2316 Driver
+ * Copyright (C) 2007,2010  Rene Herman
+ *
+ * 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, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/isa.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <asm/processor.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/wss.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+
+MODULE_DESCRIPTION(CRD_NAME);
+MODULE_AUTHOR("Rene Herman");
+MODULE_LICENSE("GPL");
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for " CRD_NAME " soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
+
+static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long wss_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
+static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
+module_param_array(wss_port, long, NULL, 0444);
+MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
+module_param_array(fm_port, long, NULL, 0444);
+MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
+module_param_array(mpu_irq, int, NULL, 0444);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
+module_param_array(dma1, int, NULL, 0444);
+MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
+module_param_array(dma2, int, NULL, 0444);
+MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
+
+/*
+ * Generic SB DSP support routines
+ */
+
+#define DSP_PORT_RESET         0x6
+#define DSP_PORT_READ          0xa
+#define DSP_PORT_COMMAND       0xc
+#define DSP_PORT_STATUS                0xc
+#define DSP_PORT_DATA_AVAIL    0xe
+
+#define DSP_SIGNATURE          0xaa
+
+#define DSP_COMMAND_GET_VERSION        0xe1
+
+static int __devinit dsp_get_byte(void __iomem *port, u8 *val)
+{
+       int loops = 1000;
+
+       while (!(ioread8(port + DSP_PORT_DATA_AVAIL) & 0x80)) {
+               if (!loops--)
+                       return -EIO;
+               cpu_relax();
+       }
+       *val = ioread8(port + DSP_PORT_READ);
+       return 0;
+}
+
+static int __devinit dsp_reset(void __iomem *port)
+{
+       u8 val;
+
+       iowrite8(1, port + DSP_PORT_RESET);
+       udelay(10);
+       iowrite8(0, port + DSP_PORT_RESET);
+
+       if (dsp_get_byte(port, &val) < 0 || val != DSP_SIGNATURE)
+               return -ENODEV;
+
+       return 0;
+}
+
+static int __devinit dsp_command(void __iomem *port, u8 cmd)
+{
+       int loops = 1000;
+
+       while (ioread8(port + DSP_PORT_STATUS) & 0x80) {
+               if (!loops--)
+                       return -EIO;
+               cpu_relax();
+       }
+       iowrite8(cmd, port + DSP_PORT_COMMAND);
+       return 0;
+}
+
+static int __devinit dsp_get_version(void __iomem *port, u8 *major, u8 *minor)
+{
+       int err;
+
+       err = dsp_command(port, DSP_COMMAND_GET_VERSION);
+       if (err < 0)
+               return err;
+
+       err = dsp_get_byte(port, major);
+       if (err < 0)
+               return err;
+
+       err = dsp_get_byte(port, minor);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+/*
+ * Generic WSS support routines
+ */
+
+#define WSS_CONFIG_DMA_0       (1 << 0)
+#define WSS_CONFIG_DMA_1       (2 << 0)
+#define WSS_CONFIG_DMA_3       (3 << 0)
+#define WSS_CONFIG_DUPLEX      (1 << 2)
+#define WSS_CONFIG_IRQ_7       (1 << 3)
+#define WSS_CONFIG_IRQ_9       (2 << 3)
+#define WSS_CONFIG_IRQ_10      (3 << 3)
+#define WSS_CONFIG_IRQ_11      (4 << 3)
+
+#define WSS_PORT_CONFIG                0
+#define WSS_PORT_SIGNATURE     3
+
+#define WSS_SIGNATURE          4
+
+static int __devinit wss_detect(void __iomem *wss_port)
+{
+       if ((ioread8(wss_port + WSS_PORT_SIGNATURE) & 0x3f) != WSS_SIGNATURE)
+               return -ENODEV;
+
+       return 0;
+}
+
+static void wss_set_config(void __iomem *wss_port, u8 wss_config)
+{
+       iowrite8(wss_config, wss_port + WSS_PORT_CONFIG);
+}
+
+/*
+ * Aztech Sound Galaxy specifics
+ */
+
+#define GALAXY_PORT_CONFIG     1024
+#define CONFIG_PORT_SET                4
+
+#define DSP_COMMAND_GALAXY_8   8
+#define GALAXY_COMMAND_GET_TYPE        5
+
+#define DSP_COMMAND_GALAXY_9   9
+#define GALAXY_COMMAND_WSSMODE 0
+#define GALAXY_COMMAND_SB8MODE 1
+
+#define GALAXY_MODE_WSS                GALAXY_COMMAND_WSSMODE
+#define GALAXY_MODE_SB8                GALAXY_COMMAND_SB8MODE
+
+struct snd_galaxy {
+       void __iomem *port;
+       void __iomem *config_port;
+       void __iomem *wss_port;
+       u32 config;
+       struct resource *res_port;
+       struct resource *res_config_port;
+       struct resource *res_wss_port;
+};
+
+static u32 config[SNDRV_CARDS];
+static u8 wss_config[SNDRV_CARDS];
+
+static int __devinit snd_galaxy_match(struct device *dev, unsigned int n)
+{
+       if (!enable[n])
+               return 0;
+
+       switch (port[n]) {
+       case SNDRV_AUTO_PORT:
+               dev_err(dev, "please specify port\n");
+               return 0;
+       case 0x220:
+               config[n] |= GALAXY_CONFIG_SBA_220;
+               break;
+       case 0x240:
+               config[n] |= GALAXY_CONFIG_SBA_240;
+               break;
+       case 0x260:
+               config[n] |= GALAXY_CONFIG_SBA_260;
+               break;
+       case 0x280:
+               config[n] |= GALAXY_CONFIG_SBA_280;
+               break;
+       default:
+               dev_err(dev, "invalid port %#lx\n", port[n]);
+               return 0;
+       }
+
+       switch (wss_port[n]) {
+       case SNDRV_AUTO_PORT:
+               dev_err(dev,  "please specify wss_port\n");
+               return 0;
+       case 0x530:
+               config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_530;
+               break;
+       case 0x604:
+               config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_604;
+               break;
+       case 0xe80:
+               config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_E80;
+               break;
+       case 0xf40:
+               config[n] |= GALAXY_CONFIG_WSS_ENABLE | GALAXY_CONFIG_WSSA_F40;
+               break;
+       default:
+               dev_err(dev, "invalid WSS port %#lx\n", wss_port[n]);
+               return 0;
+       }
+
+       switch (irq[n]) {
+       case SNDRV_AUTO_IRQ:
+               dev_err(dev,  "please specify irq\n");
+               return 0;
+       case 7:
+               wss_config[n] |= WSS_CONFIG_IRQ_7;
+               break;
+       case 2:
+               irq[n] = 9;
+       case 9:
+               wss_config[n] |= WSS_CONFIG_IRQ_9;
+               break;
+       case 10:
+               wss_config[n] |= WSS_CONFIG_IRQ_10;
+               break;
+       case 11:
+               wss_config[n] |= WSS_CONFIG_IRQ_11;
+               break;
+       default:
+               dev_err(dev, "invalid IRQ %d\n", irq[n]);
+               return 0;
+       }
+
+       switch (dma1[n]) {
+       case SNDRV_AUTO_DMA:
+               dev_err(dev,  "please specify dma1\n");
+               return 0;
+       case 0:
+               wss_config[n] |= WSS_CONFIG_DMA_0;
+               break;
+       case 1:
+               wss_config[n] |= WSS_CONFIG_DMA_1;
+               break;
+       case 3:
+               wss_config[n] |= WSS_CONFIG_DMA_3;
+               break;
+       default:
+               dev_err(dev, "invalid playback DMA %d\n", dma1[n]);
+               return 0;
+       }
+
+       if (dma2[n] == SNDRV_AUTO_DMA || dma2[n] == dma1[n]) {
+               dma2[n] = -1;
+               goto mpu;
+       }
+
+       wss_config[n] |= WSS_CONFIG_DUPLEX;
+       switch (dma2[n]) {
+       case 0:
+               break;
+       case 1:
+               if (dma1[n] == 0)
+                       break;
+       default:
+               dev_err(dev, "invalid capture DMA %d\n", dma2[n]);
+               return 0;
+       }
+
+mpu:
+       switch (mpu_port[n]) {
+       case SNDRV_AUTO_PORT:
+               dev_warn(dev, "mpu_port not specified; not using MPU-401\n");
+               mpu_port[n] = -1;
+               goto fm;
+       case 0x300:
+               config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_300;
+               break;
+       case 0x330:
+               config[n] |= GALAXY_CONFIG_MPU_ENABLE | GALAXY_CONFIG_MPUA_330;
+               break;
+       default:
+               dev_err(dev, "invalid MPU port %#lx\n", mpu_port[n]);
+               return 0;
+       }
+
+       switch (mpu_irq[n]) {
+       case SNDRV_AUTO_IRQ:
+               dev_warn(dev, "mpu_irq not specified: using polling mode\n");
+               mpu_irq[n] = -1;
+               break;
+       case 2:
+               mpu_irq[n] = 9;
+       case 9:
+               config[n] |= GALAXY_CONFIG_MPUIRQ_2;
+               break;
+#ifdef AZT1605
+       case 3:
+               config[n] |= GALAXY_CONFIG_MPUIRQ_3;
+               break;
+#endif
+       case 5:
+               config[n] |= GALAXY_CONFIG_MPUIRQ_5;
+               break;
+       case 7:
+               config[n] |= GALAXY_CONFIG_MPUIRQ_7;
+               break;
+#ifdef AZT2316
+       case 10:
+               config[n] |= GALAXY_CONFIG_MPUIRQ_10;
+               break;
+#endif
+       default:
+               dev_err(dev, "invalid MPU IRQ %d\n", mpu_irq[n]);
+               return 0;
+       }
+
+       if (mpu_irq[n] == irq[n]) {
+               dev_err(dev, "cannot share IRQ between WSS and MPU-401\n");
+               return 0;
+       }
+
+fm:
+       switch (fm_port[n]) {
+       case SNDRV_AUTO_PORT:
+               dev_warn(dev, "fm_port not specified: not using OPL3\n");
+               fm_port[n] = -1;
+               break;
+       case 0x388:
+               break;
+       default:
+               dev_err(dev, "illegal FM port %#lx\n", fm_port[n]);
+               return 0;
+       }
+
+       config[n] |= GALAXY_CONFIG_GAME_ENABLE;
+       return 1;
+}
+
+static int __devinit galaxy_init(struct snd_galaxy *galaxy, u8 *type)
+{
+       u8 major;
+       u8 minor;
+       int err;
+
+       err = dsp_reset(galaxy->port);
+       if (err < 0)
+               return err;
+
+       err = dsp_get_version(galaxy->port, &major, &minor);
+       if (err < 0)
+               return err;
+
+       if (major != GALAXY_DSP_MAJOR || minor != GALAXY_DSP_MINOR)
+               return -ENODEV;
+
+       err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_8);
+       if (err < 0)
+               return err;
+
+       err = dsp_command(galaxy->port, GALAXY_COMMAND_GET_TYPE);
+       if (err < 0)
+               return err;
+
+       err = dsp_get_byte(galaxy->port, type);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static int __devinit galaxy_set_mode(struct snd_galaxy *galaxy, u8 mode)
+{
+       int err;
+
+       err = dsp_command(galaxy->port, DSP_COMMAND_GALAXY_9);
+       if (err < 0)
+               return err;
+
+       err = dsp_command(galaxy->port, mode);
+       if (err < 0)
+               return err;
+
+#ifdef AZT1605
+       /*
+        * Needed for MPU IRQ on AZT1605, but AZT2316 loses WSS again
+        */
+       err = dsp_reset(galaxy->port);
+       if (err < 0)
+               return err;
+#endif
+
+       return 0;
+}
+
+static void galaxy_set_config(struct snd_galaxy *galaxy, u32 config)
+{
+       u8 tmp = ioread8(galaxy->config_port + CONFIG_PORT_SET);
+       int i;
+
+       iowrite8(tmp | 0x80, galaxy->config_port + CONFIG_PORT_SET);
+       for (i = 0; i < GALAXY_CONFIG_SIZE; i++) {
+               iowrite8(config, galaxy->config_port + i);
+               config >>= 8;
+       }
+       iowrite8(tmp & 0x7f, galaxy->config_port + CONFIG_PORT_SET);
+       msleep(10);
+}
+
+static void __devinit galaxy_config(struct snd_galaxy *galaxy, u32 config)
+{
+       int i;
+
+       for (i = GALAXY_CONFIG_SIZE; i; i--) {
+               u8 tmp = ioread8(galaxy->config_port + i - 1);
+               galaxy->config = (galaxy->config << 8) | tmp;
+       }
+       config |= galaxy->config & GALAXY_CONFIG_MASK;
+       galaxy_set_config(galaxy, config);
+}
+
+static int __devinit galaxy_wss_config(struct snd_galaxy *galaxy, u8 wss_config)
+{
+       int err;
+
+       err = wss_detect(galaxy->wss_port);
+       if (err < 0)
+               return err;
+
+       wss_set_config(galaxy->wss_port, wss_config);
+
+       err = galaxy_set_mode(galaxy, GALAXY_MODE_WSS);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+static void snd_galaxy_free(struct snd_card *card)
+{
+       struct snd_galaxy *galaxy = card->private_data;
+
+       if (galaxy->wss_port) {
+               wss_set_config(galaxy->wss_port, 0);
+               ioport_unmap(galaxy->wss_port);
+               release_and_free_resource(galaxy->res_wss_port);
+       }
+       if (galaxy->config_port) {
+               galaxy_set_config(galaxy, galaxy->config);
+               ioport_unmap(galaxy->config_port);
+               release_and_free_resource(galaxy->res_config_port);
+       }
+       if (galaxy->port) {
+               ioport_unmap(galaxy->port);
+               release_and_free_resource(galaxy->res_port);
+       }
+}
+
+static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
+{
+       struct snd_galaxy *galaxy;
+       struct snd_wss *chip;
+       struct snd_card *card;
+       u8 type;
+       int err;
+
+       err = snd_card_create(index[n], id[n], THIS_MODULE, sizeof *galaxy,
+                             &card);
+       if (err < 0)
+               return err;
+
+       snd_card_set_dev(card, dev);
+
+       card->private_free = snd_galaxy_free;
+       galaxy = card->private_data;
+
+       galaxy->res_port = request_region(port[n], 16, DRV_NAME);
+       if (!galaxy->res_port) {
+               dev_err(dev, "could not grab ports %#lx-%#lx\n", port[n],
+                       port[n] + 15);
+               err = -EBUSY;
+               goto error;
+       }
+       galaxy->port = ioport_map(port[n], 16);
+
+       err = galaxy_init(galaxy, &type);
+       if (err < 0) {
+               dev_err(dev, "did not find a Sound Galaxy at %#lx\n", port[n]);
+               goto error;
+       }
+       dev_info(dev, "Sound Galaxy (type %d) found at %#lx\n", type, port[n]);
+
+       galaxy->res_config_port = request_region(port[n] + GALAXY_PORT_CONFIG,
+                                                16, DRV_NAME);
+       if (!galaxy->res_config_port) {
+               dev_err(dev, "could not grab ports %#lx-%#lx\n",
+                       port[n] + GALAXY_PORT_CONFIG,
+                       port[n] + GALAXY_PORT_CONFIG + 15);
+               err = -EBUSY;
+               goto error;
+       }
+       galaxy->config_port = ioport_map(port[n] + GALAXY_PORT_CONFIG, 16);
+
+       galaxy_config(galaxy, config[n]);
+
+       galaxy->res_wss_port = request_region(wss_port[n], 4, DRV_NAME);
+       if (!galaxy->res_wss_port)  {
+               dev_err(dev, "could not grab ports %#lx-%#lx\n", wss_port[n],
+                       wss_port[n] + 3);
+               err = -EBUSY;
+               goto error;
+       }
+       galaxy->wss_port = ioport_map(wss_port[n], 4);
+
+       err = galaxy_wss_config(galaxy, wss_config[n]);
+       if (err < 0) {
+               dev_err(dev, "could not configure WSS\n");
+               goto error;
+       }
+
+       strcpy(card->driver, DRV_NAME);
+       strcpy(card->shortname, DRV_NAME);
+       sprintf(card->longname, "%s at %#lx/%#lx, irq %d, dma %d/%d",
+               card->shortname, port[n], wss_port[n], irq[n], dma1[n],
+               dma2[n]);
+
+       err = snd_wss_create(card, wss_port[n] + 4, -1, irq[n], dma1[n],
+                            dma2[n], WSS_HW_DETECT, 0, &chip);
+       if (err < 0)
+               goto error;
+
+       err = snd_wss_pcm(chip, 0, NULL);
+       if (err < 0)
+               goto error;
+
+       err = snd_wss_mixer(chip);
+       if (err < 0)
+               goto error;
+
+       err = snd_wss_timer(chip, 0, NULL);
+       if (err < 0)
+               goto error;
+
+       if (mpu_port[n] >= 0) {
+               err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
+                                         mpu_port[n], 0, mpu_irq[n],
+                                         IRQF_DISABLED, NULL);
+               if (err < 0)
+                       goto error;
+       }
+
+       if (fm_port[n] >= 0) {
+               struct snd_opl3 *opl3;
+
+               err = snd_opl3_create(card, fm_port[n], fm_port[n] + 2,
+                                     OPL3_HW_AUTO, 0, &opl3);
+               if (err < 0) {
+                       dev_err(dev, "no OPL device at %#lx\n", fm_port[n]);
+                       goto error;
+               }
+               err = snd_opl3_timer_new(opl3, 1, 2);
+               if (err < 0)
+                       goto error;
+
+               err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+               if (err < 0)
+                       goto error;
+       }
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(dev, card);
+       return 0;
+
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static int __devexit snd_galaxy_remove(struct device *dev, unsigned int n)
+{
+       snd_card_free(dev_get_drvdata(dev));
+       dev_set_drvdata(dev, NULL);
+       return 0;
+}
+
+static struct isa_driver snd_galaxy_driver = {
+       .match          = snd_galaxy_match,
+       .probe          = snd_galaxy_probe,
+       .remove         = __devexit_p(snd_galaxy_remove),
+
+       .driver         = {
+               .name   = DEV_NAME
+       }
+};
+
+static int __init alsa_card_galaxy_init(void)
+{
+       return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
+}
+
+static void __exit alsa_card_galaxy_exit(void)
+{
+       isa_unregister_driver(&snd_galaxy_driver);
+}
+
+module_init(alsa_card_galaxy_init);
+module_exit(alsa_card_galaxy_exit);
diff --git a/sound/isa/sgalaxy.c b/sound/isa/sgalaxy.c
deleted file mode 100644 (file)
index 6fe27b9..0000000
+++ /dev/null
@@ -1,369 +0,0 @@
-/*
- *  Driver for Aztech Sound Galaxy cards
- *  Copyright (c) by Christopher Butler <chrisb@sandy.force9.co.uk.
- *
- *  I don't have documentation for this card, I based this driver on the
- *  driver for OSS/Free included in the kernel source (drivers/sound/sgalaxy.c)
- *
- *   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; either version 2 of the License, or
- *   (at your option) any later version.
- *
- *   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
- *
- */
-
-#include <linux/init.h>
-#include <linux/err.h>
-#include <linux/isa.h>
-#include <linux/delay.h>
-#include <linux/time.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <asm/dma.h>
-#include <sound/core.h>
-#include <sound/sb.h>
-#include <sound/wss.h>
-#include <sound/control.h>
-#define SNDRV_LEGACY_FIND_FREE_IRQ
-#define SNDRV_LEGACY_FIND_FREE_DMA
-#include <sound/initval.h>
-
-MODULE_AUTHOR("Christopher Butler <chrisb@sandy.force9.co.uk>");
-MODULE_DESCRIPTION("Aztech Sound Galaxy");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Aztech Systems,Sound Galaxy}}");
-
-static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;     /* Index 0-MAX */
-static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;      /* ID for this card */
-static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
-static long sbport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;  /* 0x220,0x240 */
-static long wssport[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* 0x530,0xe80,0xf40,0x604 */
-static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;       /* 7,9,10,11 */
-static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;      /* 0,1,3 */
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for Sound Galaxy soundcard.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for Sound Galaxy soundcard.");
-module_param_array(sbport, long, NULL, 0444);
-MODULE_PARM_DESC(sbport, "Port # for Sound Galaxy SB driver.");
-module_param_array(wssport, long, NULL, 0444);
-MODULE_PARM_DESC(wssport, "Port # for Sound Galaxy WSS driver.");
-module_param_array(irq, int, NULL, 0444);
-MODULE_PARM_DESC(irq, "IRQ # for Sound Galaxy driver.");
-module_param_array(dma1, int, NULL, 0444);
-MODULE_PARM_DESC(dma1, "DMA1 # for Sound Galaxy driver.");
-
-#define SGALAXY_AUXC_LEFT 18
-#define SGALAXY_AUXC_RIGHT 19
-
-#define PFX    "sgalaxy: "
-
-/*
-
- */
-
-#define AD1848P1( port, x ) ( port + c_d_c_AD1848##x )
-
-/* from lowlevel/sb/sb.c - to avoid having to allocate a struct snd_sb for the */
-/* short time we actually need it.. */
-
-static int snd_sgalaxy_sbdsp_reset(unsigned long port)
-{
-       int i;
-
-       outb(1, SBP1(port, RESET));
-       udelay(10);
-       outb(0, SBP1(port, RESET));
-       udelay(30);
-       for (i = 0; i < 1000 && !(inb(SBP1(port, DATA_AVAIL)) & 0x80); i++);
-       if (inb(SBP1(port, READ)) != 0xaa) {
-               snd_printd("sb_reset: failed at 0x%lx!!!\n", port);
-               return -ENODEV;
-       }
-       return 0;
-}
-
-static int __devinit snd_sgalaxy_sbdsp_command(unsigned long port,
-                                              unsigned char val)
-{
-       int i;
-               
-       for (i = 10000; i; i--)
-               if ((inb(SBP1(port, STATUS)) & 0x80) == 0) {
-                       outb(val, SBP1(port, COMMAND));
-                       return 1;
-               }
-
-       return 0;
-}
-
-static irqreturn_t snd_sgalaxy_dummy_interrupt(int irq, void *dev_id)
-{
-       return IRQ_NONE;
-}
-
-static int __devinit snd_sgalaxy_setup_wss(unsigned long port, int irq, int dma)
-{
-       static int interrupt_bits[] = {-1, -1, -1, -1, -1, -1, -1, 0x08, -1, 
-                                      0x10, 0x18, 0x20, -1, -1, -1, -1};
-       static int dma_bits[] = {1, 2, 0, 3};
-       int tmp, tmp1;
-
-       if ((tmp = inb(port + 3)) == 0xff)
-       {
-               snd_printdd("I/O address dead (0x%lx)\n", port);
-               return 0;
-       }
-#if 0
-       snd_printdd("WSS signature = 0x%x\n", tmp);
-#endif
-
-        if ((tmp & 0x3f) != 0x04 &&
-            (tmp & 0x3f) != 0x0f &&
-            (tmp & 0x3f) != 0x00) {
-               snd_printdd("No WSS signature detected on port 0x%lx\n",
-                           port + 3);
-               return 0;
-       }
-
-#if 0
-       snd_printdd(PFX "setting up IRQ/DMA for WSS\n");
-#endif
-
-        /* initialize IRQ for WSS codec */
-        tmp = interrupt_bits[irq % 16];
-        if (tmp < 0)
-                return -EINVAL;
-
-       if (request_irq(irq, snd_sgalaxy_dummy_interrupt, IRQF_DISABLED, "sgalaxy", NULL)) {
-               snd_printk(KERN_ERR "sgalaxy: can't grab irq %d\n", irq);
-               return -EIO;
-       }
-
-        outb(tmp | 0x40, port);
-        tmp1 = dma_bits[dma % 4];
-        outb(tmp | tmp1, port);
-
-       free_irq(irq, NULL);
-
-       return 0;
-}
-
-static int __devinit snd_sgalaxy_detect(int dev, int irq, int dma)
-{
-#if 0
-       snd_printdd(PFX "switching to WSS mode\n");
-#endif
-
-       /* switch to WSS mode */
-       snd_sgalaxy_sbdsp_reset(sbport[dev]);
-
-       snd_sgalaxy_sbdsp_command(sbport[dev], 9);
-       snd_sgalaxy_sbdsp_command(sbport[dev], 0);
-
-       udelay(400);
-       return snd_sgalaxy_setup_wss(wssport[dev], irq, dma);
-}
-
-static struct snd_kcontrol_new snd_sgalaxy_controls[] = {
-WSS_DOUBLE("Aux Playback Switch", 0,
-               SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
-               SGALAXY_AUXC_LEFT, SGALAXY_AUXC_RIGHT, 0, 0, 31, 0)
-};
-
-static int __devinit snd_sgalaxy_mixer(struct snd_wss *chip)
-{
-       struct snd_card *card = chip->card;
-       struct snd_ctl_elem_id id1, id2;
-       unsigned int idx;
-       int err;
-
-       memset(&id1, 0, sizeof(id1));
-       memset(&id2, 0, sizeof(id2));
-       id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-       /* reassign AUX0 to LINE */
-       strcpy(id1.name, "Aux Playback Switch");
-       strcpy(id2.name, "Line Playback Switch");
-       if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-               return err;
-       strcpy(id1.name, "Aux Playback Volume");
-       strcpy(id2.name, "Line Playback Volume");
-       if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-               return err;
-       /* reassign AUX1 to FM */
-       strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
-       strcpy(id2.name, "FM Playback Switch");
-       if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-               return err;
-       strcpy(id1.name, "Aux Playback Volume");
-       strcpy(id2.name, "FM Playback Volume");
-       if ((err = snd_ctl_rename_id(card, &id1, &id2)) < 0)
-               return err;
-       /* build AUX2 input */
-       for (idx = 0; idx < ARRAY_SIZE(snd_sgalaxy_controls); idx++) {
-               err = snd_ctl_add(card,
-                               snd_ctl_new1(&snd_sgalaxy_controls[idx], chip));
-               if (err < 0)
-                       return err;
-       }
-       return 0;
-}
-
-static int __devinit snd_sgalaxy_match(struct device *devptr, unsigned int dev)
-{
-       if (!enable[dev])
-               return 0;
-       if (sbport[dev] == SNDRV_AUTO_PORT) {
-               snd_printk(KERN_ERR PFX "specify SB port\n");
-               return 0;
-       }
-       if (wssport[dev] == SNDRV_AUTO_PORT) {
-               snd_printk(KERN_ERR PFX "specify WSS port\n");
-               return 0;
-       }
-       return 1;
-}
-
-static int __devinit snd_sgalaxy_probe(struct device *devptr, unsigned int dev)
-{
-       static int possible_irqs[] = {7, 9, 10, 11, -1};
-       static int possible_dmas[] = {1, 3, 0, -1};
-       int err, xirq, xdma1;
-       struct snd_card *card;
-       struct snd_wss *chip;
-
-       err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &card);
-       if (err < 0)
-               return err;
-
-       xirq = irq[dev];
-       if (xirq == SNDRV_AUTO_IRQ) {
-               if ((xirq = snd_legacy_find_free_irq(possible_irqs)) < 0) {
-                       snd_printk(KERN_ERR PFX "unable to find a free IRQ\n");
-                       err = -EBUSY;
-                       goto _err;
-               }
-       }
-       xdma1 = dma1[dev];
-        if (xdma1 == SNDRV_AUTO_DMA) {
-               if ((xdma1 = snd_legacy_find_free_dma(possible_dmas)) < 0) {
-                       snd_printk(KERN_ERR PFX "unable to find a free DMA\n");
-                       err = -EBUSY;
-                       goto _err;
-               }
-       }
-
-       if ((err = snd_sgalaxy_detect(dev, xirq, xdma1)) < 0)
-               goto _err;
-
-       err = snd_wss_create(card, wssport[dev] + 4, -1,
-                            xirq, xdma1, -1,
-                            WSS_HW_DETECT, 0, &chip);
-       if (err < 0)
-               goto _err;
-       card->private_data = chip;
-
-       err = snd_wss_pcm(chip, 0, NULL);
-       if (err < 0) {
-               snd_printdd(PFX "error creating new WSS PCM device\n");
-               goto _err;
-       }
-       err = snd_wss_mixer(chip);
-       if (err < 0) {
-               snd_printdd(PFX "error creating new WSS mixer\n");
-               goto _err;
-       }
-       if ((err = snd_sgalaxy_mixer(chip)) < 0) {
-               snd_printdd(PFX "the mixer rewrite failed\n");
-               goto _err;
-       }
-
-       strcpy(card->driver, "Sound Galaxy");
-       strcpy(card->shortname, "Sound Galaxy");
-       sprintf(card->longname, "Sound Galaxy at 0x%lx, irq %d, dma %d",
-               wssport[dev], xirq, xdma1);
-
-       snd_card_set_dev(card, devptr);
-
-       if ((err = snd_card_register(card)) < 0)
-               goto _err;
-
-       dev_set_drvdata(devptr, card);
-       return 0;
-
- _err:
-       snd_card_free(card);
-       return err;
-}
-
-static int __devexit snd_sgalaxy_remove(struct device *devptr, unsigned int dev)
-{
-       snd_card_free(dev_get_drvdata(devptr));
-       dev_set_drvdata(devptr, NULL);
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int snd_sgalaxy_suspend(struct device *pdev, unsigned int n,
-                              pm_message_t state)
-{
-       struct snd_card *card = dev_get_drvdata(pdev);
-       struct snd_wss *chip = card->private_data;
-
-       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-       chip->suspend(chip);
-       return 0;
-}
-
-static int snd_sgalaxy_resume(struct device *pdev, unsigned int n)
-{
-       struct snd_card *card = dev_get_drvdata(pdev);
-       struct snd_wss *chip = card->private_data;
-
-       chip->resume(chip);
-       snd_wss_out(chip, SGALAXY_AUXC_LEFT, chip->image[SGALAXY_AUXC_LEFT]);
-       snd_wss_out(chip, SGALAXY_AUXC_RIGHT, chip->image[SGALAXY_AUXC_RIGHT]);
-
-       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
-       return 0;
-}
-#endif
-
-#define DEV_NAME "sgalaxy"
-
-static struct isa_driver snd_sgalaxy_driver = {
-       .match          = snd_sgalaxy_match,
-       .probe          = snd_sgalaxy_probe,
-       .remove         = __devexit_p(snd_sgalaxy_remove),
-#ifdef CONFIG_PM
-       .suspend        = snd_sgalaxy_suspend,
-       .resume         = snd_sgalaxy_resume,
-#endif
-       .driver         = {
-               .name   = DEV_NAME
-       },
-};
-
-static int __init alsa_card_sgalaxy_init(void)
-{
-       return isa_register_driver(&snd_sgalaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sgalaxy_exit(void)
-{
-       isa_unregister_driver(&snd_sgalaxy_driver);
-}
-
-module_init(alsa_card_sgalaxy_init)
-module_exit(alsa_card_sgalaxy_exit)
index 9feb00c..498a2d8 100644 (file)
@@ -299,9 +299,13 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
 
        *rchip = NULL;
 
-       if (snd_usb_get_speed(dev) != USB_SPEED_LOW &&
-           snd_usb_get_speed(dev) != USB_SPEED_FULL &&
-           snd_usb_get_speed(dev) != USB_SPEED_HIGH) {
+       switch (snd_usb_get_speed(dev)) {
+       case USB_SPEED_LOW:
+       case USB_SPEED_FULL:
+       case USB_SPEED_HIGH:
+       case USB_SPEED_SUPER:
+               break;
+       default:
                snd_printk(KERN_ERR "unknown device speed %d\n", snd_usb_get_speed(dev));
                return -ENXIO;
        }
@@ -377,11 +381,22 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
        if (len < sizeof(card->longname))
                usb_make_path(dev, card->longname + len, sizeof(card->longname) - len);
 
-       strlcat(card->longname,
-               snd_usb_get_speed(dev) == USB_SPEED_LOW ? ", low speed" :
-               snd_usb_get_speed(dev) == USB_SPEED_FULL ? ", full speed" :
-               ", high speed",
-               sizeof(card->longname));
+       switch (snd_usb_get_speed(dev)) {
+       case USB_SPEED_LOW:
+               strlcat(card->longname, ", low speed", sizeof(card->longname));
+               break;
+       case USB_SPEED_FULL:
+               strlcat(card->longname, ", full speed", sizeof(card->longname));
+               break;
+       case USB_SPEED_HIGH:
+               strlcat(card->longname, ", high speed", sizeof(card->longname));
+               break;
+       case USB_SPEED_SUPER:
+               strlcat(card->longname, ", super speed", sizeof(card->longname));
+               break;
+       default:
+               break;
+       }
 
        snd_usb_audio_create_proc(chip);
 
index d48d6f8..f280c19 100644 (file)
@@ -103,11 +103,16 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
 unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
                                         struct usb_host_interface *alts)
 {
-       if (snd_usb_get_speed(chip->dev) == USB_SPEED_HIGH &&
-           get_endpoint(alts, 0)->bInterval >= 1 &&
-           get_endpoint(alts, 0)->bInterval <= 4)
-               return get_endpoint(alts, 0)->bInterval - 1;
-       else
-               return 0;
+       switch (snd_usb_get_speed(chip->dev)) {
+       case USB_SPEED_HIGH:
+       case USB_SPEED_SUPER:
+               if (get_endpoint(alts, 0)->bInterval >= 1 &&
+                   get_endpoint(alts, 0)->bInterval <= 4)
+                       return get_endpoint(alts, 0)->bInterval - 1;
+               break;
+       default:
+               break;
+       }
+       return 0;
 }
 
index b9c2bc6..156cd07 100644 (file)
@@ -834,7 +834,14 @@ static void snd_usbmidi_us122l_output(struct snd_usb_midi_out_endpoint *ep,
 
        if (!ep->ports[0].active)
                return;
-       count = snd_usb_get_speed(ep->umidi->dev) == USB_SPEED_HIGH ? 1 : 2;
+       switch (snd_usb_get_speed(ep->umidi->dev)) {
+       case USB_SPEED_HIGH:
+       case USB_SPEED_SUPER:
+               count = 1;
+               break;
+       default:
+               count = 2;
+       }
        count = snd_rawmidi_transmit(ep->ports[0].substream,
                                     urb->transfer_buffer,
                                     count);
index 3634ced..58ed682 100644 (file)
@@ -467,7 +467,7 @@ static int hw_check_valid_format(struct snd_usb_substream *subs,
                return 0;
        }
        /* check whether the period time is >= the data packet interval */
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH) {
+       if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL) {
                ptime = 125 * (1 << fp->datainterval);
                if (ptime > pt->max || (ptime == pt->max && pt->openmax)) {
                        hwc_debug("   > check: ptime %u > max %u\n", ptime, pt->max);
@@ -735,7 +735,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
        }
 
        param_period_time_if_needed = SNDRV_PCM_HW_PARAM_PERIOD_TIME;
-       if (snd_usb_get_speed(subs->dev) != USB_SPEED_HIGH)
+       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
                /* full speed devices have fixed data packet interval */
                ptmin = 1000;
        if (ptmin == 1000)
index f5e3f35..3c650ab 100644 (file)
@@ -107,7 +107,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
                        }
                        snd_iprintf(buffer, "\n");
                }
-               if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+               if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
                        snd_iprintf(buffer, "    Data packet interval: %d us\n",
                                    125 * (1 << fp->datainterval));
                // snd_iprintf(buffer, "    Max Packet Size = %d\n", fp->maxpacksize);
index de607d4..8deeaad 100644 (file)
@@ -244,7 +244,7 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
        else
                subs->curpacksize = maxsize;
 
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_HIGH)
+       if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
                packs_per_ms = 8 >> subs->datainterval;
        else
                packs_per_ms = 1;