Merge branch 'fix/misc' into topic/misc
authorTakashi Iwai <tiwai@suse.de>
Wed, 17 Feb 2010 13:24:46 +0000 (14:24 +0100)
committerTakashi Iwai <tiwai@suse.de>
Wed, 17 Feb 2010 13:24:46 +0000 (14:24 +0100)
Conflicts:
sound/pci/hda/patch_realtek.c

154 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
include/sound/core.h
include/sound/cs46xx_dsp_spos.h
include/sound/pcm.h
include/sound/pcm_oss.h
include/sound/sb.h
include/sound/version.h
sound/core/control.c
sound/core/misc.c
sound/core/oss/pcm_oss.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_memory.c
sound/core/pcm_native.c
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_timer.c
sound/drivers/dummy.c
sound/drivers/vx/vx_pcm.c
sound/isa/Kconfig
sound/isa/Makefile
sound/isa/als100.c
sound/isa/dt019x.c [deleted file]
sound/isa/opti9xx/opti92x-ad1848.c
sound/isa/sb/Makefile
sound/isa/sb/jazz16.c [new file with mode: 0644]
sound/isa/sb/sb8_main.c
sound/isa/sb/sb_common.c
sound/isa/sb/sb_mixer.c
sound/isa/wss/wss_lib.c
sound/mips/sgio2audio.c
sound/oss/kahlua.c
sound/oss/soundcard.c
sound/pci/Kconfig
sound/pci/ac97/ac97_patch.c
sound/pci/ad1889.c
sound/pci/ali5451/ali5451.c
sound/pci/als300.c
sound/pci/als4000.c
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/au88x0/au8810.c
sound/pci/au88x0/au8820.c
sound/pci/au88x0/au8830.c
sound/pci/aw2/aw2-alsa.c
sound/pci/azt3328.c
sound/pci/bt87x.c
sound/pci/ca0106/ca0106_main.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/cs46xx/cs46xx.c
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/cs46xx/dsp_spos.c
sound/pci/cs46xx/dsp_spos.h
sound/pci/cs46xx/dsp_spos_scb_lib.c
sound/pci/cs5530.c
sound/pci/cs5535audio/cs5535audio.c
sound/pci/ctxfi/ctatc.c
sound/pci/ctxfi/ctatc.h
sound/pci/ctxfi/xfi.c
sound/pci/echoaudio/darla20.c
sound/pci/echoaudio/darla20_dsp.c
sound/pci/echoaudio/darla24.c
sound/pci/echoaudio/darla24_dsp.c
sound/pci/echoaudio/echo3g.c
sound/pci/echoaudio/echo3g_dsp.c
sound/pci/echoaudio/echoaudio.c
sound/pci/echoaudio/echoaudio.h
sound/pci/echoaudio/echoaudio_3g.c
sound/pci/echoaudio/echoaudio_dsp.c
sound/pci/echoaudio/gina20.c
sound/pci/echoaudio/gina20_dsp.c
sound/pci/echoaudio/gina24.c
sound/pci/echoaudio/gina24_dsp.c
sound/pci/echoaudio/indigo.c
sound/pci/echoaudio/indigo_dsp.c
sound/pci/echoaudio/indigo_express_dsp.c
sound/pci/echoaudio/indigodj.c
sound/pci/echoaudio/indigodj_dsp.c
sound/pci/echoaudio/indigodjx.c
sound/pci/echoaudio/indigodjx_dsp.c
sound/pci/echoaudio/indigoio.c
sound/pci/echoaudio/indigoio_dsp.c
sound/pci/echoaudio/indigoiox.c
sound/pci/echoaudio/indigoiox_dsp.c
sound/pci/echoaudio/layla20.c
sound/pci/echoaudio/layla20_dsp.c
sound/pci/echoaudio/layla24.c
sound/pci/echoaudio/layla24_dsp.c
sound/pci/echoaudio/mia.c
sound/pci/echoaudio/mia_dsp.c
sound/pci/echoaudio/mona.c
sound/pci/echoaudio/mona_dsp.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1x.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1724.c
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/korg1212/korg1212.c
sound/pci/lx6464es/lx6464es.c
sound/pci/maestro3.c
sound/pci/mixart/mixart.c
sound/pci/nm256/nm256.c
sound/pci/oxygen/Makefile
sound/pci/oxygen/hifier.c
sound/pci/oxygen/oxygen.c
sound/pci/oxygen/virtuoso.c
sound/pci/oxygen/wm8766.h [new file with mode: 0644]
sound/pci/oxygen/wm8776.h [new file with mode: 0644]
sound/pci/oxygen/xonar.h
sound/pci/oxygen/xonar_wm87x6.c [new file with mode: 0644]
sound/pci/pcxhr/pcxhr.c
sound/pci/riptide/riptide.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/pci/rme9652/rme9652.c
sound/pci/sis7019.c
sound/pci/sonicvibes.c
sound/pci/trident/trident.c
sound/pci/via82xx.c
sound/pci/via82xx_modem.c
sound/pci/vx222/vx222.c
sound/pci/ymfpci/ymfpci.c
sound/pcmcia/pdaudiocf/pdaudiocf_pcm.c
sound/usb/Kconfig
sound/usb/Makefile
sound/usb/ua101.c [new file with mode: 0644]
sound/usb/usbaudio.c
sound/usb/usbaudio.h
sound/usb/usbmidi.c
sound/usb/usbmixer.c
sound/usb/usbmixer_maps.c
sound/usb/usbquirks.h

index 8923597..33df82e 100644 (file)
@@ -482,6 +482,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     reference_rate     - reference sample rate, 44100 or 48000 (default)
     multiple           - multiple to ref. sample rate, 1 or 2 (default)
+    subsystem          - override the PCI SSID for probing; the value
+                         consists of SSVID << 16 | SSDID.  The default is
+                         zero, which means no override.
 
     This module supports multiple cards.
 
@@ -1123,6 +1126,21 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     This module supports multiple cards, autoprobe and ISA PnP.
 
+  Module snd-jazz16
+  -------------------
+
+    Module for Media Vision Jazz16 chipset. The chipset consists of 3 chips:
+    MVD1216 + MVA416 + MVA514.
+
+    port       - port # for SB DSP chip (0x210,0x220,0x230,0x240,0x250,0x260)
+    irq                - IRQ # for SB DSP chip (3,5,7,9,10,15)
+    dma8       - DMA # for SB DSP chip (1,3)
+    dma16      - DMA # for SB DSP chip (5,7)
+    mpu_port   - MPU-401 port # (0x300,0x310,0x320,0x330)
+    mpu_irq    - MPU-401 irq # (2,3,5,7)
+
+    This module supports multiple cards.
+
   Module snd-korg1212
   -------------------
 
@@ -1791,6 +1809,13 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     The power-management is supported.
 
+  Module snd-ua101
+  ----------------
+
+    Module for the Edirol UA-101 audio/MIDI interface.
+
+    This module supports multiple devices, autoprobe and hotplugging.
+
   Module snd-usb-audio
   --------------------
 
@@ -1923,7 +1948,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
   -------------------
 
     Module for sound cards based on the Asus AV100/AV200 chips,
-    i.e., Xonar D1, DX, D2, D2X, HDAV1.3 (Deluxe), Essence ST
+    i.e., Xonar D1, DX, D2, D2X, DS, HDAV1.3 (Deluxe), Essence ST
     (Deluxe) and Essence STX.
 
     This module supports autoprobe and multiple cards.
index a61499c..89e0ac1 100644 (file)
@@ -458,5 +458,8 @@ struct snd_pci_quirk {
 const struct snd_pci_quirk *
 snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list);
 
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup_id(u16 vendor, u16 device,
+                       const struct snd_pci_quirk *list);
 
 #endif /* __SOUND_CORE_H */
index 7c44667..49b03c9 100644 (file)
@@ -118,9 +118,11 @@ struct dsp_scb_descriptor {
 
        struct snd_info_entry *proc_info;
        int ref_count;
-       spinlock_t lock;
 
-       int deleted;
+       u16 volume[2];
+       unsigned int deleted :1;
+       unsigned int updated :1;
+       unsigned int volume_set :1;
 };
 
 struct dsp_task_descriptor {
index c83a4a7..8b611a5 100644 (file)
@@ -262,6 +262,8 @@ struct snd_pcm_hw_constraint_list {
        unsigned int mask;
 };
 
+struct snd_pcm_hwptr_log;
+
 struct snd_pcm_runtime {
        /* -- Status -- */
        struct snd_pcm_substream *trigger_master;
@@ -310,7 +312,9 @@ struct snd_pcm_runtime {
        struct snd_pcm_mmap_control *control;
 
        /* -- locking / scheduling -- */
-       wait_queue_head_t sleep;
+       unsigned int twake: 1;          /* do transfer (!poll) wakeup */
+       wait_queue_head_t sleep;        /* poll sleep */
+       wait_queue_head_t tsleep;       /* transfer sleep */
        struct fasync_struct *fasync;
 
        /* -- private section -- */
@@ -340,6 +344,10 @@ struct snd_pcm_runtime {
        /* -- OSS things -- */
        struct snd_pcm_oss_runtime oss;
 #endif
+
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+       struct snd_pcm_hwptr_log *hwptr_log;
+#endif
 };
 
 struct snd_pcm_group {         /* keep linked substreams */
@@ -834,6 +842,8 @@ void snd_pcm_set_sync(struct snd_pcm_substream *substream);
 int snd_pcm_lib_interleave_len(struct snd_pcm_substream *substream);
 int snd_pcm_lib_ioctl(struct snd_pcm_substream *substream,
                      unsigned int cmd, void *arg);                      
+int snd_pcm_update_state(struct snd_pcm_substream *substream,
+                        struct snd_pcm_runtime *runtime);
 int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream);
 int snd_pcm_playback_xrun_check(struct snd_pcm_substream *substream);
 int snd_pcm_capture_xrun_check(struct snd_pcm_substream *substream);
@@ -905,6 +915,44 @@ int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
 int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
 int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream);
 
+int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
+                                     size_t size, gfp_t gfp_flags);
+int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream);
+struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
+                                         unsigned long offset);
+#if 0 /* for kernel-doc */
+/**
+ * snd_pcm_lib_alloc_vmalloc_buffer - allocate virtual DMA buffer
+ * @substream: the substream to allocate the buffer to
+ * @size: the requested buffer size, in bytes
+ *
+ * Allocates the PCM substream buffer using vmalloc(), i.e., the memory is
+ * contiguous in kernel virtual space, but not in physical memory.  Use this
+ * if the buffer is accessed by kernel code but not by device DMA.
+ *
+ * Returns 1 if the buffer was changed, 0 if not changed, or a negative error
+ * code.
+ */
+static int snd_pcm_lib_alloc_vmalloc_buffer
+                       (struct snd_pcm_substream *substream, size_t size);
+/**
+ * snd_pcm_lib_alloc_vmalloc_32_buffer - allocate 32-bit-addressable buffer
+ * @substream: the substream to allocate the buffer to
+ * @size: the requested buffer size, in bytes
+ *
+ * This function works like snd_pcm_lib_alloc_vmalloc_buffer(), but uses
+ * vmalloc_32(), i.e., the pages are allocated from 32-bit-addressable memory.
+ */
+static int snd_pcm_lib_alloc_vmalloc_32_buffer
+                       (struct snd_pcm_substream *substream, size_t size);
+#endif
+#define snd_pcm_lib_alloc_vmalloc_buffer(subs, size) \
+       _snd_pcm_lib_alloc_vmalloc_buffer \
+                       (subs, size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO)
+#define snd_pcm_lib_alloc_vmalloc_32_buffer(subs, size) \
+       _snd_pcm_lib_alloc_vmalloc_buffer \
+                       (subs, size, GFP_KERNEL | GFP_DMA32 | __GFP_ZERO)
+
 #ifdef CONFIG_SND_DMA_SGBUF
 /*
  * SG-buffer handling
@@ -975,6 +1023,10 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream, struct vm_area_s
 #define snd_pcm_lib_mmap_iomem NULL
 #endif
 
+int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream,
+                              struct vm_area_struct *area);
+#define snd_pcm_lib_mmap_vmalloc       snd_pcm_lib_mmap_noncached
+
 static inline void snd_pcm_limit_isa_dma_size(int dma, size_t *max)
 {
        *max = dma < 4 ? 64 * 1024 : 128 * 1024;
index cc4e226..760c969 100644 (file)
@@ -61,7 +61,7 @@ struct snd_pcm_oss_runtime {
        struct snd_pcm_plugin *plugin_first;
        struct snd_pcm_plugin *plugin_last;
 #endif
-       unsigned int prev_hw_ptr_interrupt;
+       unsigned int prev_hw_ptr_period;
 };
 
 struct snd_pcm_oss_file {
index 4e62ee1..9535354 100644 (file)
@@ -33,6 +33,7 @@ enum sb_hw_type {
        SB_HW_20,
        SB_HW_201,
        SB_HW_PRO,
+       SB_HW_JAZZ16,           /* Media Vision Jazz16 */
        SB_HW_16,
        SB_HW_16CSP,            /* SB16 with CSP chip */
        SB_HW_ALS100,           /* Avance Logic ALS100 chip */
index 2293914..7fed234 100644 (file)
@@ -1,3 +1,3 @@
 /* include/version.h */
-#define CONFIG_SND_VERSION "1.0.21"
+#define CONFIG_SND_VERSION "1.0.22.1"
 #define CONFIG_SND_DATE ""
index 268ab74..439ce64 100644 (file)
@@ -237,8 +237,9 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
        access = ncontrol->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
                 (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
                                      SNDRV_CTL_ELEM_ACCESS_INACTIVE|
-                                     SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
-                                     SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
+                                     SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
+                                     SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND|
+                                     SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
        kctl.info = ncontrol->info;
        kctl.get = ncontrol->get;
        kctl.put = ncontrol->put;
@@ -1099,7 +1100,7 @@ static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
 
        if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
                return -EFAULT;
-       if (tlv.length < sizeof(unsigned int) * 3)
+       if (tlv.length < sizeof(unsigned int) * 2)
                return -EINVAL;
        down_read(&card->controls_rwsem);
        kctl = snd_ctl_find_numid(card, tlv.numid);
index 23a032c..3da4f92 100644 (file)
@@ -101,8 +101,9 @@ EXPORT_SYMBOL_GPL(__snd_printk);
 #ifdef CONFIG_PCI
 #include <linux/pci.h>
 /**
- * snd_pci_quirk_lookup - look up a PCI SSID quirk list
- * @pci: pci_dev handle
+ * snd_pci_quirk_lookup_id - look up a PCI SSID quirk list
+ * @vendor: PCI SSV id
+ * @device: PCI SSD id
  * @list: quirk list, terminated by a null entry
  *
  * Look through the given quirk list and finds a matching entry
@@ -112,18 +113,39 @@ EXPORT_SYMBOL_GPL(__snd_printk);
  * Returns the matched entry pointer, or NULL if nothing matched.
  */
 const struct snd_pci_quirk *
-snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
+snd_pci_quirk_lookup_id(u16 vendor, u16 device,
+                       const struct snd_pci_quirk *list)
 {
        const struct snd_pci_quirk *q;
 
        for (q = list; q->subvendor; q++) {
-               if (q->subvendor != pci->subsystem_vendor)
+               if (q->subvendor != vendor)
                        continue;
                if (!q->subdevice ||
-                   (pci->subsystem_device & q->subdevice_mask) == q->subdevice)
+                   (device & q->subdevice_mask) == q->subdevice)
                        return q;
        }
        return NULL;
 }
+EXPORT_SYMBOL(snd_pci_quirk_lookup_id);
+
+/**
+ * snd_pci_quirk_lookup - look up a PCI SSID quirk list
+ * @pci: pci_dev handle
+ * @list: quirk list, terminated by a null entry
+ *
+ * Look through the given quirk list and finds a matching entry
+ * with the same PCI SSID.  When subdevice is 0, all subdevice
+ * values may match.
+ *
+ * Returns the matched entry pointer, or NULL if nothing matched.
+ */
+const struct snd_pci_quirk *
+snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list)
+{
+       return snd_pci_quirk_lookup_id(pci->subsystem_vendor,
+                                      pci->subsystem_device,
+                                      list);
+}
 EXPORT_SYMBOL(snd_pci_quirk_lookup);
 #endif
index d9c9635..82d4e33 100644 (file)
@@ -632,6 +632,12 @@ static long snd_pcm_alsa_frames(struct snd_pcm_substream *substream, long bytes)
        return bytes_to_frames(runtime, (buffer_size * bytes) / runtime->oss.buffer_bytes);
 }
 
+static inline
+snd_pcm_uframes_t get_hw_ptr_period(struct snd_pcm_runtime *runtime)
+{
+       return runtime->hw_ptr_interrupt;
+}
+
 /* define extended formats in the recent OSS versions (if any) */
 /* linear formats */
 #define AFMT_S32_LE      0x00001000
@@ -1102,7 +1108,7 @@ static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
                return err;
        }
        runtime->oss.prepare = 0;
-       runtime->oss.prev_hw_ptr_interrupt = 0;
+       runtime->oss.prev_hw_ptr_period = 0;
        runtime->oss.period_ptr = 0;
        runtime->oss.buffer_used = 0;
 
@@ -1950,7 +1956,8 @@ static int snd_pcm_oss_get_caps(struct snd_pcm_oss_file *pcm_oss_file)
        return result;
 }
 
-static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream, snd_pcm_uframes_t hw_ptr)
+static void snd_pcm_oss_simulate_fill(struct snd_pcm_substream *substream,
+                                     snd_pcm_uframes_t hw_ptr)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_uframes_t appl_ptr;
@@ -1986,7 +1993,8 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
                        if (runtime->oss.trigger)
                                goto _skip1;
                        if (atomic_read(&psubstream->mmap_count))
-                               snd_pcm_oss_simulate_fill(psubstream, runtime->hw_ptr_interrupt);
+                               snd_pcm_oss_simulate_fill(psubstream,
+                                               get_hw_ptr_period(runtime));
                        runtime->oss.trigger = 1;
                        runtime->start_threshold = 1;
                        cmd = SNDRV_PCM_IOCTL_START;
@@ -2105,11 +2113,12 @@ static int snd_pcm_oss_get_ptr(struct snd_pcm_oss_file *pcm_oss_file, int stream
        info.ptr = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr % runtime->buffer_size);
        if (atomic_read(&substream->mmap_count)) {
                snd_pcm_sframes_t n;
-               n = (delay = runtime->hw_ptr_interrupt) - runtime->oss.prev_hw_ptr_interrupt;
+               delay = get_hw_ptr_period(runtime);
+               n = delay - runtime->oss.prev_hw_ptr_period;
                if (n < 0)
                        n += runtime->boundary;
                info.blocks = n / runtime->period_size;
-               runtime->oss.prev_hw_ptr_interrupt = delay;
+               runtime->oss.prev_hw_ptr_period = delay;
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        snd_pcm_oss_simulate_fill(substream, delay);
                info.bytes = snd_pcm_oss_bytes(substream, runtime->status->hw_ptr) & INT_MAX;
@@ -2673,18 +2682,22 @@ static int snd_pcm_oss_playback_ready(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        if (atomic_read(&substream->mmap_count))
-               return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+               return runtime->oss.prev_hw_ptr_period !=
+                                               get_hw_ptr_period(runtime);
        else
-               return snd_pcm_playback_avail(runtime) >= runtime->oss.period_frames;
+               return snd_pcm_playback_avail(runtime) >=
+                                               runtime->oss.period_frames;
 }
 
 static int snd_pcm_oss_capture_ready(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        if (atomic_read(&substream->mmap_count))
-               return runtime->oss.prev_hw_ptr_interrupt != runtime->hw_ptr_interrupt;
+               return runtime->oss.prev_hw_ptr_period !=
+                                               get_hw_ptr_period(runtime);
        else
-               return snd_pcm_capture_avail(runtime) >= runtime->oss.period_frames;
+               return snd_pcm_capture_avail(runtime) >=
+                                               runtime->oss.period_frames;
 }
 
 static unsigned int snd_pcm_oss_poll(struct file *file, poll_table * wait)
index 6884ae0..0d428d0 100644 (file)
@@ -894,6 +894,7 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
        memset((void*)runtime->control, 0, size);
 
        init_waitqueue_head(&runtime->sleep);
+       init_waitqueue_head(&runtime->tsleep);
 
        runtime->status->state = SNDRV_PCM_STATE_OPEN;
 
@@ -921,6 +922,10 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
        snd_free_pages((void*)runtime->control,
                       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
        kfree(runtime->hw_constraints.rules);
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+       if (runtime->hwptr_log)
+               kfree(runtime->hwptr_log);
+#endif
        kfree(runtime);
        substream->runtime = NULL;
        put_pid(substream->pid);
index a27545b..b546ac2 100644 (file)
@@ -126,17 +126,6 @@ void snd_pcm_playback_silence(struct snd_pcm_substream *substream, snd_pcm_ufram
        }
 }
 
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-#define xrun_debug(substream, mask)    ((substream)->pstr->xrun_debug & (mask))
-#else
-#define xrun_debug(substream, mask)    0
-#endif
-
-#define dump_stack_on_xrun(substream) do {             \
-               if (xrun_debug(substream, 2))           \
-                       dump_stack();                   \
-       } while (0)
-
 static void pcm_debug_name(struct snd_pcm_substream *substream,
                           char *name, size_t len)
 {
@@ -147,6 +136,24 @@ static void pcm_debug_name(struct snd_pcm_substream *substream,
                 substream->number);
 }
 
+#define XRUN_DEBUG_BASIC       (1<<0)
+#define XRUN_DEBUG_STACK       (1<<1)  /* dump also stack */
+#define XRUN_DEBUG_JIFFIESCHECK        (1<<2)  /* do jiffies check */
+#define XRUN_DEBUG_PERIODUPDATE        (1<<3)  /* full period update info */
+#define XRUN_DEBUG_HWPTRUPDATE (1<<4)  /* full hwptr update info */
+#define XRUN_DEBUG_LOG         (1<<5)  /* show last 10 positions on err */
+#define XRUN_DEBUG_LOGONCE     (1<<6)  /* do above only once */
+
+#ifdef CONFIG_SND_PCM_XRUN_DEBUG
+
+#define xrun_debug(substream, mask) \
+                       ((substream)->pstr->xrun_debug & (mask))
+
+#define dump_stack_on_xrun(substream) do {                     \
+               if (xrun_debug(substream, XRUN_DEBUG_STACK))    \
+                       dump_stack();                           \
+       } while (0)
+
 static void xrun(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
@@ -154,7 +161,7 @@ static void xrun(struct snd_pcm_substream *substream)
        if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
                snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
        snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-       if (xrun_debug(substream, 1)) {
+       if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {
                char name[16];
                pcm_debug_name(substream, name, sizeof(name));
                snd_printd(KERN_DEBUG "XRUN: %s\n", name);
@@ -162,32 +169,102 @@ static void xrun(struct snd_pcm_substream *substream)
        }
 }
 
-static snd_pcm_uframes_t
-snd_pcm_update_hw_ptr_pos(struct snd_pcm_substream *substream,
-                         struct snd_pcm_runtime *runtime)
-{
+#define hw_ptr_error(substream, fmt, args...)                          \
+       do {                                                            \
+               if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {          \
+                       xrun_log_show(substream);                       \
+                       if (printk_ratelimit()) {                       \
+                               snd_printd("PCM: " fmt, ##args);        \
+                       }                                               \
+                       dump_stack_on_xrun(substream);                  \
+               }                                                       \
+       } while (0)
+
+#define XRUN_LOG_CNT   10
+
+struct hwptr_log_entry {
+       unsigned long jiffies;
        snd_pcm_uframes_t pos;
+       snd_pcm_uframes_t period_size;
+       snd_pcm_uframes_t buffer_size;
+       snd_pcm_uframes_t old_hw_ptr;
+       snd_pcm_uframes_t hw_ptr_base;
+};
 
-       pos = substream->ops->pointer(substream);
-       if (pos == SNDRV_PCM_POS_XRUN)
-               return pos; /* XRUN */
-       if (pos >= runtime->buffer_size) {
-               if (printk_ratelimit()) {
-                       char name[16];
-                       pcm_debug_name(substream, name, sizeof(name));
-                       snd_printd(KERN_ERR  "BUG: %s, pos = 0x%lx, "
-                                  "buffer size = 0x%lx, period size = 0x%lx\n",
-                                  name, pos, runtime->buffer_size,
-                                  runtime->period_size);
-               }
-               pos = 0;
+struct snd_pcm_hwptr_log {
+       unsigned int idx;
+       unsigned int hit: 1;
+       struct hwptr_log_entry entries[XRUN_LOG_CNT];
+};
+
+static void xrun_log(struct snd_pcm_substream *substream,
+                    snd_pcm_uframes_t pos)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_pcm_hwptr_log *log = runtime->hwptr_log;
+       struct hwptr_log_entry *entry;
+
+       if (log == NULL) {
+               log = kzalloc(sizeof(*log), GFP_ATOMIC);
+               if (log == NULL)
+                       return;
+               runtime->hwptr_log = log;
+       } else {
+               if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
+                       return;
        }
-       pos -= pos % runtime->min_align;
-       return pos;
+       entry = &log->entries[log->idx];
+       entry->jiffies = jiffies;
+       entry->pos = pos;
+       entry->period_size = runtime->period_size;
+       entry->buffer_size = runtime->buffer_size;;
+       entry->old_hw_ptr = runtime->status->hw_ptr;
+       entry->hw_ptr_base = runtime->hw_ptr_base;
+       log->idx = (log->idx + 1) % XRUN_LOG_CNT;
+}
+
+static void xrun_log_show(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_hwptr_log *log = substream->runtime->hwptr_log;
+       struct hwptr_log_entry *entry;
+       char name[16];
+       unsigned int idx;
+       int cnt;
+
+       if (log == NULL)
+               return;
+       if (xrun_debug(substream, XRUN_DEBUG_LOGONCE) && log->hit)
+               return;
+       pcm_debug_name(substream, name, sizeof(name));
+       for (cnt = 0, idx = log->idx; cnt < XRUN_LOG_CNT; cnt++) {
+               entry = &log->entries[idx];
+               if (entry->period_size == 0)
+                       break;
+               snd_printd("hwptr log: %s: j=%lu, pos=%ld/%ld/%ld, "
+                          "hwptr=%ld/%ld\n",
+                          name, entry->jiffies, (unsigned long)entry->pos,
+                          (unsigned long)entry->period_size,
+                          (unsigned long)entry->buffer_size,
+                          (unsigned long)entry->old_hw_ptr,
+                          (unsigned long)entry->hw_ptr_base);
+               idx++;
+               idx %= XRUN_LOG_CNT;
+       }
+       log->hit = 1;
 }
 
-static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
-                                     struct snd_pcm_runtime *runtime)
+#else /* ! CONFIG_SND_PCM_XRUN_DEBUG */
+
+#define xrun_debug(substream, mask)    0
+#define xrun(substream)                        do { } while (0)
+#define hw_ptr_error(substream, fmt, args...) do { } while (0)
+#define xrun_log(substream, pos)       do { } while (0)
+#define xrun_log_show(substream)       do { } while (0)
+
+#endif
+
+int snd_pcm_update_state(struct snd_pcm_substream *substream,
+                        struct snd_pcm_runtime *runtime)
 {
        snd_pcm_uframes_t avail;
 
@@ -209,88 +286,94 @@ static int snd_pcm_update_hw_ptr_post(struct snd_pcm_substream *substream,
                }
        }
        if (avail >= runtime->control->avail_min)
-               wake_up(&runtime->sleep);
+               wake_up(runtime->twake ? &runtime->tsleep : &runtime->sleep);
        return 0;
 }
 
-#define hw_ptr_error(substream, fmt, args...)                          \
-       do {                                                            \
-               if (xrun_debug(substream, 1)) {                         \
-                       if (printk_ratelimit()) {                       \
-                               snd_printd("PCM: " fmt, ##args);        \
-                       }                                               \
-                       dump_stack_on_xrun(substream);                  \
-               }                                                       \
-       } while (0)
-
-static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
+static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
+                                 unsigned int in_interrupt)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_uframes_t pos;
-       snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_ptr_interrupt, hw_base;
+       snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
        snd_pcm_sframes_t hdelta, delta;
        unsigned long jdelta;
 
        old_hw_ptr = runtime->status->hw_ptr;
-       pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
+       pos = substream->ops->pointer(substream);
        if (pos == SNDRV_PCM_POS_XRUN) {
                xrun(substream);
                return -EPIPE;
        }
-       if (xrun_debug(substream, 8)) {
-               char name[16];
-               pcm_debug_name(substream, name, sizeof(name));
-               snd_printd("period_update: %s: pos=0x%x/0x%x/0x%x, "
-                          "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n",
-                          name, (unsigned int)pos,
-                          (unsigned int)runtime->period_size,
-                          (unsigned int)runtime->buffer_size,
-                          (unsigned long)old_hw_ptr,
-                          (unsigned long)runtime->hw_ptr_base,
-                          (unsigned long)runtime->hw_ptr_interrupt);
+       if (pos >= runtime->buffer_size) {
+               if (printk_ratelimit()) {
+                       char name[16];
+                       pcm_debug_name(substream, name, sizeof(name));
+                       xrun_log_show(substream);
+                       snd_printd(KERN_ERR  "BUG: %s, pos = %ld, "
+                                  "buffer size = %ld, period size = %ld\n",
+                                  name, pos, runtime->buffer_size,
+                                  runtime->period_size);
+               }
+               pos = 0;
        }
+       pos -= pos % runtime->min_align;
+       if (xrun_debug(substream, XRUN_DEBUG_LOG))
+               xrun_log(substream, pos);
        hw_base = runtime->hw_ptr_base;
        new_hw_ptr = hw_base + pos;
-       hw_ptr_interrupt = runtime->hw_ptr_interrupt + runtime->period_size;
-       delta = new_hw_ptr - hw_ptr_interrupt;
-       if (hw_ptr_interrupt >= runtime->boundary) {
-               hw_ptr_interrupt -= runtime->boundary;
-               if (hw_base < runtime->boundary / 2)
-                       /* hw_base was already lapped; recalc delta */
-                       delta = new_hw_ptr - hw_ptr_interrupt;
-       }
-       if (delta < 0) {
-               if (runtime->periods == 1 || new_hw_ptr < old_hw_ptr)
-                       delta += runtime->buffer_size;
-               if (delta < 0) {
-                       hw_ptr_error(substream, 
-                                    "Unexpected hw_pointer value "
-                                    "(stream=%i, pos=%ld, intr_ptr=%ld)\n",
-                                    substream->stream, (long)pos,
-                                    (long)hw_ptr_interrupt);
-#if 1
-                       /* simply skipping the hwptr update seems more
-                        * robust in some cases, e.g. on VMware with
-                        * inaccurate timer source
-                        */
-                       return 0; /* skip this update */
-#else
-                       /* rebase to interrupt position */
-                       hw_base = new_hw_ptr = hw_ptr_interrupt;
-                       /* align hw_base to buffer_size */
-                       hw_base -= hw_base % runtime->buffer_size;
-                       delta = 0;
-#endif
-               } else {
+       if (in_interrupt) {
+               /* we know that one period was processed */
+               /* delta = "expected next hw_ptr" for in_interrupt != 0 */
+               delta = runtime->hw_ptr_interrupt + runtime->period_size;
+               if (delta > new_hw_ptr) {
                        hw_base += runtime->buffer_size;
                        if (hw_base >= runtime->boundary)
                                hw_base = 0;
                        new_hw_ptr = hw_base + pos;
+                       goto __delta;
                }
        }
+       /* new_hw_ptr might be lower than old_hw_ptr in case when */
+       /* pointer crosses the end of the ring buffer */
+       if (new_hw_ptr < old_hw_ptr) {
+               hw_base += runtime->buffer_size;
+               if (hw_base >= runtime->boundary)
+                       hw_base = 0;
+               new_hw_ptr = hw_base + pos;
+       }
+      __delta:
+       delta = (new_hw_ptr - old_hw_ptr) % runtime->boundary;
+       if (xrun_debug(substream, in_interrupt ?
+                       XRUN_DEBUG_PERIODUPDATE : XRUN_DEBUG_HWPTRUPDATE)) {
+               char name[16];
+               pcm_debug_name(substream, name, sizeof(name));
+               snd_printd("%s_update: %s: pos=%u/%u/%u, "
+                          "hwptr=%ld/%ld/%ld/%ld\n",
+                          in_interrupt ? "period" : "hwptr",
+                          name,
+                          (unsigned int)pos,
+                          (unsigned int)runtime->period_size,
+                          (unsigned int)runtime->buffer_size,
+                          (unsigned long)delta,
+                          (unsigned long)old_hw_ptr,
+                          (unsigned long)new_hw_ptr,
+                          (unsigned long)runtime->hw_ptr_base);
+       }
+       /* something must be really wrong */
+       if (delta >= runtime->buffer_size + runtime->period_size) {
+               hw_ptr_error(substream,
+                              "Unexpected hw_pointer value %s"
+                              "(stream=%i, pos=%ld, new_hw_ptr=%ld, "
+                              "old_hw_ptr=%ld)\n",
+                                    in_interrupt ? "[Q] " : "[P]",
+                                    substream->stream, (long)pos,
+                                    (long)new_hw_ptr, (long)old_hw_ptr);
+               return 0;
+       }
 
        /* Do jiffies check only in xrun_debug mode */
-       if (!xrun_debug(substream, 4))
+       if (!xrun_debug(substream, XRUN_DEBUG_JIFFIESCHECK))
                goto no_jiffies_check;
 
        /* Skip the jiffies check for hardwares with BATCH flag.
@@ -299,7 +382,7 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
         */
        if (runtime->hw.info & SNDRV_PCM_INFO_BATCH)
                goto no_jiffies_check;
-       hdelta = new_hw_ptr - old_hw_ptr;
+       hdelta = delta;
        if (hdelta < runtime->delay)
                goto no_jiffies_check;
        hdelta -= runtime->delay;
@@ -308,130 +391,68 @@ static int snd_pcm_update_hw_ptr_interrupt(struct snd_pcm_substream *substream)
                delta = jdelta /
                        (((runtime->period_size * HZ) / runtime->rate)
                                                                + HZ/100);
+               /* move new_hw_ptr according jiffies not pos variable */
+               new_hw_ptr = old_hw_ptr;
+               hw_base = delta;
+               /* use loop to avoid checks for delta overflows */
+               /* the delta value is small or zero in most cases */
+               while (delta > 0) {
+                       new_hw_ptr += runtime->period_size;
+                       if (new_hw_ptr >= runtime->boundary)
+                               new_hw_ptr -= runtime->boundary;
+                       delta--;
+               }
+               /* align hw_base to buffer_size */
                hw_ptr_error(substream,
-                            "hw_ptr skipping! [Q] "
+                            "hw_ptr skipping! %s"
                             "(pos=%ld, delta=%ld, period=%ld, "
-                            "jdelta=%lu/%lu/%lu)\n",
+                            "jdelta=%lu/%lu/%lu, hw_ptr=%ld/%ld)\n",
+                            in_interrupt ? "[Q] " : "",
                             (long)pos, (long)hdelta,
                             (long)runtime->period_size, jdelta,
-                            ((hdelta * HZ) / runtime->rate), delta);
-               hw_ptr_interrupt = runtime->hw_ptr_interrupt +
-                                  runtime->period_size * delta;
-               if (hw_ptr_interrupt >= runtime->boundary)
-                       hw_ptr_interrupt -= runtime->boundary;
-               /* rebase to interrupt position */
-               hw_base = new_hw_ptr = hw_ptr_interrupt;
-               /* align hw_base to buffer_size */
-               hw_base -= hw_base % runtime->buffer_size;
+                            ((hdelta * HZ) / runtime->rate), hw_base,
+                            (unsigned long)old_hw_ptr,
+                            (unsigned long)new_hw_ptr);
+               /* reset values to proper state */
                delta = 0;
+               hw_base = new_hw_ptr - (new_hw_ptr % runtime->buffer_size);
        }
  no_jiffies_check:
        if (delta > runtime->period_size + runtime->period_size / 2) {
                hw_ptr_error(substream,
-                            "Lost interrupts? "
-                            "(stream=%i, delta=%ld, intr_ptr=%ld)\n",
+                            "Lost interrupts? %s"
+                            "(stream=%i, delta=%ld, new_hw_ptr=%ld, "
+                            "old_hw_ptr=%ld)\n",
+                            in_interrupt ? "[Q] " : "",
                             substream->stream, (long)delta,
-                            (long)hw_ptr_interrupt);
-               /* rebase hw_ptr_interrupt */
-               hw_ptr_interrupt =
-                       new_hw_ptr - new_hw_ptr % runtime->period_size;
+                            (long)new_hw_ptr,
+                            (long)old_hw_ptr);
        }
-       runtime->hw_ptr_interrupt = hw_ptr_interrupt;
+
+       if (runtime->status->hw_ptr == new_hw_ptr)
+               return 0;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, new_hw_ptr);
 
-       if (runtime->status->hw_ptr == new_hw_ptr)
-               return 0;
-
+       if (in_interrupt) {
+               runtime->hw_ptr_interrupt = new_hw_ptr -
+                               (new_hw_ptr % runtime->period_size);
+       }
        runtime->hw_ptr_base = hw_base;
        runtime->status->hw_ptr = new_hw_ptr;
        runtime->hw_ptr_jiffies = jiffies;
        if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
                snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
 
-       return snd_pcm_update_hw_ptr_post(substream, runtime);
+       return snd_pcm_update_state(substream, runtime);
 }
 
 /* CAUTION: call it with irq disabled */
 int snd_pcm_update_hw_ptr(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       snd_pcm_uframes_t pos;
-       snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
-       snd_pcm_sframes_t delta;
-       unsigned long jdelta;
-
-       old_hw_ptr = runtime->status->hw_ptr;
-       pos = snd_pcm_update_hw_ptr_pos(substream, runtime);
-       if (pos == SNDRV_PCM_POS_XRUN) {
-               xrun(substream);
-               return -EPIPE;
-       }
-       if (xrun_debug(substream, 16)) {
-               char name[16];
-               pcm_debug_name(substream, name, sizeof(name));
-               snd_printd("hw_update: %s: pos=0x%x/0x%x/0x%x, "
-                          "hwptr=0x%lx, hw_base=0x%lx, hw_intr=0x%lx\n",
-                          name, (unsigned int)pos,
-                          (unsigned int)runtime->period_size,
-                          (unsigned int)runtime->buffer_size,
-                          (unsigned long)old_hw_ptr,
-                          (unsigned long)runtime->hw_ptr_base,
-                          (unsigned long)runtime->hw_ptr_interrupt);
-       }
-
-       hw_base = runtime->hw_ptr_base;
-       new_hw_ptr = hw_base + pos;
-
-       delta = new_hw_ptr - old_hw_ptr;
-       jdelta = jiffies - runtime->hw_ptr_jiffies;
-       if (delta < 0) {
-               delta += runtime->buffer_size;
-               if (delta < 0) {
-                       hw_ptr_error(substream, 
-                                    "Unexpected hw_pointer value [2] "
-                                    "(stream=%i, pos=%ld, old_ptr=%ld, jdelta=%li)\n",
-                                    substream->stream, (long)pos,
-                                    (long)old_hw_ptr, jdelta);
-                       return 0;
-               }
-               hw_base += runtime->buffer_size;
-               if (hw_base >= runtime->boundary)
-                       hw_base = 0;
-               new_hw_ptr = hw_base + pos;
-       }
-       /* Do jiffies check only in xrun_debug mode */
-       if (!xrun_debug(substream, 4))
-               goto no_jiffies_check;
-       if (delta < runtime->delay)
-               goto no_jiffies_check;
-       delta -= runtime->delay;
-       if (((delta * HZ) / runtime->rate) > jdelta + HZ/100) {
-               hw_ptr_error(substream,
-                            "hw_ptr skipping! "
-                            "(pos=%ld, delta=%ld, period=%ld, jdelta=%lu/%lu)\n",
-                            (long)pos, (long)delta,
-                            (long)runtime->period_size, jdelta,
-                            ((delta * HZ) / runtime->rate));
-               return 0;
-       }
- no_jiffies_check:
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
-           runtime->silence_size > 0)
-               snd_pcm_playback_silence(substream, new_hw_ptr);
-
-       if (runtime->status->hw_ptr == new_hw_ptr)
-               return 0;
-
-       runtime->hw_ptr_base = hw_base;
-       runtime->status->hw_ptr = new_hw_ptr;
-       runtime->hw_ptr_jiffies = jiffies;
-       if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
-               snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp);
-
-       return snd_pcm_update_hw_ptr_post(substream, runtime);
+       return snd_pcm_update_hw_ptr0(substream, 0);
 }
 
 /**
@@ -745,10 +766,13 @@ int snd_interval_ratnum(struct snd_interval *i,
                        unsigned int rats_count, struct snd_ratnum *rats,
                        unsigned int *nump, unsigned int *denp)
 {
-       unsigned int best_num, best_diff, best_den;
+       unsigned int best_num, best_den;
+       int best_diff;
        unsigned int k;
        struct snd_interval t;
        int err;
+       unsigned int result_num, result_den;
+       int result_diff;
 
        best_num = best_den = best_diff = 0;
        for (k = 0; k < rats_count; ++k) {
@@ -770,6 +794,8 @@ int snd_interval_ratnum(struct snd_interval *i,
                                den -= r;
                }
                diff = num - q * den;
+               if (diff < 0)
+                       diff = -diff;
                if (best_num == 0 ||
                    diff * best_den < best_diff * den) {
                        best_diff = diff;
@@ -784,6 +810,9 @@ int snd_interval_ratnum(struct snd_interval *i,
        t.min = div_down(best_num, best_den);
        t.openmin = !!(best_num % best_den);
        
+       result_num = best_num;
+       result_diff = best_diff;
+       result_den = best_den;
        best_num = best_den = best_diff = 0;
        for (k = 0; k < rats_count; ++k) {
                unsigned int num = rats[k].num;
@@ -806,6 +835,8 @@ int snd_interval_ratnum(struct snd_interval *i,
                                den += rats[k].den_step - r;
                }
                diff = q * den - num;
+               if (diff < 0)
+                       diff = -diff;
                if (best_num == 0 ||
                    diff * best_den < best_diff * den) {
                        best_diff = diff;
@@ -825,10 +856,14 @@ int snd_interval_ratnum(struct snd_interval *i,
                return err;
 
        if (snd_interval_single(i)) {
+               if (best_diff * result_den < result_diff * best_den) {
+                       result_num = best_num;
+                       result_den = best_den;
+               }
                if (nump)
-                       *nump = best_num;
+                       *nump = result_num;
                if (denp)
-                       *denp = best_den;
+                       *denp = result_den;
        }
        return err;
 }
@@ -1643,7 +1678,7 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
 
        snd_pcm_stream_lock_irqsave(substream, flags);
        if (!snd_pcm_running(substream) ||
-           snd_pcm_update_hw_ptr_interrupt(substream) < 0)
+           snd_pcm_update_hw_ptr0(substream, 1) < 0)
                goto _end;
 
        if (substream->timer_running)
@@ -1674,7 +1709,7 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream,
        long tout;
 
        init_waitqueue_entry(&wait, current);
-       add_wait_queue(&runtime->sleep, &wait);
+       add_wait_queue(&runtime->tsleep, &wait);
        for (;;) {
                if (signal_pending(current)) {
                        err = -ERESTARTSYS;
@@ -1717,7 +1752,7 @@ static int wait_for_avail_min(struct snd_pcm_substream *substream,
                        break;
        }
  _endloop:
-       remove_wait_queue(&runtime->sleep, &wait);
+       remove_wait_queue(&runtime->tsleep, &wait);
        *availp = avail;
        return err;
 }
@@ -1776,6 +1811,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
                goto _end_unlock;
        }
 
+       runtime->twake = 1;
        while (size > 0) {
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
                snd_pcm_uframes_t avail;
@@ -1797,15 +1833,17 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
                if (frames > cont)
                        frames = cont;
                if (snd_BUG_ON(!frames)) {
+                       runtime->twake = 0;
                        snd_pcm_stream_unlock_irq(substream);
                        return -EINVAL;
                }
                appl_ptr = runtime->control->appl_ptr;
                appl_ofs = appl_ptr % runtime->buffer_size;
                snd_pcm_stream_unlock_irq(substream);
-               if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
-                       goto _end;
+               err = transfer(substream, appl_ofs, data, offset, frames);
                snd_pcm_stream_lock_irq(substream);
+               if (err < 0)
+                       goto _end_unlock;
                switch (runtime->status->state) {
                case SNDRV_PCM_STATE_XRUN:
                        err = -EPIPE;
@@ -1834,8 +1872,10 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
                }
        }
  _end_unlock:
+       runtime->twake = 0;
+       if (xfer > 0 && err >= 0)
+               snd_pcm_update_state(substream, runtime);
        snd_pcm_stream_unlock_irq(substream);
- _end:
        return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
 }
 
@@ -1993,6 +2033,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
                goto _end_unlock;
        }
 
+       runtime->twake = 1;
        while (size > 0) {
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
                snd_pcm_uframes_t avail;
@@ -2021,15 +2062,17 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
                if (frames > cont)
                        frames = cont;
                if (snd_BUG_ON(!frames)) {
+                       runtime->twake = 0;
                        snd_pcm_stream_unlock_irq(substream);
                        return -EINVAL;
                }
                appl_ptr = runtime->control->appl_ptr;
                appl_ofs = appl_ptr % runtime->buffer_size;
                snd_pcm_stream_unlock_irq(substream);
-               if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)
-                       goto _end;
+               err = transfer(substream, appl_ofs, data, offset, frames);
                snd_pcm_stream_lock_irq(substream);
+               if (err < 0)
+                       goto _end_unlock;
                switch (runtime->status->state) {
                case SNDRV_PCM_STATE_XRUN:
                        err = -EPIPE;
@@ -2052,8 +2095,10 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
                xfer += frames;
        }
  _end_unlock:
+       runtime->twake = 0;
+       if (xfer > 0 && err >= 0)
+               snd_pcm_update_state(substream, runtime);
        snd_pcm_stream_unlock_irq(substream);
- _end:
        return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;
 }
 
index caa7796..d6d49d6 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/time.h>
 #include <linux/init.h>
 #include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/info.h>
@@ -434,3 +435,57 @@ int snd_pcm_lib_free_pages(struct snd_pcm_substream *substream)
 }
 
 EXPORT_SYMBOL(snd_pcm_lib_free_pages);
+
+int _snd_pcm_lib_alloc_vmalloc_buffer(struct snd_pcm_substream *substream,
+                                     size_t size, gfp_t gfp_flags)
+{
+       struct snd_pcm_runtime *runtime;
+
+       if (PCM_RUNTIME_CHECK(substream))
+               return -EINVAL;
+       runtime = substream->runtime;
+       if (runtime->dma_area) {
+               if (runtime->dma_bytes >= size)
+                       return 0; /* already large enough */
+               vfree(runtime->dma_area);
+       }
+       runtime->dma_area = __vmalloc(size, gfp_flags, PAGE_KERNEL);
+       if (!runtime->dma_area)
+               return -ENOMEM;
+       runtime->dma_bytes = size;
+       return 1;
+}
+EXPORT_SYMBOL(_snd_pcm_lib_alloc_vmalloc_buffer);
+
+/**
+ * snd_pcm_lib_free_vmalloc_buffer - free vmalloc buffer
+ * @substream: the substream with a buffer allocated by
+ *     snd_pcm_lib_alloc_vmalloc_buffer()
+ */
+int snd_pcm_lib_free_vmalloc_buffer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime;
+
+       if (PCM_RUNTIME_CHECK(substream))
+               return -EINVAL;
+       runtime = substream->runtime;
+       vfree(runtime->dma_area);
+       runtime->dma_area = NULL;
+       return 0;
+}
+EXPORT_SYMBOL(snd_pcm_lib_free_vmalloc_buffer);
+
+/**
+ * snd_pcm_lib_get_vmalloc_page - map vmalloc buffer offset to page struct
+ * @substream: the substream with a buffer allocated by
+ *     snd_pcm_lib_alloc_vmalloc_buffer()
+ * @offset: offset in the buffer
+ *
+ * This function is to be used as the page callback in the PCM ops.
+ */
+struct page *snd_pcm_lib_get_vmalloc_page(struct snd_pcm_substream *substream,
+                                         unsigned long offset)
+{
+       return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+EXPORT_SYMBOL(snd_pcm_lib_get_vmalloc_page);
index f7e1c9f..8728876 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/pm_qos_params.h>
 #include <linux/uio.h>
 #include <linux/dma-mapping.h>
+#include <linux/math64.h>
 #include <sound/core.h>
 #include <sound/control.h>
 #include <sound/info.h>
@@ -366,6 +367,38 @@ static int period_to_usecs(struct snd_pcm_runtime *runtime)
        return usecs;
 }
 
+static int calc_boundary(struct snd_pcm_runtime *runtime)
+{
+       u_int64_t boundary;
+
+       boundary = (u_int64_t)runtime->buffer_size *
+                  (u_int64_t)runtime->period_size;
+#if BITS_PER_LONG < 64
+       /* try to find lowest common multiple for buffer and period */
+       if (boundary > LONG_MAX - runtime->buffer_size) {
+               u_int32_t remainder = -1;
+               u_int32_t divident = runtime->buffer_size;
+               u_int32_t divisor = runtime->period_size;
+               while (remainder) {
+                       remainder = divident % divisor;
+                       if (remainder) {
+                               divident = divisor;
+                               divisor = remainder;
+                       }
+               }
+               boundary = div_u64(boundary, divisor);
+               if (boundary > LONG_MAX - runtime->buffer_size)
+                       return -ERANGE;
+       }
+#endif
+       if (boundary == 0)
+               return -ERANGE;
+       runtime->boundary = boundary;
+       while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
+               runtime->boundary *= 2;
+       return 0;
+}
+
 static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params)
 {
@@ -441,9 +474,9 @@ static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
        runtime->stop_threshold = runtime->buffer_size;
        runtime->silence_threshold = 0;
        runtime->silence_size = 0;
-       runtime->boundary = runtime->buffer_size;
-       while (runtime->boundary * 2 <= LONG_MAX - runtime->buffer_size)
-               runtime->boundary *= 2;
+       err = calc_boundary(runtime);
+       if (err < 0)
+               goto _error;
 
        snd_pcm_timer_resolution_change(substream);
        runtime->status->state = SNDRV_PCM_STATE_SETUP;
@@ -516,6 +549,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_sw_params *params)
 {
        struct snd_pcm_runtime *runtime;
+       int err;
 
        if (PCM_RUNTIME_CHECK(substream))
                return -ENXIO;
@@ -540,6 +574,7 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
                if (params->silence_threshold > runtime->buffer_size)
                        return -EINVAL;
        }
+       err = 0;
        snd_pcm_stream_lock_irq(substream);
        runtime->tstamp_mode = params->tstamp_mode;
        runtime->period_step = params->period_step;
@@ -553,10 +588,10 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
                    runtime->silence_size > 0)
                        snd_pcm_playback_silence(substream, ULONG_MAX);
-               wake_up(&runtime->sleep);
+               err = snd_pcm_update_state(substream, runtime);
        }
        snd_pcm_stream_unlock_irq(substream);
-       return 0;
+       return err;
 }
 
 static int snd_pcm_sw_params_user(struct snd_pcm_substream *substream,
@@ -917,6 +952,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
                runtime->status->state = state;
        }
        wake_up(&runtime->sleep);
+       wake_up(&runtime->tsleep);
 }
 
 static struct action_ops snd_pcm_action_stop = {
@@ -1002,6 +1038,7 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
                                         SNDRV_TIMER_EVENT_MPAUSE,
                                         &runtime->trigger_tstamp);
                wake_up(&runtime->sleep);
+               wake_up(&runtime->tsleep);
        } else {
                runtime->status->state = SNDRV_PCM_STATE_RUNNING;
                if (substream->timer)
@@ -1059,6 +1096,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
        runtime->status->suspended_state = runtime->status->state;
        runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
        wake_up(&runtime->sleep);
+       wake_up(&runtime->tsleep);
 }
 
 static struct action_ops snd_pcm_action_suspend = {
@@ -3162,9 +3200,7 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
        long size;
        unsigned long offset;
 
-#ifdef pgprot_noncached
        area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
-#endif
        area->vm_flags |= VM_IO;
        size = area->vm_end - area->vm_start;
        offset = area->vm_pgoff << PAGE_SHIFT;
@@ -3178,6 +3214,15 @@ int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
 EXPORT_SYMBOL(snd_pcm_lib_mmap_iomem);
 #endif /* SNDRV_PCM_INFO_MMAP */
 
+/* mmap callback with pgprot_noncached */
+int snd_pcm_lib_mmap_noncached(struct snd_pcm_substream *substream,
+                              struct vm_area_struct *area)
+{
+       area->vm_page_prot = pgprot_noncached(area->vm_page_prot);
+       return snd_pcm_default_mmap(substream, area);
+}
+EXPORT_SYMBOL(snd_pcm_lib_mmap_noncached);
+
 /*
  * mmap DMA buffer
  */
index 8ca2be3..48eca9f 100644 (file)
@@ -2190,7 +2190,7 @@ static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
                if (p->cmd == cmd)
                        return p->func(client, arg);
        }
-       snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%2x)\n",
+       snd_printd("seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
                   cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
        return -ENOTTY;
 }
index f745c31..160b1bd 100644 (file)
 
 #define SKEW_BASE      0x10000 /* 16bit shift */
 
-static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer_tick *tick,
-                                             int tempo, int ppq)
+static void snd_seq_timer_set_tick_resolution(struct snd_seq_timer *tmr)
 {
-       if (tempo < 1000000)
-               tick->resolution = (tempo * 1000) / ppq;
+       if (tmr->tempo < 1000000)
+               tmr->tick.resolution = (tmr->tempo * 1000) / tmr->ppq;
        else {
                /* might overflow.. */
                unsigned int s;
-               s = tempo % ppq;
-               s = (s * 1000) / ppq;
-               tick->resolution = (tempo / ppq) * 1000;
-               tick->resolution += s;
+               s = tmr->tempo % tmr->ppq;
+               s = (s * 1000) / tmr->ppq;
+               tmr->tick.resolution = (tmr->tempo / tmr->ppq) * 1000;
+               tmr->tick.resolution += s;
        }
-       if (tick->resolution <= 0)
-               tick->resolution = 1;
-       snd_seq_timer_update_tick(tick, 0);
+       if (tmr->tick.resolution <= 0)
+               tmr->tick.resolution = 1;
+       snd_seq_timer_update_tick(&tmr->tick, 0);
 }
 
 /* create new timer (constructor) */
@@ -96,7 +95,7 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
        /* setup defaults */
        tmr->ppq = 96;          /* 96 PPQ */
        tmr->tempo = 500000;    /* 120 BPM */
-       snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+       snd_seq_timer_set_tick_resolution(tmr);
        tmr->running = 0;
 
        tmr->type = SNDRV_SEQ_TIMER_ALSA;
@@ -180,7 +179,7 @@ int snd_seq_timer_set_tempo(struct snd_seq_timer * tmr, int tempo)
        spin_lock_irqsave(&tmr->lock, flags);
        if ((unsigned int)tempo != tmr->tempo) {
                tmr->tempo = tempo;
-               snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+               snd_seq_timer_set_tick_resolution(tmr);
        }
        spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
@@ -205,7 +204,7 @@ int snd_seq_timer_set_ppq(struct snd_seq_timer * tmr, int ppq)
        }
 
        tmr->ppq = ppq;
-       snd_seq_timer_set_tick_resolution(&tmr->tick, tmr->tempo, tmr->ppq);
+       snd_seq_timer_set_tick_resolution(tmr);
        spin_unlock_irqrestore(&tmr->lock, flags);
        return 0;
 }
index 252e04c..7f41990 100644 (file)
@@ -45,109 +45,23 @@ MODULE_SUPPORTED_DEVICE("{{ALSA,Dummy soundcard}}");
 #define MAX_PCM_SUBSTREAMS     128
 #define MAX_MIDI_DEVICES       2
 
-#if 0 /* emu10k1 emulation */
-#define MAX_BUFFER_SIZE                (128 * 1024)
-static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
-{
-       int err;
-       err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
-       if (err < 0)
-               return err;
-       err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
-       if (err < 0)
-               return err;
-       return 0;
-}
-#define add_playback_constraints emu10k1_playback_constraints
-#endif
-
-#if 0 /* RME9652 emulation */
-#define MAX_BUFFER_SIZE                (26 * 64 * 1024)
-#define USE_FORMATS            SNDRV_PCM_FMTBIT_S32_LE
-#define USE_CHANNELS_MIN       26
-#define USE_CHANNELS_MAX       26
-#define USE_PERIODS_MIN                2
-#define USE_PERIODS_MAX                2
-#endif
-
-#if 0 /* ICE1712 emulation */
-#define MAX_BUFFER_SIZE                (256 * 1024)
-#define USE_FORMATS            SNDRV_PCM_FMTBIT_S32_LE
-#define USE_CHANNELS_MIN       10
-#define USE_CHANNELS_MAX       10
-#define USE_PERIODS_MIN                1
-#define USE_PERIODS_MAX                1024
-#endif
-
-#if 0 /* UDA1341 emulation */
-#define MAX_BUFFER_SIZE                (16380)
-#define USE_FORMATS            SNDRV_PCM_FMTBIT_S16_LE
-#define USE_CHANNELS_MIN       2
-#define USE_CHANNELS_MAX       2
-#define USE_PERIODS_MIN                2
-#define USE_PERIODS_MAX                255
-#endif
-
-#if 0 /* simple AC97 bridge (intel8x0) with 48kHz AC97 only codec */
-#define USE_FORMATS            SNDRV_PCM_FMTBIT_S16_LE
-#define USE_CHANNELS_MIN       2
-#define USE_CHANNELS_MAX       2
-#define USE_RATE               SNDRV_PCM_RATE_48000
-#define USE_RATE_MIN           48000
-#define USE_RATE_MAX           48000
-#endif
-
-#if 0 /* CA0106 */
-#define USE_FORMATS            SNDRV_PCM_FMTBIT_S16_LE
-#define USE_CHANNELS_MIN       2
-#define USE_CHANNELS_MAX       2
-#define USE_RATE               (SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000) 
-#define USE_RATE_MIN           48000 
-#define USE_RATE_MAX           192000
-#define MAX_BUFFER_SIZE                ((65536-64)*8)
-#define MAX_PERIOD_SIZE                (65536-64)
-#define USE_PERIODS_MIN                2
-#define USE_PERIODS_MAX                8
-#endif
-
-
 /* defaults */
-#ifndef MAX_BUFFER_SIZE
 #define MAX_BUFFER_SIZE                (64*1024)
-#endif
-#ifndef MAX_PERIOD_SIZE
+#define MIN_PERIOD_SIZE                64
 #define MAX_PERIOD_SIZE                MAX_BUFFER_SIZE
-#endif
-#ifndef USE_FORMATS
 #define USE_FORMATS            (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE)
-#endif
-#ifndef USE_RATE
 #define USE_RATE               SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000
 #define USE_RATE_MIN           5500
 #define USE_RATE_MAX           48000
-#endif
-#ifndef USE_CHANNELS_MIN
 #define USE_CHANNELS_MIN       1
-#endif
-#ifndef USE_CHANNELS_MAX
 #define USE_CHANNELS_MAX       2
-#endif
-#ifndef USE_PERIODS_MIN
 #define USE_PERIODS_MIN        1
-#endif
-#ifndef USE_PERIODS_MAX
 #define USE_PERIODS_MAX        1024
-#endif
-#ifndef add_playback_constraints
-#define add_playback_constraints(x) 0
-#endif
-#ifndef add_capture_constraints
-#define add_capture_constraints(x) 0
-#endif
 
 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 char *model[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = NULL};
 static int pcm_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 1};
 static int pcm_substreams[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 8};
 //static int midi_devs[SNDRV_CARDS] = {[0 ... (SNDRV_CARDS - 1)] = 2};
@@ -162,6 +76,8 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for dummy soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable this dummy soundcard.");
+module_param_array(model, charp, NULL, 0444);
+MODULE_PARM_DESC(model, "Soundcard model.");
 module_param_array(pcm_devs, int, NULL, 0444);
 MODULE_PARM_DESC(pcm_devs, "PCM devices # (0-4) for dummy driver.");
 module_param_array(pcm_substreams, int, NULL, 0444);
@@ -193,15 +109,120 @@ struct dummy_timer_ops {
        snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
 };
 
+struct dummy_model {
+       const char *name;
+       int (*playback_constraints)(struct snd_pcm_runtime *runtime);
+       int (*capture_constraints)(struct snd_pcm_runtime *runtime);
+       u64 formats;
+       size_t buffer_bytes_max;
+       size_t period_bytes_min;
+       size_t period_bytes_max;
+       unsigned int periods_min;
+       unsigned int periods_max;
+       unsigned int rates;
+       unsigned int rate_min;
+       unsigned int rate_max;
+       unsigned int channels_min;
+       unsigned int channels_max;
+};
+
 struct snd_dummy {
        struct snd_card *card;
+       struct dummy_model *model;
        struct snd_pcm *pcm;
+       struct snd_pcm_hardware pcm_hw;
        spinlock_t mixer_lock;
        int mixer_volume[MIXER_ADDR_LAST+1][2];
        int capture_source[MIXER_ADDR_LAST+1][2];
        const struct dummy_timer_ops *timer_ops;
 };
 
+/*
+ * card models
+ */
+
+static int emu10k1_playback_constraints(struct snd_pcm_runtime *runtime)
+{
+       int err;
+       err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 256, UINT_MAX);
+       if (err < 0)
+               return err;
+       return 0;
+}
+
+struct dummy_model model_emu10k1 = {
+       .name = "emu10k1",
+       .playback_constraints = emu10k1_playback_constraints,
+       .buffer_bytes_max = 128 * 1024,
+};
+
+struct dummy_model model_rme9652 = {
+       .name = "rme9652",
+       .buffer_bytes_max = 26 * 64 * 1024,
+       .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       .channels_min = 26,
+       .channels_max = 26,
+       .periods_min = 2,
+       .periods_max = 2,
+};
+
+struct dummy_model model_ice1712 = {
+       .name = "ice1712",
+       .buffer_bytes_max = 256 * 1024,
+       .formats = SNDRV_PCM_FMTBIT_S32_LE,
+       .channels_min = 10,
+       .channels_max = 10,
+       .periods_min = 1,
+       .periods_max = 1024,
+};
+
+struct dummy_model model_uda1341 = {
+       .name = "uda1341",
+       .buffer_bytes_max = 16380,
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .periods_min = 2,
+       .periods_max = 255,
+};
+
+struct dummy_model model_ac97 = {
+       .name = "ac97",
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .channels_min = 2,
+       .channels_max = 2,
+       .rates = SNDRV_PCM_RATE_48000,
+       .rate_min = 48000,
+       .rate_max = 48000,
+};
+
+struct dummy_model model_ca0106 = {
+       .name = "ca0106",
+       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       .buffer_bytes_max = ((65536-64)*8),
+       .period_bytes_max = (65536-64),
+       .periods_min = 2,
+       .periods_max = 8,
+       .channels_min = 2,
+       .channels_max = 2,
+       .rates = SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_96000|SNDRV_PCM_RATE_192000,
+       .rate_min = 48000,
+       .rate_max = 192000,
+};
+
+struct dummy_model *dummy_models[] = {
+       &model_emu10k1,
+       &model_rme9652,
+       &model_ice1712,
+       &model_uda1341,
+       &model_ac97,
+       &model_ca0106,
+       NULL
+};
+
 /*
  * system timer interface
  */
@@ -509,7 +530,7 @@ static struct snd_pcm_hardware dummy_pcm_hardware = {
        .channels_min =         USE_CHANNELS_MIN,
        .channels_max =         USE_CHANNELS_MAX,
        .buffer_bytes_max =     MAX_BUFFER_SIZE,
-       .period_bytes_min =     64,
+       .period_bytes_min =     MIN_PERIOD_SIZE,
        .period_bytes_max =     MAX_PERIOD_SIZE,
        .periods_min =          USE_PERIODS_MIN,
        .periods_max =          USE_PERIODS_MAX,
@@ -538,6 +559,7 @@ static int dummy_pcm_hw_free(struct snd_pcm_substream *substream)
 static int dummy_pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
+       struct dummy_model *model = dummy->model;
        struct snd_pcm_runtime *runtime = substream->runtime;
        int err;
 
@@ -551,7 +573,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
        if (err < 0)
                return err;
 
-       runtime->hw = dummy_pcm_hardware;
+       runtime->hw = dummy->pcm_hw;
        if (substream->pcm->device & 1) {
                runtime->hw.info &= ~SNDRV_PCM_INFO_INTERLEAVED;
                runtime->hw.info |= SNDRV_PCM_INFO_NONINTERLEAVED;
@@ -560,10 +582,16 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
                runtime->hw.info &= ~(SNDRV_PCM_INFO_MMAP |
                                      SNDRV_PCM_INFO_MMAP_VALID);
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               err = add_playback_constraints(substream->runtime);
-       else
-               err = add_capture_constraints(substream->runtime);
+       if (model == NULL)
+               return 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (model->playback_constraints)
+                       err = model->playback_constraints(substream->runtime);
+       } else {
+               if (model->capture_constraints)
+                       err = model->capture_constraints(substream->runtime);
+       }
        if (err < 0) {
                dummy->timer_ops->free(substream);
                return err;
@@ -823,17 +851,19 @@ static int __devinit snd_card_dummy_new_mixer(struct snd_dummy *dummy)
 /*
  * proc interface
  */
-static void print_formats(struct snd_info_buffer *buffer)
+static void print_formats(struct snd_dummy *dummy,
+                         struct snd_info_buffer *buffer)
 {
        int i;
 
        for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
-               if (dummy_pcm_hardware.formats & (1ULL << i))
+               if (dummy->pcm_hw.formats & (1ULL << i))
                        snd_iprintf(buffer, " %s", snd_pcm_format_name(i));
        }
 }
 
-static void print_rates(struct snd_info_buffer *buffer)
+static void print_rates(struct snd_dummy *dummy,
+                       struct snd_info_buffer *buffer)
 {
        static int rates[] = {
                5512, 8000, 11025, 16000, 22050, 32000, 44100, 48000,
@@ -841,19 +871,19 @@ static void print_rates(struct snd_info_buffer *buffer)
        };
        int i;
 
-       if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_CONTINUOUS)
+       if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_CONTINUOUS)
                snd_iprintf(buffer, " continuous");
-       if (dummy_pcm_hardware.rates & SNDRV_PCM_RATE_KNOT)
+       if (dummy->pcm_hw.rates & SNDRV_PCM_RATE_KNOT)
                snd_iprintf(buffer, " knot");
        for (i = 0; i < ARRAY_SIZE(rates); i++)
-               if (dummy_pcm_hardware.rates & (1 << i))
+               if (dummy->pcm_hw.rates & (1 << i))
                        snd_iprintf(buffer, " %d", rates[i]);
 }
 
-#define get_dummy_int_ptr(ofs) \
-       (unsigned int *)((char *)&dummy_pcm_hardware + (ofs))
-#define get_dummy_ll_ptr(ofs) \
-       (unsigned long long *)((char *)&dummy_pcm_hardware + (ofs))
+#define get_dummy_int_ptr(dummy, ofs) \
+       (unsigned int *)((char *)&((dummy)->pcm_hw) + (ofs))
+#define get_dummy_ll_ptr(dummy, ofs) \
+       (unsigned long long *)((char *)&((dummy)->pcm_hw) + (ofs))
 
 struct dummy_hw_field {
        const char *name;
@@ -884,20 +914,21 @@ static struct dummy_hw_field fields[] = {
 static void dummy_proc_read(struct snd_info_entry *entry,
                            struct snd_info_buffer *buffer)
 {
+       struct snd_dummy *dummy = entry->private_data;
        int i;
 
        for (i = 0; i < ARRAY_SIZE(fields); i++) {
                snd_iprintf(buffer, "%s ", fields[i].name);
                if (fields[i].size == sizeof(int))
                        snd_iprintf(buffer, fields[i].format,
-                                   *get_dummy_int_ptr(fields[i].offset));
+                               *get_dummy_int_ptr(dummy, fields[i].offset));
                else
                        snd_iprintf(buffer, fields[i].format,
-                                   *get_dummy_ll_ptr(fields[i].offset));
+                               *get_dummy_ll_ptr(dummy, fields[i].offset));
                if (!strcmp(fields[i].name, "formats"))
-                       print_formats(buffer);
+                       print_formats(dummy, buffer);
                else if (!strcmp(fields[i].name, "rates"))
-                       print_rates(buffer);
+                       print_rates(dummy, buffer);
                snd_iprintf(buffer, "\n");
        }
 }
@@ -905,6 +936,7 @@ static void dummy_proc_read(struct snd_info_entry *entry,
 static void dummy_proc_write(struct snd_info_entry *entry,
                             struct snd_info_buffer *buffer)
 {
+       struct snd_dummy *dummy = entry->private_data;
        char line[64];
 
        while (!snd_info_get_line(buffer, line, sizeof(line))) {
@@ -924,9 +956,9 @@ static void dummy_proc_write(struct snd_info_entry *entry,
                if (strict_strtoull(item, 0, &val))
                        continue;
                if (fields[i].size == sizeof(int))
-                       *get_dummy_int_ptr(fields[i].offset) = val;
+                       *get_dummy_int_ptr(dummy, fields[i].offset) = val;
                else
-                       *get_dummy_ll_ptr(fields[i].offset) = val;
+                       *get_dummy_ll_ptr(dummy, fields[i].offset) = val;
        }
 }
 
@@ -938,6 +970,7 @@ static void __devinit dummy_proc_init(struct snd_dummy *chip)
                snd_info_set_text_ops(entry, chip, dummy_proc_read);
                entry->c.text.write = dummy_proc_write;
                entry->mode |= S_IWUSR;
+               entry->private_data = chip;
        }
 }
 #else
@@ -948,6 +981,7 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
 {
        struct snd_card *card;
        struct snd_dummy *dummy;
+       struct dummy_model *m = NULL, **mdl;
        int idx, err;
        int dev = devptr->id;
 
@@ -957,6 +991,15 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
                return err;
        dummy = card->private_data;
        dummy->card = card;
+       for (mdl = dummy_models; *mdl && model[dev]; mdl++) {
+               if (strcmp(model[dev], (*mdl)->name) == 0) {
+                       printk(KERN_INFO
+                               "snd-dummy: Using model '%s' for card %i\n",
+                               (*mdl)->name, card->number);
+                       m = dummy->model = *mdl;
+                       break;
+               }
+       }
        for (idx = 0; idx < MAX_PCM_DEVICES && idx < pcm_devs[dev]; idx++) {
                if (pcm_substreams[dev] < 1)
                        pcm_substreams[dev] = 1;
@@ -966,6 +1009,33 @@ static int __devinit snd_dummy_probe(struct platform_device *devptr)
                if (err < 0)
                        goto __nodev;
        }
+
+       dummy->pcm_hw = dummy_pcm_hardware;
+       if (m) {
+               if (m->formats)
+                       dummy->pcm_hw.formats = m->formats;
+               if (m->buffer_bytes_max)
+                       dummy->pcm_hw.buffer_bytes_max = m->buffer_bytes_max;
+               if (m->period_bytes_min)
+                       dummy->pcm_hw.period_bytes_min = m->period_bytes_min;
+               if (m->period_bytes_max)
+                       dummy->pcm_hw.period_bytes_max = m->period_bytes_max;
+               if (m->periods_min)
+                       dummy->pcm_hw.periods_min = m->periods_min;
+               if (m->periods_max)
+                       dummy->pcm_hw.periods_max = m->periods_max;
+               if (m->rates)
+                       dummy->pcm_hw.rates = m->rates;
+               if (m->rate_min)
+                       dummy->pcm_hw.rate_min = m->rate_min;
+               if (m->rate_max)
+                       dummy->pcm_hw.rate_max = m->rate_max;
+               if (m->channels_min)
+                       dummy->pcm_hw.channels_min = m->channels_min;
+               if (m->channels_max)
+                       dummy->pcm_hw.channels_max = m->channels_max;
+       }
+
        err = snd_card_dummy_new_mixer(dummy);
        if (err < 0)
                goto __nodev;
index 6644d00..35a2f71 100644 (file)
@@ -46,7 +46,6 @@
  */
 
 #include <linux/slab.h>
-#include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include "vx_cmd.h"
 
 
-/*
- * we use a vmalloc'ed (sg-)buffer
- */
-
-/* get the physical page pointer on the given offset */
-static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
-                                            unsigned long offset)
-{
-       void *pageptr = subs->runtime->dma_area + offset;
-       return vmalloc_to_page(pageptr);
-}
-
-/*
- * allocate a buffer via vmalloc_32().
- * called from hw_params
- * NOTE: this may be called not only once per pcm open!
- */
-static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
-{
-       struct snd_pcm_runtime *runtime = subs->runtime;
-       if (runtime->dma_area) {
-               /* already allocated */
-               if (runtime->dma_bytes >= size)
-                       return 0; /* already enough large */
-               vfree(runtime->dma_area);
-       }
-       runtime->dma_area = vmalloc_32(size);
-       if (! runtime->dma_area)
-               return -ENOMEM;
-       memset(runtime->dma_area, 0, size);
-       runtime->dma_bytes = size;
-       return 1; /* changed */
-}
-
-/*
- * free the buffer.
- * called from hw_free callback
- * NOTE: this may be called not only once per pcm open!
- */
-static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream *subs)
-{
-       struct snd_pcm_runtime *runtime = subs->runtime;
-
-       vfree(runtime->dma_area);
-       runtime->dma_area = NULL;
-       return 0;
-}
-
-
 /*
  * read three pending pcm bytes via inb()
  */
@@ -865,7 +815,8 @@ static snd_pcm_uframes_t vx_pcm_playback_pointer(struct snd_pcm_substream *subs)
 static int vx_pcm_hw_params(struct snd_pcm_substream *subs,
                                     struct snd_pcm_hw_params *hw_params)
 {
-       return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params));
+       return snd_pcm_lib_alloc_vmalloc_32_buffer
+                                       (subs, params_buffer_bytes(hw_params));
 }
 
 /*
@@ -873,7 +824,7 @@ static int vx_pcm_hw_params(struct snd_pcm_substream *subs,
  */
 static int vx_pcm_hw_free(struct snd_pcm_substream *subs)
 {
-       return snd_pcm_free_vmalloc_buffer(subs);
+       return snd_pcm_lib_free_vmalloc_buffer(subs);
 }
 
 /*
@@ -953,7 +904,8 @@ static struct snd_pcm_ops vx_pcm_playback_ops = {
        .prepare =      vx_pcm_prepare,
        .trigger =      vx_pcm_trigger,
        .pointer =      vx_pcm_playback_pointer,
-       .page =         snd_pcm_get_vmalloc_page,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
 };
 
 
@@ -1173,7 +1125,8 @@ static struct snd_pcm_ops vx_pcm_capture_ops = {
        .prepare =      vx_pcm_prepare,
        .trigger =      vx_pcm_trigger,
        .pointer =      vx_pcm_capture_pointer,
-       .page =         snd_pcm_get_vmalloc_page,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
 };
 
 
index 02fe81c..755a0a5 100644 (file)
@@ -63,15 +63,16 @@ config SND_AD1848
          will be called snd-ad1848.
 
 config SND_ALS100
-       tristate "Avance Logic ALS100/ALS120"
+       tristate "Diamond Tech. DT-019x and Avance Logic ALSxxx"
        depends on PNP
        select ISAPNP
        select SND_OPL3_LIB
        select SND_MPU401_UART
        select SND_SB16_DSP
        help
-         Say Y here to include support for soundcards based on Avance
-         Logic ALS100, ALS110, ALS120 and ALS200 chips.
+         Say Y here to include support for soundcards based on the
+         Diamond Technologies DT-019X or Avance Logic chips: ALS007,
+         ALS100, ALS110, ALS120 and ALS200 chips.
 
          To compile this driver as a module, choose M here: the module
          will be called snd-als100.
@@ -127,20 +128,6 @@ config SND_CS4236
          To compile this driver as a module, choose M here: the module
          will be called snd-cs4236.
 
-config SND_DT019X
-       tristate "Diamond Technologies DT-019X, Avance Logic ALS-007"
-       depends on PNP
-       select ISAPNP
-       select SND_OPL3_LIB
-       select SND_MPU401_UART
-       select SND_SB16_DSP
-       help
-         Say Y here to include support for soundcards based on the
-         Diamond Technologies DT-019X or Avance Logic ALS-007 chips.
-
-         To compile this driver as a module, choose M here: the module
-         will be called snd-dt019x.
-
 config SND_ES968
        tristate "Generic ESS ES968 driver"
        depends on PNP
@@ -252,6 +239,22 @@ config SND_INTERWAVE_STB
          To compile this driver as a module, choose M here: the module
          will be called snd-interwave-stb.
 
+config SND_JAZZ16
+       tristate "Media Vision Jazz16 card and compatibles"
+       select SND_OPL3_LIB
+       select SND_MPU401_UART
+       select SND_SB8_DSP
+       help
+         Say Y here to include support for soundcards based on the
+         Media Vision Jazz16 chipset: digital chip MVD1216 (Jazz16),
+         codec MVA416 (CS4216) and mixer MVA514 (ICS2514).
+         Media Vision's Jazz16 cards were sold under names Pro Sonic 16,
+         Premium 3-D and Pro 3-D. There were also OEMs cards with the
+         Jazz16 chipset.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-jazz16.
+
 config SND_OPL3SA2
        tristate "Yamaha OPL3-SA2/SA3"
        select SND_OPL3_LIB
index b906b9a..c73d30c 100644 (file)
@@ -7,7 +7,6 @@ snd-adlib-objs := adlib.o
 snd-als100-objs := als100.o
 snd-azt2320-objs := azt2320.o
 snd-cmi8330-objs := cmi8330.o
-snd-dt019x-objs := dt019x.o
 snd-es18xx-objs := es18xx.o
 snd-opl3sa2-objs := opl3sa2.o
 snd-sc6000-objs := sc6000.o
@@ -19,7 +18,6 @@ obj-$(CONFIG_SND_ADLIB) += snd-adlib.o
 obj-$(CONFIG_SND_ALS100) += snd-als100.o
 obj-$(CONFIG_SND_AZT2320) += snd-azt2320.o
 obj-$(CONFIG_SND_CMI8330) += snd-cmi8330.o
-obj-$(CONFIG_SND_DT019X) += snd-dt019x.o
 obj-$(CONFIG_SND_ES18XX) += snd-es18xx.o
 obj-$(CONFIG_SND_OPL3SA2) += snd-opl3sa2.o
 obj-$(CONFIG_SND_SC6000) += snd-sc6000.o
index 5fd52e4..20becc8 100644 (file)
@@ -2,9 +2,13 @@
 /*
     card-als100.c - driver for Avance Logic ALS100 based soundcards.
     Copyright (C) 1999-2000 by Massimo Piccioni <dafastidio@libero.it>
+    Copyright (C) 1999-2002 by Massimo Piccioni <dafastidio@libero.it>
 
     Thanks to Pierfrancesco 'qM2' Passerini.
 
+    Generalised for soundcards based on DT-0196 and ALS-007 chips
+    by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>: June 2002.
+
     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
 
 #define PFX "als100: "
 
-MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
-MODULE_DESCRIPTION("Avance Logic ALS1X0");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS100 - PRO16PNP},"
+MODULE_DESCRIPTION("Avance Logic ALS007/ALS1X0");
+MODULE_SUPPORTED_DEVICE("{{Diamond Technologies DT-019X},"
+               "{Avance Logic ALS-007}}"
+               "{{Avance Logic,ALS100 - PRO16PNP},"
                "{Avance Logic,ALS110},"
                "{Avance Logic,ALS120},"
                "{Avance Logic,ALS200},"
@@ -45,9 +49,12 @@ MODULE_SUPPORTED_DEVICE("{{Avance Logic,ALS100 - PRO16PNP},"
                "{Avance Logic,ALS120},"
                "{RTL,RTL3000}}");
 
+MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
+MODULE_LICENSE("GPL");
+
 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_ISAPNP; /* Enable this card */
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE; /* Enable this card */
 static long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;    /* PnP setup */
 static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;        /* PnP setup */
 static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
@@ -57,14 +64,15 @@ static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;   /* PnP setup */
 static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;     /* PnP setup */
 
 module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for als100 based soundcard.");
+MODULE_PARM_DESC(index, "Index value for Avance Logic based soundcard.");
 module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for als100 based soundcard.");
+MODULE_PARM_DESC(id, "ID string for Avance Logic based soundcard.");
 module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable als100 based soundcard.");
+MODULE_PARM_DESC(enable, "Enable Avance Logic based soundcard.");
+
+MODULE_ALIAS("snd-dt019x");
 
 struct snd_card_als100 {
-       int dev_no;
        struct pnp_dev *dev;
        struct pnp_dev *devmpu;
        struct pnp_dev *devopl;
@@ -72,25 +80,43 @@ struct snd_card_als100 {
 };
 
 static struct pnp_card_device_id snd_als100_pnpids[] = {
+       /* DT197A30 */
+       { .id = "RWB1688",
+         .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },
+         .driver_data = SB_HW_DT019X },
+       /* DT0196 / ALS-007 */
+       { .id = "ALS0007",
+         .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },
+         .driver_data = SB_HW_DT019X },
        /* ALS100 - PRO16PNP */
-       { .id = "ALS0001", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } } },
+       { .id = "ALS0001",
+         .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" } },
+         .driver_data = SB_HW_ALS100 },
        /* ALS110 - MF1000 - Digimate 3D Sound */
-       { .id = "ALS0110", .devs = { { "@@@1001" }, { "@X@1001" }, { "@H@1001" } } },
+       { .id = "ALS0110",
+         .devs = { { "@@@1001" }, { "@X@1001" }, { "@H@1001" } },
+         .driver_data = SB_HW_ALS100 },
        /* ALS120 */
-       { .id = "ALS0120", .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } } },
+       { .id = "ALS0120",
+         .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } },
+         .driver_data = SB_HW_ALS100 },
        /* ALS200 */
-       { .id = "ALS0200", .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0001" } } },
+       { .id = "ALS0200",
+         .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0001" } },
+         .driver_data = SB_HW_ALS100 },
        /* ALS200 OEM */
-       { .id = "ALS0200", .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0020" } } },
+       { .id = "ALS0200",
+         .devs = { { "@@@0020" }, { "@X@0020" }, { "@H@0020" } },
+         .driver_data = SB_HW_ALS100 },
        /* RTL3000 */
-       { .id = "RTL3000", .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } } },
-       { .id = "", } /* end */
+       { .id = "RTL3000",
+         .devs = { { "@@@2001" }, { "@X@2001" }, { "@H@2001" } },
+         .driver_data = SB_HW_ALS100 },
+       { .id = "" } /* end */
 };
 
 MODULE_DEVICE_TABLE(pnp_card, snd_als100_pnpids);
 
-#define DRIVER_NAME    "snd-card-als100"
-
 static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
                                         struct pnp_card_link *card,
                                         const struct pnp_card_device_id *id)
@@ -113,8 +139,12 @@ static int __devinit snd_card_als100_pnp(int dev, struct snd_card_als100 *acard,
                return err;
        }
        port[dev] = pnp_port_start(pdev, 0);
-       dma8[dev] = pnp_dma(pdev, 1);
-       dma16[dev] = pnp_dma(pdev, 0);
+       if (id->driver_data == SB_HW_DT019X)
+               dma8[dev] = pnp_dma(pdev, 0);
+       else {
+               dma8[dev] = pnp_dma(pdev, 1);
+               dma16[dev] = pnp_dma(pdev, 0);
+       }
        irq[dev] = pnp_irq(pdev, 0);
 
        pdev = acard->devmpu;
@@ -175,22 +205,33 @@ static int __devinit snd_card_als100_probe(int dev,
        }
        snd_card_set_dev(card, &pcard->card->dev);
 
-       if ((error = snd_sbdsp_create(card, port[dev],
-                                     irq[dev],
-                                     snd_sb16dsp_interrupt,
-                                     dma8[dev],
-                                     dma16[dev],
-                                     SB_HW_ALS100, &chip)) < 0) {
+       if (pid->driver_data == SB_HW_DT019X)
+               dma16[dev] = -1;
+
+       error = snd_sbdsp_create(card, port[dev], irq[dev],
+                                 snd_sb16dsp_interrupt,
+                                 dma8[dev], dma16[dev],
+                                 pid->driver_data,
+                                 &chip);
+       if (error < 0) {
                snd_card_free(card);
                return error;
        }
        acard->chip = chip;
 
-       strcpy(card->driver, "ALS100");
-       strcpy(card->shortname, "Avance Logic ALS100");
-       sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
-               card->shortname, chip->name, chip->port,
-               irq[dev], dma8[dev], dma16[dev]);
+       if (pid->driver_data == SB_HW_DT019X) {
+               strcpy(card->driver, "DT-019X");
+               strcpy(card->shortname, "Diamond Tech. DT-019X");
+               sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
+                       card->shortname, chip->name, chip->port,
+                       irq[dev], dma8[dev]);
+       } else {
+               strcpy(card->driver, "ALS100");
+               strcpy(card->shortname, "Avance Logic ALS100");
+               sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d&%d",
+                       card->shortname, chip->name, chip->port,
+                       irq[dev], dma8[dev], dma16[dev]);
+       }
 
        if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
                snd_card_free(card);
@@ -203,9 +244,19 @@ static int __devinit snd_card_als100_probe(int dev,
        }
 
        if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
-               if (snd_mpu401_uart_new(card, 0, MPU401_HW_ALS100,
+               int mpu_type = MPU401_HW_ALS100;
+
+               if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
+                       mpu_irq[dev] = -1;
+
+               if (pid->driver_data == SB_HW_DT019X)
+                       mpu_type = MPU401_HW_MPU401;
+
+               if (snd_mpu401_uart_new(card, 0,
+                                       mpu_type,
                                        mpu_port[dev], 0, 
-                                       mpu_irq[dev], IRQF_DISABLED,
+                                       mpu_irq[dev],
+                                       mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
                                        NULL) < 0)
                        snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
        }
@@ -291,7 +342,7 @@ static int snd_als100_pnp_resume(struct pnp_card_link *pcard)
 
 static struct pnp_card_driver als100_pnpc_driver = {
        .flags          = PNP_DRIVER_RES_DISABLE,
-        .name           = "als100",
+       .name           = "als100",
         .id_table       = snd_als100_pnpids,
         .probe          = snd_als100_pnp_detect,
         .remove         = __devexit_p(snd_als100_pnp_remove),
@@ -312,7 +363,7 @@ static int __init alsa_card_als100_init(void)
        if (!als100_devices) {
                pnp_unregister_card_driver(&als100_pnpc_driver);
 #ifdef MODULE
-               snd_printk(KERN_ERR "no ALS100 based soundcards found\n");
+               snd_printk(KERN_ERR "no Avance Logic based soundcards found\n");
 #endif
                return -ENODEV;
        }
diff --git a/sound/isa/dt019x.c b/sound/isa/dt019x.c
deleted file mode 100644 (file)
index 80f5b1a..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-
-/*
-    dt019x.c - driver for Diamond Technologies DT-0197H based soundcards.
-    Copyright (C) 1999, 2002 by Massimo Piccioni <dafastidio@libero.it>
-
-    Generalised for soundcards based on DT-0196 and ALS-007 chips 
-    by Jonathan Woithe <jwoithe@physics.adelaide.edu.au>: June 2002.
-
-    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/wait.h>
-#include <linux/pnp.h>
-#include <linux/moduleparam.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/mpu401.h>
-#include <sound/opl3.h>
-#include <sound/sb.h>
-
-#define PFX "dt019x: "
-
-MODULE_AUTHOR("Massimo Piccioni <dafastidio@libero.it>");
-MODULE_DESCRIPTION("Diamond Technologies DT-019X / Avance Logic ALS-007");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{Diamond Technologies DT-019X},"
-              "{Avance Logic ALS-007}}");
-
-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 port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;    /* PnP setup */
-static long mpu_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;        /* PnP setup */
-static long fm_port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT; /* PnP setup */
-static int irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;       /* PnP setup */
-static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;   /* PnP setup */
-static int dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;      /* PnP setup */
-
-module_param_array(index, int, NULL, 0444);
-MODULE_PARM_DESC(index, "Index value for DT-019X based soundcard.");
-module_param_array(id, charp, NULL, 0444);
-MODULE_PARM_DESC(id, "ID string for DT-019X based soundcard.");
-module_param_array(enable, bool, NULL, 0444);
-MODULE_PARM_DESC(enable, "Enable DT-019X based soundcard.");
-
-struct snd_card_dt019x {
-       struct pnp_dev *dev;
-       struct pnp_dev *devmpu;
-       struct pnp_dev *devopl;
-       struct snd_sb *chip;
-};
-
-static struct pnp_card_device_id snd_dt019x_pnpids[] = {
-       /* DT197A30 */
-       { .id = "RWB1688", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" }, } },
-       /* DT0196 / ALS-007 */
-       { .id = "ALS0007", .devs = { { "@@@0001" }, { "@X@0001" }, { "@H@0001" }, } },
-       { .id = "",  }
-};
-
-MODULE_DEVICE_TABLE(pnp_card, snd_dt019x_pnpids);
-
-
-#define DRIVER_NAME    "snd-card-dt019x"
-
-
-static int __devinit snd_card_dt019x_pnp(int dev, struct snd_card_dt019x *acard,
-                                        struct pnp_card_link *card,
-                                        const struct pnp_card_device_id *pid)
-{
-       struct pnp_dev *pdev;
-       int err;
-
-       acard->dev = pnp_request_card_device(card, pid->devs[0].id, NULL);
-       if (acard->dev == NULL)
-               return -ENODEV;
-
-       acard->devmpu = pnp_request_card_device(card, pid->devs[1].id, NULL);
-       acard->devopl = pnp_request_card_device(card, pid->devs[2].id, NULL);
-
-       pdev = acard->dev;
-
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
-               snd_printk(KERN_ERR PFX "DT-019X AUDIO pnp configure failure\n");
-               return err;
-       }
-
-       port[dev] = pnp_port_start(pdev, 0);
-       dma8[dev] = pnp_dma(pdev, 0);
-       irq[dev] = pnp_irq(pdev, 0);
-       snd_printdd("dt019x: found audio interface: port=0x%lx, irq=0x%x, dma=0x%x\n",
-                       port[dev],irq[dev],dma8[dev]);
-
-       pdev = acard->devmpu;
-       if (pdev != NULL) {
-               err = pnp_activate_dev(pdev);
-               if (err < 0) {
-                       pnp_release_card_device(pdev);
-                       snd_printk(KERN_ERR PFX "DT-019X MPU401 pnp configure failure, skipping\n");
-                       goto __mpu_error;
-               }
-               mpu_port[dev] = pnp_port_start(pdev, 0);
-               mpu_irq[dev] = pnp_irq(pdev, 0);
-               snd_printdd("dt019x: found MPU-401: port=0x%lx, irq=0x%x\n",
-                               mpu_port[dev],mpu_irq[dev]);
-       } else {
-       __mpu_error:
-               acard->devmpu = NULL;
-               mpu_port[dev] = -1;
-       }
-
-       pdev = acard->devopl;
-       if (pdev != NULL) {
-               err = pnp_activate_dev(pdev);
-               if (err < 0) {
-                       pnp_release_card_device(pdev);
-                       snd_printk(KERN_ERR PFX "DT-019X OPL3 pnp configure failure, skipping\n");
-                       goto __fm_error;
-               }
-               fm_port[dev] = pnp_port_start(pdev, 0);
-               snd_printdd("dt019x: found OPL3 synth: port=0x%lx\n",fm_port[dev]);
-       } else {
-       __fm_error:
-               acard->devopl = NULL;
-               fm_port[dev] = -1;
-       }
-
-       return 0;
-}
-
-static int __devinit snd_card_dt019x_probe(int dev, struct pnp_card_link *pcard, const struct pnp_card_device_id *pid)
-{
-       int error;
-       struct snd_sb *chip;
-       struct snd_card *card;
-       struct snd_card_dt019x *acard;
-       struct snd_opl3 *opl3;
-
-       error = snd_card_create(index[dev], id[dev], THIS_MODULE,
-                               sizeof(struct snd_card_dt019x), &card);
-       if (error < 0)
-               return error;
-       acard = card->private_data;
-
-       snd_card_set_dev(card, &pcard->card->dev);
-       if ((error = snd_card_dt019x_pnp(dev, acard, pcard, pid))) {
-               snd_card_free(card);
-               return error;
-       }
-
-       if ((error = snd_sbdsp_create(card, port[dev],
-                                     irq[dev],
-                                     snd_sb16dsp_interrupt,
-                                     dma8[dev],
-                                     -1,
-                                     SB_HW_DT019X,
-                                     &chip)) < 0) {
-               snd_card_free(card);
-               return error;
-       }
-       acard->chip = chip;
-
-       strcpy(card->driver, "DT-019X");
-       strcpy(card->shortname, "Diamond Tech. DT-019X");
-       sprintf(card->longname, "%s, %s at 0x%lx, irq %d, dma %d",
-               card->shortname, chip->name, chip->port,
-               irq[dev], dma8[dev]);
-
-       if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
-               snd_card_free(card);
-               return error;
-       }
-       if ((error = snd_sbmixer_new(chip)) < 0) {
-               snd_card_free(card);
-               return error;
-       }
-
-       if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
-               if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
-                       mpu_irq[dev] = -1;
-               if (snd_mpu401_uart_new(card, 0,
-/*                                     MPU401_HW_SB,*/
-                                       MPU401_HW_MPU401,
-                                       mpu_port[dev], 0,
-                                       mpu_irq[dev],
-                                       mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
-                                       NULL) < 0)
-                       snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx ?\n", mpu_port[dev]);
-       }
-
-       if (fm_port[dev] > 0 && fm_port[dev] != SNDRV_AUTO_PORT) {
-               if (snd_opl3_create(card,
-                                   fm_port[dev],
-                                   fm_port[dev] + 2,
-                                   OPL3_HW_AUTO, 0, &opl3) < 0) {
-                       snd_printk(KERN_ERR PFX "no OPL device at 0x%lx-0x%lx ?\n",
-                                  fm_port[dev], fm_port[dev] + 2);
-               } else {
-                       if ((error = snd_opl3_timer_new(opl3, 0, 1)) < 0) {
-                               snd_card_free(card);
-                               return error;
-                       }
-                       if ((error = snd_opl3_hwdep_new(opl3, 0, 1, NULL)) < 0) {
-                               snd_card_free(card);
-                               return error;
-                       }
-               }
-       }
-
-       if ((error = snd_card_register(card)) < 0) {
-               snd_card_free(card);
-               return error;
-       }
-       pnp_set_card_drvdata(pcard, card);
-       return 0;
-}
-
-static unsigned int __devinitdata dt019x_devices;
-
-static int __devinit snd_dt019x_pnp_probe(struct pnp_card_link *card,
-                                         const struct pnp_card_device_id *pid)
-{
-       static int dev;
-       int res;
-
-       for ( ; dev < SNDRV_CARDS; dev++) {
-               if (!enable[dev])
-                       continue;
-               res = snd_card_dt019x_probe(dev, card, pid);
-               if (res < 0)
-                       return res;
-               dev++;
-               dt019x_devices++;
-               return 0;
-       }
-       return -ENODEV;
-}
-
-static void __devexit snd_dt019x_pnp_remove(struct pnp_card_link * pcard)
-{
-       snd_card_free(pnp_get_card_drvdata(pcard));
-       pnp_set_card_drvdata(pcard, NULL);
-}
-
-#ifdef CONFIG_PM
-static int snd_dt019x_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
-{
-       struct snd_card *card = pnp_get_card_drvdata(pcard);
-       struct snd_card_dt019x *acard = card->private_data;
-       struct snd_sb *chip = acard->chip;
-
-       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-       snd_pcm_suspend_all(chip->pcm);
-       snd_sbmixer_suspend(chip);
-       return 0;
-}
-
-static int snd_dt019x_pnp_resume(struct pnp_card_link *pcard)
-{
-       struct snd_card *card = pnp_get_card_drvdata(pcard);
-       struct snd_card_dt019x *acard = card->private_data;
-       struct snd_sb *chip = acard->chip;
-
-       snd_sbdsp_reset(chip);
-       snd_sbmixer_resume(chip);
-       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
-       return 0;
-}
-#endif
-
-static struct pnp_card_driver dt019x_pnpc_driver = {
-       .flags          = PNP_DRIVER_RES_DISABLE,
-       .name           = "dt019x",
-       .id_table       = snd_dt019x_pnpids,
-       .probe          = snd_dt019x_pnp_probe,
-       .remove         = __devexit_p(snd_dt019x_pnp_remove),
-#ifdef CONFIG_PM
-       .suspend        = snd_dt019x_pnp_suspend,
-       .resume         = snd_dt019x_pnp_resume,
-#endif
-};
-
-static int __init alsa_card_dt019x_init(void)
-{
-       int err;
-
-       err = pnp_register_card_driver(&dt019x_pnpc_driver);
-       if (err)
-               return err;
-
-       if (!dt019x_devices) {
-               pnp_unregister_card_driver(&dt019x_pnpc_driver);
-#ifdef MODULE
-               snd_printk(KERN_ERR "no DT-019X / ALS-007 based soundcards found\n");
-#endif
-               return -ENODEV;
-       }
-       return 0;
-}
-
-static void __exit alsa_card_dt019x_exit(void)
-{
-       pnp_unregister_card_driver(&dt019x_pnpc_driver);
-}
-
-module_init(alsa_card_dt019x_init)
-module_exit(alsa_card_dt019x_exit)
index c8a8da0..a4af53b 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/io.h>
 #include <asm/dma.h>
 #include <sound/core.h>
+#include <sound/tlv.h>
 #include <sound/wss.h>
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
@@ -546,6 +547,93 @@ __skip_mpu:
 
 #ifdef OPTi93X
 
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_step, -9300, 300, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static const DECLARE_TLV_DB_SCALE(db_scale_4bit_12db_max, -3300, 300, 0);
+
+static struct snd_kcontrol_new snd_opti93x_controls[] = {
+WSS_DOUBLE("Master Playback Switch", 0,
+               OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Master Playback Volume", 0,
+               OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
+               db_scale_5bit_3db_step),
+WSS_DOUBLE_TLV("PCM Playback Volume", 0,
+               CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1,
+               db_scale_5bit),
+WSS_DOUBLE_TLV("FM Playback Volume", 0,
+               CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1,
+               db_scale_4bit_12db_max),
+WSS_DOUBLE("Line Playback Switch", 0,
+               CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Line Playback Volume", 0,
+               CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1,
+               db_scale_4bit_12db_max),
+WSS_DOUBLE("Mic Playback Switch", 0,
+               OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Mic Playback Volume", 0,
+               OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1,
+               db_scale_4bit_12db_max),
+WSS_DOUBLE_TLV("CD Playback Volume", 0,
+               CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1,
+               db_scale_4bit_12db_max),
+WSS_DOUBLE("Aux Playback Switch", 0,
+               OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
+WSS_DOUBLE_TLV("Aux Playback Volume", 0,
+               OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1,
+               db_scale_4bit_12db_max),
+};
+
+static int __devinit snd_opti93x_mixer(struct snd_wss *chip)
+{
+       struct snd_card *card;
+       unsigned int idx;
+       struct snd_ctl_elem_id id1, id2;
+       int err;
+
+       if (snd_BUG_ON(!chip || !chip->pcm))
+               return -EINVAL;
+
+       card = chip->card;
+
+       strcpy(card->mixername, chip->pcm->name);
+
+       memset(&id1, 0, sizeof(id1));
+       memset(&id2, 0, sizeof(id2));
+       id1.iface = id2.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       /* reassign AUX0 switch to CD */
+       strcpy(id1.name, "Aux Playback Switch");
+       strcpy(id2.name, "CD Playback Switch");
+       err = snd_ctl_rename_id(card, &id1, &id2);
+       if (err < 0) {
+               snd_printk(KERN_ERR "Cannot rename opti93x control\n");
+               return err;
+       }
+       /* reassign AUX1 switch to FM */
+       strcpy(id1.name, "Aux Playback Switch"); id1.index = 1;
+       strcpy(id2.name, "FM Playback Switch");
+       err = snd_ctl_rename_id(card, &id1, &id2);
+       if (err < 0) {
+               snd_printk(KERN_ERR "Cannot rename opti93x control\n");
+               return err;
+       }
+       /* remove AUX1 volume */
+       strcpy(id1.name, "Aux Playback Volume"); id1.index = 1;
+       snd_ctl_remove_id(card, &id1);
+
+       /* Replace WSS volume controls with OPTi93x volume controls */
+       id1.index = 0;
+       for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
+               strcpy(id1.name, snd_opti93x_controls[idx].name);
+               snd_ctl_remove_id(card, &id1);
+
+               err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_opti93x_controls[idx], chip));
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
 static irqreturn_t snd_opti93x_interrupt(int irq, void *dev_id)
 {
        struct snd_opti9xx *chip = dev_id;
@@ -754,6 +842,11 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
        error = snd_wss_mixer(codec);
        if (error < 0)
                return error;
+#ifdef OPTi93X
+       error = snd_opti93x_mixer(codec);
+       if (error < 0)
+               return error;
+#endif
 #ifdef CS4231
        error = snd_wss_timer(codec, 0, &timer);
        if (error < 0)
index faeffce..af36696 100644 (file)
@@ -12,6 +12,7 @@ snd-sb16-objs := sb16.o
 snd-sbawe-objs := sbawe.o emu8000.o
 snd-emu8000-synth-objs := emu8000_synth.o emu8000_callback.o emu8000_patch.o emu8000_pcm.o
 snd-es968-objs := es968.o
+snd-jazz16-objs := jazz16.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_SB_COMMON) += snd-sb-common.o
@@ -21,6 +22,7 @@ obj-$(CONFIG_SND_SB8) += snd-sb8.o
 obj-$(CONFIG_SND_SB16) += snd-sb16.o
 obj-$(CONFIG_SND_SBAWE) += snd-sbawe.o
 obj-$(CONFIG_SND_ES968) += snd-es968.o
+obj-$(CONFIG_SND_JAZZ16) += snd-jazz16.o
 ifeq ($(CONFIG_SND_SB16_CSP),y)
   obj-$(CONFIG_SND_SB16) += snd-sb16-csp.o
   obj-$(CONFIG_SND_SBAWE) += snd-sb16-csp.o
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
new file mode 100644 (file)
index 0000000..8d21a3f
--- /dev/null
@@ -0,0 +1,404 @@
+
+/*
+ * jazz16.c - driver for Media Vision Jazz16 based soundcards.
+ * Copyright (C) 2009 Krzysztof Helt <krzysztof.h1@wp.pl>
+ * Based on patches posted by Rask Ingemann Lambertsen and Rene Herman.
+ * Based on OSS Sound Blaster driver.
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+#include <linux/isa.h>
+#include <sound/core.h>
+#include <sound/mpu401.h>
+#include <sound/opl3.h>
+#include <sound/sb.h>
+#define SNDRV_LEGACY_FIND_FREE_IRQ
+#define SNDRV_LEGACY_FIND_FREE_DMA
+#include <sound/initval.h>
+
+#define PFX "jazz16: "
+
+MODULE_DESCRIPTION("Media Vision Jazz16");
+MODULE_SUPPORTED_DEVICE("{{Media Vision ??? },"
+               "{RTL,RTL3000}}");
+
+MODULE_AUTHOR("Krzysztof Helt <krzysztof.h1@wp.pl>");
+MODULE_LICENSE("GPL");
+
+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 unsigned long port[SNDRV_CARDS] = SNDRV_DEFAULT_PORT;
+static unsigned long mpu_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 dma8[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+static int dma16[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for Media Vision Jazz16 based soundcard.");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string for Media Vision Jazz16 based soundcard.");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "Enable Media Vision Jazz16 based soundcard.");
+module_param_array(port, long, NULL, 0444);
+MODULE_PARM_DESC(port, "Port # for jazz16 driver.");
+module_param_array(mpu_port, long, NULL, 0444);
+MODULE_PARM_DESC(mpu_port, "MPU-401 port # for jazz16 driver.");
+module_param_array(irq, int, NULL, 0444);
+MODULE_PARM_DESC(irq, "IRQ # for jazz16 driver.");
+module_param_array(mpu_irq, int, NULL, 0444);
+MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for jazz16 driver.");
+module_param_array(dma8, int, NULL, 0444);
+MODULE_PARM_DESC(dma8, "DMA8 # for jazz16 driver.");
+module_param_array(dma16, int, NULL, 0444);
+MODULE_PARM_DESC(dma16, "DMA16 # for jazz16 driver.");
+
+#define SB_JAZZ16_WAKEUP       0xaf
+#define SB_JAZZ16_SET_PORTS    0x50
+#define SB_DSP_GET_JAZZ_BRD_REV        0xfa
+#define SB_JAZZ16_SET_DMAINTR  0xfb
+#define SB_DSP_GET_JAZZ_MODEL  0xfe
+
+struct snd_card_jazz16 {
+       struct snd_sb *chip;
+};
+
+static irqreturn_t jazz16_interrupt(int irq, void *chip)
+{
+       return snd_sb8dsp_interrupt(chip);
+}
+
+static int __devinit jazz16_configure_ports(unsigned long port,
+                                           unsigned long mpu_port, int idx)
+{
+       unsigned char val;
+
+       if (!request_region(0x201, 1, "jazz16 config")) {
+               snd_printk(KERN_ERR "config port region is already in use.\n");
+               return -EBUSY;
+       }
+       outb(SB_JAZZ16_WAKEUP - idx, 0x201);
+       udelay(100);
+       outb(SB_JAZZ16_SET_PORTS + idx, 0x201);
+       udelay(100);
+       val = port & 0x70;
+       val |= (mpu_port & 0x30) >> 4;
+       outb(val, 0x201);
+
+       release_region(0x201, 1);
+       return 0;
+}
+
+static int __devinit jazz16_detect_board(unsigned long port,
+                                        unsigned long mpu_port)
+{
+       int err;
+       int val;
+       struct snd_sb chip;
+
+       if (!request_region(port, 0x10, "jazz16")) {
+               snd_printk(KERN_ERR "I/O port region is already in use.\n");
+               return -EBUSY;
+       }
+       /* just to call snd_sbdsp_command/reset/get_byte() */
+       chip.port = port;
+
+       err = snd_sbdsp_reset(&chip);
+       if (err < 0)
+               for (val = 0; val < 4; val++) {
+                       err = jazz16_configure_ports(port, mpu_port, val);
+                       if (err < 0)
+                               break;
+
+                       err = snd_sbdsp_reset(&chip);
+                       if (!err)
+                               break;
+               }
+       if (err < 0) {
+               err = -ENODEV;
+               goto err_unmap;
+       }
+       if (!snd_sbdsp_command(&chip, SB_DSP_GET_JAZZ_BRD_REV)) {
+               err = -EBUSY;
+               goto err_unmap;
+       }
+       val = snd_sbdsp_get_byte(&chip);
+       if (val >= 0x30)
+               snd_sbdsp_get_byte(&chip);
+
+       if ((val & 0xf0) != 0x10) {
+               err = -ENODEV;
+               goto err_unmap;
+       }
+       if (!snd_sbdsp_command(&chip, SB_DSP_GET_JAZZ_MODEL)) {
+               err = -EBUSY;
+               goto err_unmap;
+       }
+       snd_sbdsp_get_byte(&chip);
+       err = snd_sbdsp_get_byte(&chip);
+       snd_printd("Media Vision Jazz16 board detected: rev 0x%x, model 0x%x\n",
+                  val, err);
+
+       err = 0;
+
+err_unmap:
+       release_region(port, 0x10);
+       return err;
+}
+
+static int __devinit jazz16_configure_board(struct snd_sb *chip, int mpu_irq)
+{
+       static unsigned char jazz_irq_bits[] = { 0, 0, 2, 3, 0, 1, 0, 4,
+                                                0, 2, 5, 0, 0, 0, 0, 6 };
+       static unsigned char jazz_dma_bits[] = { 0, 1, 0, 2, 0, 3, 0, 4 };
+
+       if (jazz_dma_bits[chip->dma8] == 0 ||
+           jazz_dma_bits[chip->dma16] == 0 ||
+           jazz_irq_bits[chip->irq] == 0)
+               return -EINVAL;
+
+       if (!snd_sbdsp_command(chip, SB_JAZZ16_SET_DMAINTR))
+               return -EBUSY;
+
+       if (!snd_sbdsp_command(chip,
+                              jazz_dma_bits[chip->dma8] |
+                              (jazz_dma_bits[chip->dma16] << 4)))
+               return -EBUSY;
+
+       if (!snd_sbdsp_command(chip,
+                              jazz_irq_bits[chip->irq] |
+                              (jazz_irq_bits[mpu_irq] << 4)))
+               return -EBUSY;
+
+       return 0;
+}
+
+static int __devinit snd_jazz16_match(struct device *devptr, unsigned int dev)
+{
+       if (!enable[dev])
+               return 0;
+       if (port[dev] == SNDRV_AUTO_PORT) {
+               snd_printk(KERN_ERR "please specify port\n");
+               return 0;
+       } else if (port[dev] == 0x200 || (port[dev] & ~0x270)) {
+               snd_printk(KERN_ERR "incorrect port specified\n");
+               return 0;
+       }
+       if (dma8[dev] != SNDRV_AUTO_DMA &&
+           dma8[dev] != 1 && dma8[dev] != 3) {
+               snd_printk(KERN_ERR "dma8 must be 1 or 3\n");
+               return 0;
+       }
+       if (dma16[dev] != SNDRV_AUTO_DMA &&
+           dma16[dev] != 5 && dma16[dev] != 7) {
+               snd_printk(KERN_ERR "dma16 must be 5 or 7\n");
+               return 0;
+       }
+       if (mpu_port[dev] != SNDRV_AUTO_PORT &&
+           (mpu_port[dev] & ~0x030) != 0x300) {
+               snd_printk(KERN_ERR "incorrect mpu_port specified\n");
+               return 0;
+       }
+       if (mpu_irq[dev] != SNDRV_AUTO_DMA &&
+           mpu_irq[dev] != 2 && mpu_irq[dev] != 3 &&
+           mpu_irq[dev] != 5 && mpu_irq[dev] != 7) {
+               snd_printk(KERN_ERR "mpu_irq must be 2, 3, 5 or 7\n");
+               return 0;
+       }
+       return 1;
+}
+
+static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
+{
+       struct snd_card *card;
+       struct snd_card_jazz16 *jazz16;
+       struct snd_sb *chip;
+       struct snd_opl3 *opl3;
+       static int possible_irqs[] = {2, 3, 5, 7, 9, 10, 15, -1};
+       static int possible_dmas8[] = {1, 3, -1};
+       static int possible_dmas16[] = {5, 7, -1};
+       int err, xirq, xdma8, xdma16, xmpu_port, xmpu_irq;
+
+       err = snd_card_create(index[dev], id[dev], THIS_MODULE,
+                             sizeof(struct snd_card_jazz16), &card);
+       if (err < 0)
+               return err;
+
+       jazz16 = card->private_data;
+
+       xirq = irq[dev];
+       if (xirq == SNDRV_AUTO_IRQ) {
+               xirq = snd_legacy_find_free_irq(possible_irqs);
+               if (xirq < 0) {
+                       snd_printk(KERN_ERR "unable to find a free IRQ\n");
+                       err = -EBUSY;
+                       goto err_free;
+               }
+       }
+       xdma8 = dma8[dev];
+       if (xdma8 == SNDRV_AUTO_DMA) {
+               xdma8 = snd_legacy_find_free_dma(possible_dmas8);
+               if (xdma8 < 0) {
+                       snd_printk(KERN_ERR "unable to find a free DMA8\n");
+                       err = -EBUSY;
+                       goto err_free;
+               }
+       }
+       xdma16 = dma16[dev];
+       if (xdma16 == SNDRV_AUTO_DMA) {
+               xdma16 = snd_legacy_find_free_dma(possible_dmas16);
+               if (xdma16 < 0) {
+                       snd_printk(KERN_ERR "unable to find a free DMA16\n");
+                       err = -EBUSY;
+                       goto err_free;
+               }
+       }
+
+       xmpu_port = mpu_port[dev];
+       if (xmpu_port == SNDRV_AUTO_PORT)
+               xmpu_port = 0;
+       err = jazz16_detect_board(port[dev], xmpu_port);
+       if (err < 0) {
+               printk(KERN_ERR "Media Vision Jazz16 board not detected\n");
+               goto err_free;
+       }
+       err = snd_sbdsp_create(card, port[dev], irq[dev],
+                              jazz16_interrupt,
+                              dma8[dev], dma16[dev],
+                              SB_HW_JAZZ16,
+                              &chip);
+       if (err < 0)
+               goto err_free;
+
+       xmpu_irq = mpu_irq[dev];
+       if (xmpu_irq == SNDRV_AUTO_IRQ || mpu_port[dev] == SNDRV_AUTO_PORT)
+               xmpu_irq = 0;
+       err = jazz16_configure_board(chip, xmpu_irq);
+       if (err < 0) {
+               printk(KERN_ERR "Media Vision Jazz16 configuration failed\n");
+               goto err_free;
+       }
+
+       jazz16->chip = chip;
+
+       strcpy(card->driver, "jazz16");
+       strcpy(card->shortname, "Media Vision Jazz16");
+       sprintf(card->longname,
+               "Media Vision Jazz16 at 0x%lx, irq %d, dma8 %d, dma16 %d",
+               port[dev], xirq, xdma8, xdma16);
+
+       err = snd_sb8dsp_pcm(chip, 0, NULL);
+       if (err < 0)
+               goto err_free;
+       err = snd_sbmixer_new(chip);
+       if (err < 0)
+               goto err_free;
+
+       err = snd_opl3_create(card, chip->port, chip->port + 2,
+                             OPL3_HW_AUTO, 1, &opl3);
+       if (err < 0)
+               snd_printk(KERN_WARNING "no OPL device at 0x%lx-0x%lx\n",
+                          chip->port, chip->port + 2);
+       else {
+               err = snd_opl3_hwdep_new(opl3, 0, 1, NULL);
+               if (err < 0)
+                       goto err_free;
+       }
+       if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
+               if (mpu_irq[dev] == SNDRV_AUTO_IRQ)
+                       mpu_irq[dev] = -1;
+
+               if (snd_mpu401_uart_new(card, 0,
+                                       MPU401_HW_MPU401,
+                                       mpu_port[dev], 0,
+                                       mpu_irq[dev],
+                                       mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
+                                       NULL) < 0)
+                       snd_printk(KERN_ERR "no MPU-401 device at 0x%lx\n",
+                                       mpu_port[dev]);
+       }
+
+       snd_card_set_dev(card, devptr);
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto err_free;
+
+       dev_set_drvdata(devptr, card);
+       return 0;
+
+err_free:
+       snd_card_free(card);
+       return err;
+}
+
+static int __devexit snd_jazz16_remove(struct device *devptr, unsigned int dev)
+{
+       struct snd_card *card = dev_get_drvdata(devptr);
+
+       dev_set_drvdata(devptr, NULL);
+       snd_card_free(card);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int snd_jazz16_suspend(struct device *pdev, unsigned int n,
+                              pm_message_t state)
+{
+       struct snd_card *card = dev_get_drvdata(pdev);
+       struct snd_card_jazz16 *acard = card->private_data;
+       struct snd_sb *chip = acard->chip;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       snd_pcm_suspend_all(chip->pcm);
+       snd_sbmixer_suspend(chip);
+       return 0;
+}
+
+static int snd_jazz16_resume(struct device *pdev, unsigned int n)
+{
+       struct snd_card *card = dev_get_drvdata(pdev);
+       struct snd_card_jazz16 *acard = card->private_data;
+       struct snd_sb *chip = acard->chip;
+
+       snd_sbdsp_reset(chip);
+       snd_sbmixer_resume(chip);
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+       return 0;
+}
+#endif
+
+static struct isa_driver snd_jazz16_driver = {
+       .match          = snd_jazz16_match,
+       .probe          = snd_jazz16_probe,
+       .remove         = __devexit_p(snd_jazz16_remove),
+#ifdef CONFIG_PM
+       .suspend        = snd_jazz16_suspend,
+       .resume         = snd_jazz16_resume,
+#endif
+       .driver         = {
+               .name   = "jazz16"
+       },
+};
+
+static int __init alsa_card_jazz16_init(void)
+{
+       return isa_register_driver(&snd_jazz16_driver, SNDRV_CARDS);
+}
+
+static void __exit alsa_card_jazz16_exit(void)
+{
+       isa_unregister_driver(&snd_jazz16_driver);
+}
+
+module_init(alsa_card_jazz16_init)
+module_exit(alsa_card_jazz16_exit)
index 658d557..7d84c9f 100644 (file)
@@ -106,9 +106,21 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
        struct snd_sb *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
        unsigned int mixreg, rate, size, count;
+       unsigned char format;
+       unsigned char stereo = runtime->channels > 1;
+       int dma;
 
        rate = runtime->rate;
        switch (chip->hardware) {
+       case SB_HW_JAZZ16:
+               if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
+                       if (chip->mode & SB_MODE_CAPTURE_16)
+                               return -EBUSY;
+                       else
+                               chip->mode |= SB_MODE_PLAYBACK_16;
+               }
+               chip->playback_format = SB_DSP_LO_OUTPUT_AUTO;
+               break;
        case SB_HW_PRO:
                if (runtime->channels > 1) {
                        if (snd_BUG_ON(rate != SB8_RATE(11025) &&
@@ -133,11 +145,21 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
        default:
                return -EINVAL;
        }
+       if (chip->mode & SB_MODE_PLAYBACK_16) {
+               format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT;
+               dma = chip->dma16;
+       } else {
+               format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT;
+               chip->mode |= SB_MODE_PLAYBACK_8;
+               dma = chip->dma8;
+       }
        size = chip->p_dma_size = snd_pcm_lib_buffer_bytes(substream);
        count = chip->p_period_size = snd_pcm_lib_period_bytes(substream);
        spin_lock_irqsave(&chip->reg_lock, flags);
        snd_sbdsp_command(chip, SB_DSP_SPEAKER_ON);
-       if (runtime->channels > 1) {
+       if (chip->hardware == SB_HW_JAZZ16)
+               snd_sbdsp_command(chip, format);
+       else if (stereo) {
                /* set playback stereo mode */
                spin_lock(&chip->mixer_lock);
                mixreg = snd_sbmixer_read(chip, SB_DSP_STEREO_SW);
@@ -147,15 +169,14 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
                /* Soundblaster hardware programming reference guide, 3-23 */
                snd_sbdsp_command(chip, SB_DSP_DMA8_EXIT);
                runtime->dma_area[0] = 0x80;
-               snd_dma_program(chip->dma8, runtime->dma_addr, 1, DMA_MODE_WRITE);
+               snd_dma_program(dma, runtime->dma_addr, 1, DMA_MODE_WRITE);
                /* force interrupt */
-               chip->mode = SB_MODE_HALT;
                snd_sbdsp_command(chip, SB_DSP_OUTPUT);
                snd_sbdsp_command(chip, 0);
                snd_sbdsp_command(chip, 0);
        }
        snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
-       if (runtime->channels > 1) {
+       if (stereo) {
                snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
                spin_lock(&chip->mixer_lock);
                /* save output filter status and turn it off */
@@ -168,13 +189,15 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream)
                snd_sbdsp_command(chip, 256 - runtime->rate_den);
        }
        if (chip->playback_format != SB_DSP_OUTPUT) {
+               if (chip->mode & SB_MODE_PLAYBACK_16)
+                       count /= 2;
                count--;
                snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
                snd_sbdsp_command(chip, count & 0xff);
                snd_sbdsp_command(chip, count >> 8);
        }
        spin_unlock_irqrestore(&chip->reg_lock, flags);
-       snd_dma_program(chip->dma8, runtime->dma_addr,
+       snd_dma_program(dma, runtime->dma_addr,
                        size, DMA_MODE_WRITE | DMA_AUTOINIT);
        return 0;
 }
@@ -212,7 +235,6 @@ static int snd_sb8_playback_trigger(struct snd_pcm_substream *substream,
                snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
        }
        spin_unlock_irqrestore(&chip->reg_lock, flags);
-       chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_PLAYBACK_8 : SB_MODE_HALT;
        return 0;
 }
 
@@ -234,9 +256,21 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
        struct snd_sb *chip = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
        unsigned int mixreg, rate, size, count;
+       unsigned char format;
+       unsigned char stereo = runtime->channels > 1;
+       int dma;
 
        rate = runtime->rate;
        switch (chip->hardware) {
+       case SB_HW_JAZZ16:
+               if (runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
+                       if (chip->mode & SB_MODE_PLAYBACK_16)
+                               return -EBUSY;
+                       else
+                               chip->mode |= SB_MODE_CAPTURE_16;
+               }
+               chip->capture_format = SB_DSP_LO_INPUT_AUTO;
+               break;
        case SB_HW_PRO:
                if (runtime->channels > 1) {
                        if (snd_BUG_ON(rate != SB8_RATE(11025) &&
@@ -262,14 +296,24 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
        default:
                return -EINVAL;
        }
+       if (chip->mode & SB_MODE_CAPTURE_16) {
+               format = stereo ? SB_DSP_STEREO_16BIT : SB_DSP_MONO_16BIT;
+               dma = chip->dma16;
+       } else {
+               format = stereo ? SB_DSP_STEREO_8BIT : SB_DSP_MONO_8BIT;
+               chip->mode |= SB_MODE_CAPTURE_8;
+               dma = chip->dma8;
+       }
        size = chip->c_dma_size = snd_pcm_lib_buffer_bytes(substream);
        count = chip->c_period_size = snd_pcm_lib_period_bytes(substream);
        spin_lock_irqsave(&chip->reg_lock, flags);
        snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
-       if (runtime->channels > 1)
+       if (chip->hardware == SB_HW_JAZZ16)
+               snd_sbdsp_command(chip, format);
+       else if (stereo)
                snd_sbdsp_command(chip, SB_DSP_STEREO_8BIT);
        snd_sbdsp_command(chip, SB_DSP_SAMPLE_RATE);
-       if (runtime->channels > 1) {
+       if (stereo) {
                snd_sbdsp_command(chip, 256 - runtime->rate_den / 2);
                spin_lock(&chip->mixer_lock);
                /* save input filter status and turn it off */
@@ -282,13 +326,15 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream)
                snd_sbdsp_command(chip, 256 - runtime->rate_den);
        }
        if (chip->capture_format != SB_DSP_INPUT) {
+               if (chip->mode & SB_MODE_PLAYBACK_16)
+                       count /= 2;
                count--;
                snd_sbdsp_command(chip, SB_DSP_BLOCK_SIZE);
                snd_sbdsp_command(chip, count & 0xff);
                snd_sbdsp_command(chip, count >> 8);
        }
        spin_unlock_irqrestore(&chip->reg_lock, flags);
-       snd_dma_program(chip->dma8, runtime->dma_addr,
+       snd_dma_program(dma, runtime->dma_addr,
                        size, DMA_MODE_READ | DMA_AUTOINIT);
        return 0;
 }
@@ -328,7 +374,6 @@ static int snd_sb8_capture_trigger(struct snd_pcm_substream *substream,
                snd_sbdsp_command(chip, SB_DSP_SPEAKER_OFF);
        }
        spin_unlock_irqrestore(&chip->reg_lock, flags);
-       chip->mode = (cmd == SNDRV_PCM_TRIGGER_START) ? SB_MODE_CAPTURE_8 : SB_MODE_HALT;
        return 0;
 }
 
@@ -339,13 +384,21 @@ irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip)
 
        snd_sb_ack_8bit(chip);
        switch (chip->mode) {
-       case SB_MODE_PLAYBACK_8:        /* ok.. playback is active */
+       case SB_MODE_PLAYBACK_16:       /* ok.. playback is active */
+               if (chip->hardware != SB_HW_JAZZ16)
+                       break;
+               /* fallthru */
+       case SB_MODE_PLAYBACK_8:
                substream = chip->playback_substream;
                runtime = substream->runtime;
                if (chip->playback_format == SB_DSP_OUTPUT)
                        snd_sb8_playback_trigger(substream, SNDRV_PCM_TRIGGER_START);
                snd_pcm_period_elapsed(substream);
                break;
+       case SB_MODE_CAPTURE_16:
+               if (chip->hardware != SB_HW_JAZZ16)
+                       break;
+               /* fallthru */
        case SB_MODE_CAPTURE_8:
                substream = chip->capture_substream;
                runtime = substream->runtime;
@@ -361,10 +414,15 @@ static snd_pcm_uframes_t snd_sb8_playback_pointer(struct snd_pcm_substream *subs
 {
        struct snd_sb *chip = snd_pcm_substream_chip(substream);
        size_t ptr;
+       int dma;
 
-       if (chip->mode != SB_MODE_PLAYBACK_8)
+       if (chip->mode & SB_MODE_PLAYBACK_8)
+               dma = chip->dma8;
+       else if (chip->mode & SB_MODE_PLAYBACK_16)
+               dma = chip->dma16;
+       else
                return 0;
-       ptr = snd_dma_pointer(chip->dma8, chip->p_dma_size);
+       ptr = snd_dma_pointer(dma, chip->p_dma_size);
        return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -372,10 +430,15 @@ static snd_pcm_uframes_t snd_sb8_capture_pointer(struct snd_pcm_substream *subst
 {
        struct snd_sb *chip = snd_pcm_substream_chip(substream);
        size_t ptr;
+       int dma;
 
-       if (chip->mode != SB_MODE_CAPTURE_8)
+       if (chip->mode & SB_MODE_CAPTURE_8)
+               dma = chip->dma8;
+       else if (chip->mode & SB_MODE_CAPTURE_16)
+               dma = chip->dma16;
+       else
                return 0;
-       ptr = snd_dma_pointer(chip->dma8, chip->c_dma_size);
+       ptr = snd_dma_pointer(dma, chip->c_dma_size);
        return bytes_to_frames(substream->runtime, ptr);
 }
 
@@ -446,6 +509,14 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
                runtime->hw = snd_sb8_capture;
        }
        switch (chip->hardware) {
+       case SB_HW_JAZZ16:
+               if (chip->dma16 == 5 || chip->dma16 == 7)
+                       runtime->hw.formats |= SNDRV_PCM_FMTBIT_S16_LE;
+               runtime->hw.rates |= SNDRV_PCM_RATE_8000_48000;
+               runtime->hw.rate_min = 4000;
+               runtime->hw.rate_max = 50000;
+               runtime->hw.channels_max = 2;
+               break;
        case SB_HW_PRO:
                runtime->hw.rate_max = 44100;
                runtime->hw.channels_max = 2;
@@ -468,6 +539,14 @@ static int snd_sb8_open(struct snd_pcm_substream *substream)
        }
        snd_pcm_hw_constraint_ratnums(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
                                      &hw_constraints_clock);
+       if (chip->dma8 > 3 || chip->dma16 >= 0) {
+               snd_pcm_hw_constraint_step(runtime, 0,
+                                          SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 2);
+               snd_pcm_hw_constraint_step(runtime, 0,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 2);
+               runtime->hw.buffer_bytes_max = 128 * 1024 * 1024;
+               runtime->hw.period_bytes_max = 128 * 1024 * 1024;
+       }
        return 0;       
 }
 
@@ -480,6 +559,10 @@ static int snd_sb8_close(struct snd_pcm_substream *substream)
        chip->capture_substream = NULL;
        spin_lock_irqsave(&chip->open_lock, flags);
        chip->open &= ~SB_OPEN_PCM;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               chip->mode &= ~SB_MODE_PLAYBACK;
+       else
+               chip->mode &= ~SB_MODE_CAPTURE;
        spin_unlock_irqrestore(&chip->open_lock, flags);
        return 0;
 }
@@ -515,6 +598,7 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm)
        struct snd_card *card = chip->card;
        struct snd_pcm *pcm;
        int err;
+       size_t max_prealloc = 64 * 1024;
 
        if (rpcm)
                *rpcm = NULL;
@@ -527,9 +611,11 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device, struct snd_pcm ** rpcm)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb8_playback_ops);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb8_capture_ops);
 
+       if (chip->dma8 > 3 || chip->dma16 >= 0)
+               max_prealloc = 128 * 1024;
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                              snd_dma_isa_data(),
-                                             64*1024, 64*1024);
+                                             64*1024, max_prealloc);
 
        if (rpcm)
                *rpcm = pcm;
index 27a6515..eae6c1c 100644 (file)
@@ -170,6 +170,9 @@ static int snd_sbdsp_probe(struct snd_sb * chip)
        case SB_HW_CS5530:
                str = "16 (CS5530)";
                break;
+       case SB_HW_JAZZ16:
+               str = "Pro (Jazz16)";
+               break;
        default:
                return -ENODEV;
        }
index 318ff0c..6496822 100644 (file)
@@ -528,20 +528,11 @@ int snd_sbmixer_add_ctl(struct snd_sb *chip, const char *name, int index, int ty
  * SB 2.0 specific mixer elements
  */
 
-static struct sbmix_elem snd_sb20_ctl_master_play_vol =
-       SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7);
-static struct sbmix_elem snd_sb20_ctl_pcm_play_vol =
-       SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3);
-static struct sbmix_elem snd_sb20_ctl_synth_play_vol =
-       SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7);
-static struct sbmix_elem snd_sb20_ctl_cd_play_vol =
-       SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7);
-
-static struct sbmix_elem *snd_sb20_controls[] = {
-       &snd_sb20_ctl_master_play_vol,
-       &snd_sb20_ctl_pcm_play_vol,
-       &snd_sb20_ctl_synth_play_vol,
-       &snd_sb20_ctl_cd_play_vol
+static struct sbmix_elem snd_sb20_controls[] = {
+       SB_SINGLE("Master Playback Volume", SB_DSP20_MASTER_DEV, 1, 7),
+       SB_SINGLE("PCM Playback Volume", SB_DSP20_PCM_DEV, 1, 3),
+       SB_SINGLE("Synth Playback Volume", SB_DSP20_FM_DEV, 1, 7),
+       SB_SINGLE("CD Playback Volume", SB_DSP20_CD_DEV, 1, 7)
 };
 
 static unsigned char snd_sb20_init_values[][2] = {
@@ -552,41 +543,24 @@ static unsigned char snd_sb20_init_values[][2] = {
 /*
  * SB Pro specific mixer elements
  */
-static struct sbmix_elem snd_sbpro_ctl_master_play_vol =
-       SB_DOUBLE("Master Playback Volume", SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7);
-static struct sbmix_elem snd_sbpro_ctl_pcm_play_vol =
-       SB_DOUBLE("PCM Playback Volume", SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7);
-static struct sbmix_elem snd_sbpro_ctl_pcm_play_filter =
-       SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1);
-static struct sbmix_elem snd_sbpro_ctl_synth_play_vol =
-       SB_DOUBLE("Synth Playback Volume", SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7);
-static struct sbmix_elem snd_sbpro_ctl_cd_play_vol =
-       SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7);
-static struct sbmix_elem snd_sbpro_ctl_line_play_vol =
-       SB_DOUBLE("Line Playback Volume", SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7);
-static struct sbmix_elem snd_sbpro_ctl_mic_play_vol =
-       SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3);
-static struct sbmix_elem snd_sbpro_ctl_capture_source =
+static struct sbmix_elem snd_sbpro_controls[] = {
+       SB_DOUBLE("Master Playback Volume",
+                 SB_DSP_MASTER_DEV, SB_DSP_MASTER_DEV, 5, 1, 7),
+       SB_DOUBLE("PCM Playback Volume",
+                 SB_DSP_PCM_DEV, SB_DSP_PCM_DEV, 5, 1, 7),
+       SB_SINGLE("PCM Playback Filter", SB_DSP_PLAYBACK_FILT, 5, 1),
+       SB_DOUBLE("Synth Playback Volume",
+                 SB_DSP_FM_DEV, SB_DSP_FM_DEV, 5, 1, 7),
+       SB_DOUBLE("CD Playback Volume", SB_DSP_CD_DEV, SB_DSP_CD_DEV, 5, 1, 7),
+       SB_DOUBLE("Line Playback Volume",
+                 SB_DSP_LINE_DEV, SB_DSP_LINE_DEV, 5, 1, 7),
+       SB_SINGLE("Mic Playback Volume", SB_DSP_MIC_DEV, 1, 3),
        {
                .name = "Capture Source",
                .type = SB_MIX_CAPTURE_PRO
-       };
-static struct sbmix_elem snd_sbpro_ctl_capture_filter =
-       SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1);
-static struct sbmix_elem snd_sbpro_ctl_capture_low_filter =
-       SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1);
-
-static struct sbmix_elem *snd_sbpro_controls[] = {
-       &snd_sbpro_ctl_master_play_vol,
-       &snd_sbpro_ctl_pcm_play_vol,
-       &snd_sbpro_ctl_pcm_play_filter,
-       &snd_sbpro_ctl_synth_play_vol,
-       &snd_sbpro_ctl_cd_play_vol,
-       &snd_sbpro_ctl_line_play_vol,
-       &snd_sbpro_ctl_mic_play_vol,
-       &snd_sbpro_ctl_capture_source,
-       &snd_sbpro_ctl_capture_filter,
-       &snd_sbpro_ctl_capture_low_filter
+       },
+       SB_SINGLE("Capture Filter", SB_DSP_CAPTURE_FILT, 5, 1),
+       SB_SINGLE("Capture Low-Pass Filter", SB_DSP_CAPTURE_FILT, 3, 1)
 };
 
 static unsigned char snd_sbpro_init_values[][2] = {
@@ -598,68 +572,42 @@ static unsigned char snd_sbpro_init_values[][2] = {
 /*
  * SB16 specific mixer elements
  */
-static struct sbmix_elem snd_sb16_ctl_master_play_vol =
-       SB_DOUBLE("Master Playback Volume", SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31);
-static struct sbmix_elem snd_sb16_ctl_3d_enhance_switch =
-       SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1);
-static struct sbmix_elem snd_sb16_ctl_tone_bass =
-       SB_DOUBLE("Tone Control - Bass", SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15);
-static struct sbmix_elem snd_sb16_ctl_tone_treble =
-       SB_DOUBLE("Tone Control - Treble", SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15);
-static struct sbmix_elem snd_sb16_ctl_pcm_play_vol =
-       SB_DOUBLE("PCM Playback Volume", SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31);
-static struct sbmix_elem snd_sb16_ctl_synth_capture_route =
-       SB16_INPUT_SW("Synth Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5);
-static struct sbmix_elem snd_sb16_ctl_synth_play_vol =
-       SB_DOUBLE("Synth Playback Volume", SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31);
-static struct sbmix_elem snd_sb16_ctl_cd_capture_route =
-       SB16_INPUT_SW("CD Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1);
-static struct sbmix_elem snd_sb16_ctl_cd_play_switch =
-       SB_DOUBLE("CD Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1);
-static struct sbmix_elem snd_sb16_ctl_cd_play_vol =
-       SB_DOUBLE("CD Playback Volume", SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31);
-static struct sbmix_elem snd_sb16_ctl_line_capture_route =
-       SB16_INPUT_SW("Line Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3);
-static struct sbmix_elem snd_sb16_ctl_line_play_switch =
-       SB_DOUBLE("Line Playback Switch", SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1);
-static struct sbmix_elem snd_sb16_ctl_line_play_vol =
-       SB_DOUBLE("Line Playback Volume", SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31);
-static struct sbmix_elem snd_sb16_ctl_mic_capture_route =
-       SB16_INPUT_SW("Mic Capture Route", SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0);
-static struct sbmix_elem snd_sb16_ctl_mic_play_switch =
-       SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1);
-static struct sbmix_elem snd_sb16_ctl_mic_play_vol =
-       SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31);
-static struct sbmix_elem snd_sb16_ctl_pc_speaker_vol =
-       SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3);
-static struct sbmix_elem snd_sb16_ctl_capture_vol =
-       SB_DOUBLE("Capture Volume", SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3);
-static struct sbmix_elem snd_sb16_ctl_play_vol =
-       SB_DOUBLE("Playback Volume", SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3);
-static struct sbmix_elem snd_sb16_ctl_auto_mic_gain =
-       SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1);
-
-static struct sbmix_elem *snd_sb16_controls[] = {
-       &snd_sb16_ctl_master_play_vol,
-       &snd_sb16_ctl_3d_enhance_switch,
-       &snd_sb16_ctl_tone_bass,
-       &snd_sb16_ctl_tone_treble,
-       &snd_sb16_ctl_pcm_play_vol,
-       &snd_sb16_ctl_synth_capture_route,
-       &snd_sb16_ctl_synth_play_vol,
-       &snd_sb16_ctl_cd_capture_route,
-       &snd_sb16_ctl_cd_play_switch,
-       &snd_sb16_ctl_cd_play_vol,
-       &snd_sb16_ctl_line_capture_route,
-       &snd_sb16_ctl_line_play_switch,
-       &snd_sb16_ctl_line_play_vol,
-       &snd_sb16_ctl_mic_capture_route,
-       &snd_sb16_ctl_mic_play_switch,
-       &snd_sb16_ctl_mic_play_vol,
-       &snd_sb16_ctl_pc_speaker_vol,
-       &snd_sb16_ctl_capture_vol,
-       &snd_sb16_ctl_play_vol,
-       &snd_sb16_ctl_auto_mic_gain
+static struct sbmix_elem snd_sb16_controls[] = {
+       SB_DOUBLE("Master Playback Volume",
+                 SB_DSP4_MASTER_DEV, (SB_DSP4_MASTER_DEV + 1), 3, 3, 31),
+       SB_DOUBLE("PCM Playback Volume",
+                 SB_DSP4_PCM_DEV, (SB_DSP4_PCM_DEV + 1), 3, 3, 31),
+       SB16_INPUT_SW("Synth Capture Route",
+                     SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 6, 5),
+       SB_DOUBLE("Synth Playback Volume",
+                 SB_DSP4_SYNTH_DEV, (SB_DSP4_SYNTH_DEV + 1), 3, 3, 31),
+       SB16_INPUT_SW("CD Capture Route",
+                     SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 2, 1),
+       SB_DOUBLE("CD Playback Switch",
+                 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
+       SB_DOUBLE("CD Playback Volume",
+                 SB_DSP4_CD_DEV, (SB_DSP4_CD_DEV + 1), 3, 3, 31),
+       SB16_INPUT_SW("Mic Capture Route",
+                     SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 0, 0),
+       SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
+       SB_SINGLE("Mic Playback Volume", SB_DSP4_MIC_DEV, 3, 31),
+       SB_SINGLE("Beep Volume", SB_DSP4_SPEAKER_DEV, 6, 3),
+       SB_DOUBLE("Capture Volume",
+                 SB_DSP4_IGAIN_DEV, (SB_DSP4_IGAIN_DEV + 1), 6, 6, 3),
+       SB_DOUBLE("Playback Volume",
+                 SB_DSP4_OGAIN_DEV, (SB_DSP4_OGAIN_DEV + 1), 6, 6, 3),
+       SB16_INPUT_SW("Line Capture Route",
+                     SB_DSP4_INPUT_LEFT, SB_DSP4_INPUT_RIGHT, 4, 3),
+       SB_DOUBLE("Line Playback Switch",
+                 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
+       SB_DOUBLE("Line Playback Volume",
+                 SB_DSP4_LINE_DEV, (SB_DSP4_LINE_DEV + 1), 3, 3, 31),
+       SB_SINGLE("Mic Auto Gain", SB_DSP4_MIC_AGC, 0, 1),
+       SB_SINGLE("3D Enhancement Switch", SB_DSP4_3DSE, 0, 1),
+       SB_DOUBLE("Tone Control - Bass",
+                 SB_DSP4_BASS_DEV, (SB_DSP4_BASS_DEV + 1), 4, 4, 15),
+       SB_DOUBLE("Tone Control - Treble",
+                 SB_DSP4_TREBLE_DEV, (SB_DSP4_TREBLE_DEV + 1), 4, 4, 15)
 };
 
 static unsigned char snd_sb16_init_values[][2] = {
@@ -678,46 +626,34 @@ static unsigned char snd_sb16_init_values[][2] = {
 /*
  * DT019x specific mixer elements
  */
-static struct sbmix_elem snd_dt019x_ctl_master_play_vol =
-       SB_DOUBLE("Master Playback Volume", SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4,0, 15);
-static struct sbmix_elem snd_dt019x_ctl_pcm_play_vol =
-       SB_DOUBLE("PCM Playback Volume", SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4,0, 15);
-static struct sbmix_elem snd_dt019x_ctl_synth_play_vol =
-       SB_DOUBLE("Synth Playback Volume", SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4,0, 15);
-static struct sbmix_elem snd_dt019x_ctl_cd_play_vol =
-       SB_DOUBLE("CD Playback Volume", SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4,0, 15);
-static struct sbmix_elem snd_dt019x_ctl_mic_play_vol =
-       SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7);
-static struct sbmix_elem snd_dt019x_ctl_pc_speaker_vol =
-       SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7);
-static struct sbmix_elem snd_dt019x_ctl_line_play_vol =
-       SB_DOUBLE("Line Playback Volume", SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4,0, 15);
-static struct sbmix_elem snd_dt019x_ctl_pcm_play_switch =
-       SB_DOUBLE("PCM Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2,1, 1);
-static struct sbmix_elem snd_dt019x_ctl_synth_play_switch =
-       SB_DOUBLE("Synth Playback Switch", SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4,3, 1);
-static struct sbmix_elem snd_dt019x_ctl_capture_source =
+static struct sbmix_elem snd_dt019x_controls[] = {
+       /* ALS4000 below has some parts which we might be lacking,
+        * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
+       SB_DOUBLE("Master Playback Volume",
+                 SB_DT019X_MASTER_DEV, SB_DT019X_MASTER_DEV, 4, 0, 15),
+       SB_DOUBLE("PCM Playback Switch",
+                 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
+       SB_DOUBLE("PCM Playback Volume",
+                 SB_DT019X_PCM_DEV, SB_DT019X_PCM_DEV, 4, 0, 15),
+       SB_DOUBLE("Synth Playback Switch",
+                 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
+       SB_DOUBLE("Synth Playback Volume",
+                 SB_DT019X_SYNTH_DEV, SB_DT019X_SYNTH_DEV, 4, 0, 15),
+       SB_DOUBLE("CD Playback Switch",
+                 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 2, 1, 1),
+       SB_DOUBLE("CD Playback Volume",
+                 SB_DT019X_CD_DEV, SB_DT019X_CD_DEV, 4, 0, 15),
+       SB_SINGLE("Mic Playback Switch", SB_DSP4_OUTPUT_SW, 0, 1),
+       SB_SINGLE("Mic Playback Volume", SB_DT019X_MIC_DEV, 4, 7),
+       SB_SINGLE("Beep Volume", SB_DT019X_SPKR_DEV, 0,  7),
+       SB_DOUBLE("Line Playback Switch",
+                 SB_DSP4_OUTPUT_SW, SB_DSP4_OUTPUT_SW, 4, 3, 1),
+       SB_DOUBLE("Line Playback Volume",
+                 SB_DT019X_LINE_DEV, SB_DT019X_LINE_DEV, 4, 0, 15),
        {
                .name = "Capture Source",
                .type = SB_MIX_CAPTURE_DT019X
-       };
-
-static struct sbmix_elem *snd_dt019x_controls[] = {
-       /* ALS4000 below has some parts which we might be lacking,
-        * e.g. snd_als4000_ctl_mono_playback_switch - check it! */
-       &snd_dt019x_ctl_master_play_vol,
-       &snd_dt019x_ctl_pcm_play_vol,
-       &snd_dt019x_ctl_synth_play_vol,
-       &snd_dt019x_ctl_cd_play_vol,
-       &snd_dt019x_ctl_mic_play_vol,
-       &snd_dt019x_ctl_pc_speaker_vol,
-       &snd_dt019x_ctl_line_play_vol,
-       &snd_sb16_ctl_mic_play_switch,
-       &snd_sb16_ctl_cd_play_switch,
-       &snd_sb16_ctl_line_play_switch,
-       &snd_dt019x_ctl_pcm_play_switch,
-       &snd_dt019x_ctl_synth_play_switch,
-       &snd_dt019x_ctl_capture_source
+       }
 };
 
 static unsigned char snd_dt019x_init_values[][2] = {
@@ -735,82 +671,37 @@ static unsigned char snd_dt019x_init_values[][2] = {
 /*
  * ALS4000 specific mixer elements
  */
-static struct sbmix_elem snd_als4000_ctl_master_mono_playback_switch =
-       SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1);
-static struct sbmix_elem snd_als4k_ctl_master_mono_capture_route = {
+static struct sbmix_elem snd_als4000_controls[] = {
+       SB_DOUBLE("PCM Playback Switch",
+                 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 2, 1, 1),
+       SB_DOUBLE("Synth Playback Switch",
+                 SB_DT019X_OUTPUT_SW2, SB_DT019X_OUTPUT_SW2, 4, 3, 1),
+       SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03),
+       SB_SINGLE("Master Mono Playback Switch", SB_ALS4000_MONO_IO_CTRL, 5, 1),
+       {
                .name = "Master Mono Capture Route",
                .type = SB_MIX_MONO_CAPTURE_ALS4K
-       };
-static struct sbmix_elem snd_als4000_ctl_mono_playback_switch =
-       SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1);
-static struct sbmix_elem snd_als4000_ctl_mic_20db_boost =
-       SB_SINGLE("Mic Boost (+20dB)", SB_ALS4000_MIC_IN_GAIN, 0, 0x03);
-static struct sbmix_elem snd_als4000_ctl_mixer_analog_loopback =
-       SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01);
-static struct sbmix_elem snd_als4000_ctl_mixer_digital_loopback =
+       },
+       SB_SINGLE("Mono Playback Switch", SB_DT019X_OUTPUT_SW2, 0, 1),
+       SB_SINGLE("Analog Loopback Switch", SB_ALS4000_MIC_IN_GAIN, 7, 0x01),
+       SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01),
        SB_SINGLE("Digital Loopback Switch",
-                 SB_ALS4000_CR3_CONFIGURATION, 7, 0x01);
-/* FIXME: functionality of 3D controls might be swapped, I didn't find
- * a description of how to identify what is supposed to be what */
-static struct sbmix_elem snd_als4000_3d_control_switch =
-       SB_SINGLE("3D Control - Switch", SB_ALS4000_3D_SND_FX, 6, 0x01);
-static struct sbmix_elem snd_als4000_3d_control_ratio =
-       SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07);
-static struct sbmix_elem snd_als4000_3d_control_freq =
+                 SB_ALS4000_CR3_CONFIGURATION, 7, 0x01),
+       /* FIXME: functionality of 3D controls might be swapped, I didn't find
+        * a description of how to identify what is supposed to be what */
+       SB_SINGLE("3D Control - Level", SB_ALS4000_3D_SND_FX, 0, 0x07),
        /* FIXME: maybe there's actually some standard 3D ctrl name for it?? */
-       SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03);
-static struct sbmix_elem snd_als4000_3d_control_delay =
+       SB_SINGLE("3D Control - Freq", SB_ALS4000_3D_SND_FX, 4, 0x03),
        /* FIXME: ALS4000a.pdf mentions BBD (Bucket Brigade Device) time delay,
         * but what ALSA 3D attribute is that actually? "Center", "Depth",
         * "Wide" or "Space" or even "Level"? Assuming "Wide" for now... */
-       SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f);
-static struct sbmix_elem snd_als4000_3d_control_poweroff_switch =
-       SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01);
-static struct sbmix_elem snd_als4000_ctl_3db_freq_control_switch =
+       SB_SINGLE("3D Control - Wide", SB_ALS4000_3D_TIME_DELAY, 0, 0x0f),
+       SB_SINGLE("3D PowerOff Switch", SB_ALS4000_3D_TIME_DELAY, 4, 0x01),
        SB_SINGLE("Master Playback 8kHz / 20kHz LPF Switch",
-                 SB_ALS4000_FMDAC, 5, 0x01);
+                 SB_ALS4000_FMDAC, 5, 0x01),
 #ifdef NOT_AVAILABLE
-static struct sbmix_elem snd_als4000_ctl_fmdac =
-       SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01);
-static struct sbmix_elem snd_als4000_ctl_qsound =
-       SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f);
-#endif
-
-static struct sbmix_elem *snd_als4000_controls[] = {
-                                               /* ALS4000a.PDF regs page */
-       &snd_sb16_ctl_master_play_vol,          /* MX30/31 12 */
-       &snd_dt019x_ctl_pcm_play_switch,        /* MX4C    16 */
-       &snd_sb16_ctl_pcm_play_vol,             /* MX32/33 12 */
-       &snd_sb16_ctl_synth_capture_route,      /* MX3D/3E 14 */
-       &snd_dt019x_ctl_synth_play_switch,      /* MX4C    16 */
-       &snd_sb16_ctl_synth_play_vol,           /* MX34/35 12/13 */
-       &snd_sb16_ctl_cd_capture_route,         /* MX3D/3E 14 */
-       &snd_sb16_ctl_cd_play_switch,           /* MX3C    14 */
-       &snd_sb16_ctl_cd_play_vol,              /* MX36/37 13 */
-       &snd_sb16_ctl_line_capture_route,       /* MX3D/3E 14 */
-       &snd_sb16_ctl_line_play_switch,         /* MX3C    14 */
-       &snd_sb16_ctl_line_play_vol,            /* MX38/39 13 */
-       &snd_sb16_ctl_mic_capture_route,        /* MX3D/3E 14 */
-       &snd_als4000_ctl_mic_20db_boost,        /* MX4D    16 */
-       &snd_sb16_ctl_mic_play_switch,          /* MX3C    14 */
-       &snd_sb16_ctl_mic_play_vol,             /* MX3A    13 */
-       &snd_sb16_ctl_pc_speaker_vol,           /* MX3B    14 */
-       &snd_sb16_ctl_capture_vol,              /* MX3F/40 15 */
-       &snd_sb16_ctl_play_vol,                 /* MX41/42 15 */
-       &snd_als4000_ctl_master_mono_playback_switch, /* MX4C 16 */
-       &snd_als4k_ctl_master_mono_capture_route, /* MX4B  16 */
-       &snd_als4000_ctl_mono_playback_switch,  /* MX4C    16 */
-       &snd_als4000_ctl_mixer_analog_loopback, /* MX4D    16 */
-       &snd_als4000_ctl_mixer_digital_loopback, /* CR3    21 */
-       &snd_als4000_3d_control_switch,          /* MX50   17 */
-       &snd_als4000_3d_control_ratio,           /* MX50   17 */
-       &snd_als4000_3d_control_freq,            /* MX50   17 */
-       &snd_als4000_3d_control_delay,           /* MX51   18 */
-       &snd_als4000_3d_control_poweroff_switch,        /* MX51    18 */
-       &snd_als4000_ctl_3db_freq_control_switch,       /* MX4F    17 */
-#ifdef NOT_AVAILABLE
-       &snd_als4000_ctl_fmdac,
-       &snd_als4000_ctl_qsound,
+       SB_SINGLE("FMDAC Switch (Option ?)", SB_ALS4000_FMDAC, 0, 0x01),
+       SB_SINGLE("QSound Mode", SB_ALS4000_QSOUND, 1, 0x1f),
 #endif
 };
 
@@ -829,11 +720,10 @@ static unsigned char snd_als4000_init_values[][2] = {
        { SB_ALS4000_MIC_IN_GAIN, 0 },
 };
 
-
 /*
  */
 static int snd_sbmixer_init(struct snd_sb *chip,
-                           struct sbmix_elem **controls,
+                           struct sbmix_elem *controls,
                            int controls_count,
                            unsigned char map[][2],
                            int map_count,
@@ -856,7 +746,8 @@ static int snd_sbmixer_init(struct snd_sb *chip,
        }
 
        for (idx = 0; idx < controls_count; idx++) {
-               if ((err = snd_sbmixer_add_ctl_elem(chip, controls[idx])) < 0)
+               err = snd_sbmixer_add_ctl_elem(chip, &controls[idx]);
+               if (err < 0)
                        return err;
        }
        snd_component_add(card, name);
@@ -888,6 +779,7 @@ int snd_sbmixer_new(struct snd_sb *chip)
                        return err;
                break;
        case SB_HW_PRO:
+       case SB_HW_JAZZ16:
                if ((err = snd_sbmixer_init(chip,
                                            snd_sbpro_controls,
                                            ARRAY_SIZE(snd_sbpro_controls),
@@ -908,6 +800,15 @@ int snd_sbmixer_new(struct snd_sb *chip)
                        return err;
                break;
        case SB_HW_ALS4000:
+               /* use only the first 16 controls from SB16 */
+               err = snd_sbmixer_init(chip,
+                                       snd_sb16_controls,
+                                       16,
+                                       snd_sb16_init_values,
+                                       ARRAY_SIZE(snd_sb16_init_values),
+                                       "ALS4000");
+               if (err < 0)
+                       return err;
                if ((err = snd_sbmixer_init(chip,
                                            snd_als4000_controls,
                                            ARRAY_SIZE(snd_als4000_controls),
@@ -1029,6 +930,7 @@ void snd_sbmixer_suspend(struct snd_sb *chip)
                save_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
                break;
        case SB_HW_PRO:
+       case SB_HW_JAZZ16:
                save_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
                break;
        case SB_HW_16:
@@ -1055,6 +957,7 @@ void snd_sbmixer_resume(struct snd_sb *chip)
                restore_mixer(chip, sb20_saved_regs, ARRAY_SIZE(sb20_saved_regs));
                break;
        case SB_HW_PRO:
+       case SB_HW_JAZZ16:
                restore_mixer(chip, sbpro_saved_regs, ARRAY_SIZE(sbpro_saved_regs));
                break;
        case SB_HW_16:
index 5b9d6c1..9191b32 100644 (file)
@@ -2014,6 +2014,7 @@ static int snd_wss_info_mux(struct snd_kcontrol *kcontrol,
        case WSS_HW_INTERWAVE:
                ptexts = gusmax_texts;
                break;
+       case WSS_HW_OPTI93X:
        case WSS_HW_OPL3SA2:
                ptexts = opl3sa_texts;
                break;
@@ -2246,54 +2247,12 @@ WSS_SINGLE("Beep Bypass Playback Switch", 0,
                CS4231_MONO_CTRL, 5, 1, 0),
 };
 
-static struct snd_kcontrol_new snd_opti93x_controls[] = {
-WSS_DOUBLE("Master Playback Switch", 0,
-               OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1),
-WSS_DOUBLE_TLV("Master Playback Volume", 0,
-               OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1,
-               db_scale_6bit),
-WSS_DOUBLE("PCM Playback Switch", 0,
-               CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1),
-WSS_DOUBLE("PCM Playback Volume", 0,
-               CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1),
-WSS_DOUBLE("FM Playback Switch", 0,
-               CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("FM Playback Volume", 0,
-               CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1),
-WSS_DOUBLE("Line Playback Switch", 0,
-               CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1),
-WSS_DOUBLE("Line Playback Volume", 0,
-               CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1),
-WSS_DOUBLE("Mic Playback Switch", 0,
-               OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Mic Playback Volume", 0,
-               OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1),
-WSS_DOUBLE("Mic Boost", 0,
-               CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0),
-WSS_DOUBLE("CD Playback Switch", 0,
-               CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("CD Playback Volume", 0,
-               CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1),
-WSS_DOUBLE("Aux Playback Switch", 0,
-               OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1),
-WSS_DOUBLE("Aux Playback Volume", 0,
-               OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1),
-WSS_DOUBLE("Capture Volume", 0,
-               CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0),
-{
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "Capture Source",
-       .info = snd_wss_info_mux,
-       .get = snd_wss_get_mux,
-       .put = snd_wss_put_mux,
-}
-};
-
 int snd_wss_mixer(struct snd_wss *chip)
 {
        struct snd_card *card;
        unsigned int idx;
        int err;
+       int count = ARRAY_SIZE(snd_wss_controls);
 
        if (snd_BUG_ON(!chip || !chip->pcm))
                return -EINVAL;
@@ -2302,28 +2261,19 @@ int snd_wss_mixer(struct snd_wss *chip)
 
        strcpy(card->mixername, chip->pcm->name);
 
-       if (chip->hardware == WSS_HW_OPTI93X)
-               for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) {
-                       err = snd_ctl_add(card,
-                                       snd_ctl_new1(&snd_opti93x_controls[idx],
-                                                    chip));
-                       if (err < 0)
-                               return err;
-               }
-       else {
-               int count = ARRAY_SIZE(snd_wss_controls);
-
-               /* Use only the first 11 entries on AD1848 */
-               if (chip->hardware & WSS_HW_AD1848_MASK)
-                       count = 11;
-
-               for (idx = 0; idx < count; idx++) {
-                       err = snd_ctl_add(card,
-                                       snd_ctl_new1(&snd_wss_controls[idx],
-                                                    chip));
-                       if (err < 0)
-                               return err;
-               }
+       /* Use only the first 11 entries on AD1848 */
+       if (chip->hardware & WSS_HW_AD1848_MASK)
+               count = 11;
+       /* There is no loopback on OPTI93X */
+       else if (chip->hardware == WSS_HW_OPTI93X)
+               count = 9;
+
+       for (idx = 0; idx < count; idx++) {
+               err = snd_ctl_add(card,
+                               snd_ctl_new1(&snd_wss_controls[idx],
+                                            chip));
+               if (err < 0)
+                       return err;
        }
        return 0;
 }
index f1d9d16..6aff217 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/delay.h>
 #include <linux/spinlock.h>
 #include <linux/gfp.h>
-#include <linux/vmalloc.h>
 #include <linux/interrupt.h>
 #include <linux/dma-mapping.h>
 #include <linux/platform_device.h>
@@ -603,25 +602,14 @@ static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream)
 static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream,
                                        struct snd_pcm_hw_params *hw_params)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       int size = params_buffer_bytes(hw_params);
-
-       /* alloc virtual 'dma' area */
-       if (runtime->dma_area)
-               vfree(runtime->dma_area);
-       runtime->dma_area = vmalloc_user(size);
-       if (runtime->dma_area == NULL)
-               return -ENOMEM;
-       runtime->dma_bytes = size;
-       return 0;
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
 }
 
 /* hw_free callback */
 static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream)
 {
-       vfree(substream->runtime->dma_area);
-       substream->runtime->dma_area = NULL;
-       return 0;
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
 /* prepare callback */
@@ -692,13 +680,6 @@ snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream)
                               chip->channel[chan->idx].pos);
 }
 
-/* get the physical page pointer on the given offset */
-static struct page *snd_sgio2audio_page(struct snd_pcm_substream *substream,
-                                       unsigned long offset)
-{
-       return vmalloc_to_page(substream->runtime->dma_area + offset);
-}
-
 /* operators */
 static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
        .open =        snd_sgio2audio_playback1_open,
@@ -709,7 +690,8 @@ static struct snd_pcm_ops snd_sgio2audio_playback1_ops = {
        .prepare =     snd_sgio2audio_pcm_prepare,
        .trigger =     snd_sgio2audio_pcm_trigger,
        .pointer =     snd_sgio2audio_pcm_pointer,
-       .page =        snd_sgio2audio_page,
+       .page =        snd_pcm_lib_get_vmalloc_page,
+       .mmap =        snd_pcm_lib_mmap_vmalloc,
 };
 
 static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
@@ -721,7 +703,8 @@ static struct snd_pcm_ops snd_sgio2audio_playback2_ops = {
        .prepare =     snd_sgio2audio_pcm_prepare,
        .trigger =     snd_sgio2audio_pcm_trigger,
        .pointer =     snd_sgio2audio_pcm_pointer,
-       .page =        snd_sgio2audio_page,
+       .page =        snd_pcm_lib_get_vmalloc_page,
+       .mmap =        snd_pcm_lib_mmap_vmalloc,
 };
 
 static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
@@ -733,7 +716,8 @@ static struct snd_pcm_ops snd_sgio2audio_capture_ops = {
        .prepare =     snd_sgio2audio_pcm_prepare,
        .trigger =     snd_sgio2audio_pcm_trigger,
        .pointer =     snd_sgio2audio_pcm_pointer,
-       .page =        snd_sgio2audio_page,
+       .page =        snd_pcm_lib_get_vmalloc_page,
+       .mmap =        snd_pcm_lib_mmap_vmalloc,
 };
 
 /*
index 89466b0..24d152c 100644 (file)
@@ -198,7 +198,7 @@ MODULE_LICENSE("GPL");
  *     5530 only. The 5510/5520 decode is different.
  */
 
-static struct pci_device_id id_tbl[] = {
+static DEFINE_PCI_DEVICE_TABLE(id_tbl) = {
        { PCI_VDEVICE(CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO), 0 },
        { }
 };
index c625309..fde7c12 100644 (file)
@@ -328,11 +328,11 @@ static int sound_mixer_ioctl(int mixdev, unsigned int cmd, void __user *arg)
        return mixer_devs[mixdev]->ioctl(mixdev, cmd, arg);
 }
 
-static int sound_ioctl(struct inode *inode, struct file *file,
-                      unsigned int cmd, unsigned long arg)
+static long sound_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        int len = 0, dtype;
-       int dev = iminor(inode);
+       int dev = iminor(file->f_dentry->d_inode);
+       long ret = -EINVAL;
        void __user *p = (void __user *)arg;
 
        if (_SIOC_DIR(cmd) != _SIOC_NONE && _SIOC_DIR(cmd) != 0) {
@@ -353,6 +353,7 @@ static int sound_ioctl(struct inode *inode, struct file *file,
        if (cmd == OSS_GETVERSION)
                return __put_user(SOUND_VERSION, (int __user *)p);
        
+       lock_kernel();
        if (_IOC_TYPE(cmd) == 'M' && num_mixers > 0 &&   /* Mixer ioctl */
            (dev & 0x0f) != SND_DEV_CTL) {              
                dtype = dev & 0x0f;
@@ -360,24 +361,31 @@ static int sound_ioctl(struct inode *inode, struct file *file,
                case SND_DEV_DSP:
                case SND_DEV_DSP16:
                case SND_DEV_AUDIO:
-                       return sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
+                       ret = sound_mixer_ioctl(audio_devs[dev >> 4]->mixer_dev,
                                                 cmd, p);
-                       
+                       break;                  
                default:
-                       return sound_mixer_ioctl(dev >> 4, cmd, p);
+                       ret = sound_mixer_ioctl(dev >> 4, cmd, p);
+                       break;
                }
+               unlock_kernel();
+               return ret;
        }
+
        switch (dev & 0x0f) {
        case SND_DEV_CTL:
                if (cmd == SOUND_MIXER_GETLEVELS)
-                       return get_mixer_levels(p);
-               if (cmd == SOUND_MIXER_SETLEVELS)
-                       return set_mixer_levels(p);
-               return sound_mixer_ioctl(dev >> 4, cmd, p);
+                       ret = get_mixer_levels(p);
+               else if (cmd == SOUND_MIXER_SETLEVELS)
+                       ret = set_mixer_levels(p);
+               else
+                       ret = sound_mixer_ioctl(dev >> 4, cmd, p);
+               break;
 
        case SND_DEV_SEQ:
        case SND_DEV_SEQ2:
-               return sequencer_ioctl(dev, file, cmd, p);
+               ret = sequencer_ioctl(dev, file, cmd, p);
+               break;
 
        case SND_DEV_DSP:
        case SND_DEV_DSP16:
@@ -390,7 +398,8 @@ static int sound_ioctl(struct inode *inode, struct file *file,
                break;
 
        }
-       return -EINVAL;
+       unlock_kernel();
+       return ret;
 }
 
 static unsigned int sound_poll(struct file *file, poll_table * wait)
@@ -490,7 +499,7 @@ const struct file_operations oss_sound_fops = {
        .read           = sound_read,
        .write          = sound_write,
        .poll           = sound_poll,
-       .ioctl          = sound_ioctl,
+       .unlocked_ioctl = sound_ioctl,
        .mmap           = sound_mmap,
        .open           = sound_open,
        .release        = sound_release,
index 351654c..1298c68 100644 (file)
@@ -789,6 +789,7 @@ config SND_VIRTUOSO
          Say Y here to include support for sound cards based on the
          Asus AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X,
          Essence ST (Deluxe), and Essence STX.
+         Support for the DS is experimental.
          Support for the HDAV1.3 (Deluxe) is very experimental.
 
          To compile this driver as a module, choose M here: the module
index d9266ba..1caf5e3 100644 (file)
@@ -544,25 +544,10 @@ static int patch_wolfson04(struct snd_ac97 * ac97)
        return 0;
 }
 
-static int patch_wolfson_wm9705_specific(struct snd_ac97 * ac97)
-{
-       int err, i;
-       for (i = 0; i < ARRAY_SIZE(wm97xx_snd_ac97_controls); i++) {
-               if ((err = snd_ctl_add(ac97->bus->card, snd_ac97_cnew(&wm97xx_snd_ac97_controls[i], ac97))) < 0)
-                       return err;
-       }
-       snd_ac97_write_cache(ac97,  0x72, 0x0808);
-       return 0;
-}
-
-static struct snd_ac97_build_ops patch_wolfson_wm9705_ops = {
-       .build_specific = patch_wolfson_wm9705_specific,
-};
-
 static int patch_wolfson05(struct snd_ac97 * ac97)
 {
        /* WM9705, WM9710 */
-       ac97->build_ops = &patch_wolfson_wm9705_ops;
+       ac97->build_ops = &patch_wolfson_wm9703_ops;
 #ifdef CONFIG_TOUCHSCREEN_WM9705
        /* WM9705 touchscreen uses AUX and VIDEO for touch */
        ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
index 8f5098f..4382d0f 100644 (file)
@@ -1048,7 +1048,7 @@ snd_ad1889_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_device_id snd_ad1889_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_ad1889_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_ANALOG_DEVICES, PCI_DEVICE_ID_AD1889JS) },
        { 0, },
 };
index aaf4da6..5c6e322 100644 (file)
@@ -275,7 +275,7 @@ struct snd_ali {
 #endif
 };
 
-static struct pci_device_id snd_ali_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_ali_ids) = {
        {PCI_DEVICE(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5451), 0, 0, 0},
        {0, }
 };
index 3aa35af..d7653cb 100644 (file)
@@ -145,7 +145,7 @@ struct snd_als300_substream_data {
        int block_counter_register;
 };
 
-static struct pci_device_id snd_als300_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_als300_ids) = {
        { 0x4005, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300 },
        { 0x4005, 0x0308, PCI_ANY_ID, PCI_ANY_ID, 0, 0, DEVICE_ALS300_PLUS },
        { 0, }
index 3dbacde..d75cf7b 100644 (file)
@@ -117,7 +117,7 @@ struct snd_card_als4000 {
 #endif
 };
 
-static struct pci_device_id snd_als4000_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_als4000_ids) = {
        { 0x4005, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },   /* ALS4000 */
        { 0, }
 };
index 42b4fbb..49d572a 100644 (file)
@@ -286,7 +286,7 @@ struct atiixp {
 
 /*
  */
-static struct pci_device_id snd_atiixp_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
        { PCI_VDEVICE(ATI, 0x4341), 0 }, /* SB200 */
        { PCI_VDEVICE(ATI, 0x4361), 0 }, /* SB300 */
        { PCI_VDEVICE(ATI, 0x4370), 0 }, /* SB400 */
index e7e147b..91d7036 100644 (file)
@@ -261,7 +261,7 @@ struct atiixp_modem {
 
 /*
  */
-static struct pci_device_id snd_atiixp_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_atiixp_ids) = {
        { PCI_VDEVICE(ATI, 0x434d), 0 }, /* SB200 */
        { PCI_VDEVICE(ATI, 0x4378), 0 }, /* SB400 */
        { 0, }
index c0e8c6b..aa51cc7 100644 (file)
@@ -1,6 +1,6 @@
 #include "au8810.h"
 #include "au88x0.h"
-static struct pci_device_id snd_vortex_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
        {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_ADVANTAGE), 1,},
        {0,}
 };
index a652733..2f321e7 100644 (file)
@@ -1,6 +1,6 @@
 #include "au8820.h"
 #include "au88x0.h"
-static struct pci_device_id snd_vortex_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
        {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_1), 0,},
        {0,}
 };
index 6c702ad..279b78f 100644 (file)
@@ -1,6 +1,6 @@
 #include "au8830.h"
 #include "au88x0.h"
-static struct pci_device_id snd_vortex_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_vortex_ids) = {
        {PCI_VDEVICE(AUREAL, PCI_DEVICE_ID_AUREAL_VORTEX_2), 0,},
        {0,}
 };
index 4d34bb0..67921f9 100644 (file)
@@ -164,7 +164,7 @@ MODULE_PARM_DESC(id, "ID string for the Audiowerk2 soundcard.");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable Audiowerk2 soundcard.");
 
-static struct pci_device_id snd_aw2_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
        {PCI_VENDOR_ID_SAA7146, PCI_DEVICE_ID_SAA7146, 0, 0,
         0, 0, 0},
        {0}
index 69867ac..4679ed8 100644 (file)
@@ -350,7 +350,7 @@ struct snd_azf3328 {
 #endif
 };
 
-static const struct pci_device_id snd_azf3328_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_azf3328_ids) = {
        { 0x122D, 0x50DC, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* PCI168/3328 */
        { 0x122D, 0x80DA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },   /* 3328 */
        { 0, }
index 4e2b925..37e1b5d 100644 (file)
@@ -795,7 +795,7 @@ fail:
          .driver_data = SND_BT87X_BOARD_ ## id }
 /* driver_data is the card id for that device */
 
-static struct pci_device_id snd_bt87x_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_ids) = {
        /* Hauppauge WinTV series */
        BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, 0x0070, 0x13eb, GENERIC),
        /* Hauppauge WinTV series */
@@ -964,7 +964,7 @@ static void __devexit snd_bt87x_remove(struct pci_dev *pci)
 
 /* default entries for all Bt87x cards - it's not exported */
 /* driver_data is set to 0 to call detection */
-static struct pci_device_id snd_bt87x_default_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = {
        BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_878, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN),
        BT_DEVICE(PCI_DEVICE_ID_BROOKTREE_879, PCI_ANY_ID, PCI_ANY_ID, UNKNOWN),
        { }
index 15e4138..0a3d3d6 100644 (file)
@@ -1875,7 +1875,7 @@ static int snd_ca0106_resume(struct pci_dev *pci)
 #endif
 
 // PCI IDs
-static struct pci_device_id snd_ca0106_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_ca0106_ids) = {
        { PCI_VDEVICE(CREATIVE, 0x0007), 0 },   /* Audigy LS or Live 24bit */
        { 0, }
 };
index a312bae..1ded64e 100644 (file)
@@ -2796,7 +2796,7 @@ static inline void snd_cmipci_proc_init(struct cmipci *cm) {}
 #endif
 
 
-static struct pci_device_id snd_cmipci_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_cmipci_ids) = {
        {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338A), 0},
        {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8338B), 0},
        {PCI_VDEVICE(CMEDIA, PCI_DEVICE_ID_CMEDIA_CM8738), 0},
@@ -3018,7 +3018,7 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
        int integrated_midi = 0;
        char modelstr[16];
        int pcm_index, pcm_spdif_index;
-       static struct pci_device_id intel_82437vx[] = {
+       static DEFINE_PCI_DEVICE_TABLE(intel_82437vx) = {
                { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437VX) },
                { },
        };
index e2e0359..9edc650 100644 (file)
@@ -494,7 +494,7 @@ struct cs4281 {
 
 static irqreturn_t snd_cs4281_interrupt(int irq, void *dev_id);
 
-static struct pci_device_id snd_cs4281_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_cs4281_ids) = {
        { PCI_VDEVICE(CIRRUS, 0x6005), 0, },    /* CS4281 */
        { 0, }
 };
index 033aec4..767fa7f 100644 (file)
@@ -64,7 +64,7 @@ MODULE_PARM_DESC(thinkpad, "Force to enable Thinkpad's CLKRUN control.");
 module_param_array(mmap_valid, bool, NULL, 0444);
 MODULE_PARM_DESC(mmap_valid, "Support OSS mmap.");
 
-static struct pci_device_id snd_cs46xx_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_cs46xx_ids) = {
        { PCI_VDEVICE(CIRRUS, 0x6001), 0, },   /* CS4280 */
        { PCI_VDEVICE(CIRRUS, 0x6003), 0, },   /* CS4612 */
        { PCI_VDEVICE(CIRRUS, 0x6004), 0, },   /* CS4615 */
index 1be96ea..56fcf00 100644 (file)
@@ -3597,7 +3597,7 @@ static struct cs_card_type __devinitdata cards[] = {
 #ifdef CONFIG_PM
 static unsigned int saved_regs[] = {
        BA0_ACOSV,
-       BA0_ASER_FADDR,
+       /*BA0_ASER_FADDR,*/
        BA0_ASER_MASTER,
        BA1_PVOL,
        BA1_CVOL,
@@ -3644,6 +3644,7 @@ int snd_cs46xx_resume(struct pci_dev *pci)
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
        int i;
 #endif
+       unsigned int tmp;
 
        pci_set_power_state(pci, PCI_D0);
        pci_restore_state(pci);
@@ -3685,6 +3686,15 @@ int snd_cs46xx_resume(struct pci_dev *pci)
        snd_ac97_resume(chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]);
        snd_ac97_resume(chip->ac97[CS46XX_SECONDARY_CODEC_INDEX]);
 
+       /*
+         *  Stop capture DMA.
+        */
+       tmp = snd_cs46xx_peek(chip, BA1_CCTL);
+       chip->capt.ctl = tmp & 0x0000ffff;
+       snd_cs46xx_poke(chip, BA1_CCTL, tmp & 0xffff0000);
+
+       mdelay(5);
+
        /* reset playback/capture */
        snd_cs46xx_set_play_sample_rate(chip, 8000);
        snd_cs46xx_set_capture_sample_rate(chip, 8000);
index f4f0c8f..3e5ca8f 100644 (file)
@@ -298,6 +298,9 @@ void  cs46xx_dsp_spos_destroy (struct snd_cs46xx * chip)
                if (ins->scbs[i].deleted) continue;
 
                cs46xx_dsp_proc_free_scb_desc ( (ins->scbs + i) );
+#ifdef CONFIG_PM
+               kfree(ins->scbs[i].data);
+#endif
        }
 
        kfree(ins->code.data);
@@ -974,13 +977,11 @@ static struct dsp_scb_descriptor * _map_scb (struct snd_cs46xx *chip, char * nam
 
        index = find_free_scb_index (ins);
 
+       memset(&ins->scbs[index], 0, sizeof(ins->scbs[index]));
        strcpy(ins->scbs[index].scb_name, name);
        ins->scbs[index].address = dest;
        ins->scbs[index].index = index;
-       ins->scbs[index].proc_info = NULL;
        ins->scbs[index].ref_count = 1;
-       ins->scbs[index].deleted = 0;
-       spin_lock_init(&ins->scbs[index].lock);
 
        desc = (ins->scbs + index);
        ins->scbs[index].scb_symbol = add_symbol (chip, name, dest, SYMBOL_PARAMETER);
@@ -1022,17 +1023,29 @@ _map_task_tree (struct snd_cs46xx *chip, char * name, u32 dest, u32 size)
        return desc;
 }
 
+#define SCB_BYTES      (0x10 * 4)
+
 struct dsp_scb_descriptor *
 cs46xx_dsp_create_scb (struct snd_cs46xx *chip, char * name, u32 * scb_data, u32 dest)
 {
        struct dsp_scb_descriptor * desc;
 
+#ifdef CONFIG_PM
+       /* copy the data for resume */
+       scb_data = kmemdup(scb_data, SCB_BYTES, GFP_KERNEL);
+       if (!scb_data)
+               return NULL;
+#endif
+
        desc = _map_scb (chip,name,dest);
        if (desc) {
                desc->data = scb_data;
                _dsp_create_scb(chip,scb_data,dest);
        } else {
                snd_printk(KERN_ERR "dsp_spos: failed to map SCB\n");
+#ifdef CONFIG_PM
+               kfree(scb_data);
+#endif
        }
 
        return desc;
@@ -1988,7 +2001,28 @@ int cs46xx_dsp_resume(struct snd_cs46xx * chip)
                        continue;
                _dsp_create_scb(chip, s->data, s->address);
        }
-
+       for (i = 0; i < ins->nscb; i++) {
+               struct dsp_scb_descriptor *s = &ins->scbs[i];
+               if (s->deleted)
+                       continue;
+               if (s->updated)
+                       cs46xx_dsp_spos_update_scb(chip, s);
+               if (s->volume_set)
+                       cs46xx_dsp_scb_set_volume(chip, s,
+                                                 s->volume[0], s->volume[1]);
+       }
+       if (ins->spdif_status_out & DSP_SPDIF_STATUS_HW_ENABLED) {
+               cs46xx_dsp_enable_spdif_hw(chip);
+               snd_cs46xx_poke(chip, (ins->ref_snoop_scb->address + 2) << 2,
+                               (OUTPUT_SNOOP_BUFFER + 0x10) << 0x10);
+               if (ins->spdif_status_out & DSP_SPDIF_STATUS_PLAYBACK_OPEN)
+                       cs46xx_poke_via_dsp(chip, SP_SPDOUT_CSUV,
+                                           ins->spdif_csuv_stream);
+       }
+       if (chip->dsp_spos_instance->spdif_status_in) {
+               cs46xx_poke_via_dsp(chip, SP_ASER_COUNTDOWN, 0x80000005);
+               cs46xx_poke_via_dsp(chip, SP_SPDIN_CONTROL, 0x800003ff);
+       }
        return 0;
 }
 #endif
index f9e169d..ca47a81 100644 (file)
@@ -212,6 +212,7 @@ static inline void cs46xx_dsp_spos_update_scb (struct snd_cs46xx * chip,
                        (scb->address + SCBsubListPtr) << 2,
                        (scb->sub_list_ptr->address << 0x10) |
                        (scb->next_scb_ptr->address));  
+       scb->updated = 1;
 }
 
 static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip,
@@ -222,6 +223,9 @@ static inline void cs46xx_dsp_scb_set_volume (struct snd_cs46xx * chip,
 
        snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl) << 2, val);
        snd_cs46xx_poke(chip, (scb->address + SCBVolumeCtrl + 1) << 2, val);
+       scb->volume_set = 1;
+       scb->volume[0] = left;
+       scb->volume[1] = right;
 }
 #endif /* __DSP_SPOS_H__ */
 #endif /* CONFIG_SND_CS46XX_NEW_DSP  */
index dd7c41b..00b148a 100644 (file)
@@ -115,7 +115,6 @@ static void cs46xx_dsp_proc_scb_info_read (struct snd_info_entry *entry,
 static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor * scb)
 {
        struct dsp_spos_instance * ins = chip->dsp_spos_instance;
-       unsigned long flags;
 
        if ( scb->parent_scb_ptr ) {
                /* unlink parent SCB */
@@ -153,8 +152,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor
                        scb->next_scb_ptr = ins->the_null_scb;
                }
 
-               spin_lock_irqsave(&chip->reg_lock, flags);    
-
                /* update parent first entry in DSP RAM */
                cs46xx_dsp_spos_update_scb(chip,scb->parent_scb_ptr);
 
@@ -162,7 +159,6 @@ static void _dsp_unlink_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor
                cs46xx_dsp_spos_update_scb(chip,scb);
 
                scb->parent_scb_ptr = NULL;
-               spin_unlock_irqrestore(&chip->reg_lock, flags);
        }
 }
 
@@ -197,9 +193,9 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
                goto _end;
 #endif
 
-       spin_lock_irqsave(&scb->lock, flags);
+       spin_lock_irqsave(&chip->reg_lock, flags);    
        _dsp_unlink_scb (chip,scb);
-       spin_unlock_irqrestore(&scb->lock, flags);
+       spin_unlock_irqrestore(&chip->reg_lock, flags);
 
        cs46xx_dsp_proc_free_scb_desc(scb);
        if (snd_BUG_ON(!scb->scb_symbol))
@@ -207,6 +203,10 @@ void cs46xx_dsp_remove_scb (struct snd_cs46xx *chip, struct dsp_scb_descriptor *
        remove_symbol (chip,scb->scb_symbol);
 
        ins->scbs[scb->index].deleted = 1;
+#ifdef CONFIG_PM
+       kfree(ins->scbs[scb->index].data);
+       ins->scbs[scb->index].data = NULL;
+#endif
 
        if (scb->index < ins->scb_highest_frag_index)
                ins->scb_highest_frag_index = scb->index;
@@ -1508,20 +1508,17 @@ int cs46xx_dsp_pcm_unlink (struct snd_cs46xx * chip,
                       chip->dsp_spos_instance->npcm_channels <= 0))
                return -EIO;
 
-       spin_lock(&pcm_channel->src_scb->lock);
-
+       spin_lock_irqsave(&chip->reg_lock, flags);
        if (pcm_channel->unlinked) {
-               spin_unlock(&pcm_channel->src_scb->lock);
+               spin_unlock_irqrestore(&chip->reg_lock, flags);
                return -EIO;
        }
 
-       spin_lock_irqsave(&chip->reg_lock, flags);
        pcm_channel->unlinked = 1;
-       spin_unlock_irqrestore(&chip->reg_lock, flags);
 
        _dsp_unlink_scb (chip,pcm_channel->pcm_reader_scb);
+       spin_unlock_irqrestore(&chip->reg_lock, flags);
 
-       spin_unlock(&pcm_channel->src_scb->lock);
        return 0;
 }
 
@@ -1533,10 +1530,10 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
        struct dsp_scb_descriptor * src_scb = pcm_channel->src_scb;
        unsigned long flags;
 
-       spin_lock(&pcm_channel->src_scb->lock);
+       spin_lock_irqsave(&chip->reg_lock, flags);
 
        if (pcm_channel->unlinked == 0) {
-               spin_unlock(&pcm_channel->src_scb->lock);
+               spin_unlock_irqrestore(&chip->reg_lock, flags);
                return -EIO;
        }
 
@@ -1552,8 +1549,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
        snd_BUG_ON(pcm_channel->pcm_reader_scb->parent_scb_ptr);
        pcm_channel->pcm_reader_scb->parent_scb_ptr = parent_scb;
 
-       spin_lock_irqsave(&chip->reg_lock, flags);
-
        /* update SCB entry in DSP RAM */
        cs46xx_dsp_spos_update_scb(chip,pcm_channel->pcm_reader_scb);
 
@@ -1562,8 +1557,6 @@ int cs46xx_dsp_pcm_link (struct snd_cs46xx * chip,
 
        pcm_channel->unlinked = 0;
        spin_unlock_irqrestore(&chip->reg_lock, flags);
-
-       spin_unlock(&pcm_channel->src_scb->lock);
        return 0;
 }
 
@@ -1596,13 +1589,17 @@ cs46xx_add_record_source (struct snd_cs46xx *chip, struct dsp_scb_descriptor * s
 
 int cs46xx_src_unlink(struct snd_cs46xx *chip, struct dsp_scb_descriptor * src)
 {
+       unsigned long flags;
+
        if (snd_BUG_ON(!src->parent_scb_ptr))
                return -EINVAL;
 
        /* mute SCB */
        cs46xx_dsp_scb_set_volume (chip,src,0,0);
 
+       spin_lock_irqsave(&chip->reg_lock, flags);
        _dsp_unlink_scb (chip,src);
+       spin_unlock_irqrestore(&chip->reg_lock, flags);
 
        return 0;
 }
index dc46432..207479a 100644 (file)
@@ -58,7 +58,7 @@ struct snd_cs5530 {
        unsigned long pci_base;
 };
 
-static struct pci_device_id snd_cs5530_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_cs5530_ids) = {
        {PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_AUDIO, PCI_ANY_ID,
                                                        PCI_ANY_ID, 0, 0},
        {0,}
index 91e7faf..afb8037 100644 (file)
@@ -66,7 +66,7 @@ MODULE_PARM_DESC(id, "ID string for " DRIVER_NAME);
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable " DRIVER_NAME);
 
-static struct pci_device_id snd_cs5535audio_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_cs5535audio_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_AUDIO) },
        { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_AUDIO) },
        {}
index 459c1f6..480cb1e 100644 (file)
@@ -1214,10 +1214,11 @@ static int atc_dev_free(struct snd_device *dev)
        return ct_atc_destroy(atc);
 }
 
-static int __devinit atc_identify_card(struct ct_atc *atc)
+static int __devinit atc_identify_card(struct ct_atc *atc, unsigned int ssid)
 {
        const struct snd_pci_quirk *p;
        const struct snd_pci_quirk *list;
+       u16 vendor_id, device_id;
 
        switch (atc->chip_type) {
        case ATC20K1:
@@ -1231,13 +1232,19 @@ static int __devinit atc_identify_card(struct ct_atc *atc)
        default:
                return -ENOENT;
        }
-       p = snd_pci_quirk_lookup(atc->pci, list);
+       if (ssid) {
+               vendor_id = ssid >> 16;
+               device_id = ssid & 0xffff;
+       } else {
+               vendor_id = atc->pci->subsystem_vendor;
+               device_id = atc->pci->subsystem_device;
+       }
+       p = snd_pci_quirk_lookup_id(vendor_id, device_id, list);
        if (p) {
                if (p->value < 0) {
                        printk(KERN_ERR "ctxfi: "
                               "Device %04x:%04x is black-listed\n",
-                              atc->pci->subsystem_vendor,
-                              atc->pci->subsystem_device);
+                              vendor_id, device_id);
                        return -ENOENT;
                }
                atc->model = p->value;
@@ -1250,8 +1257,7 @@ static int __devinit atc_identify_card(struct ct_atc *atc)
        atc->model_name = ct_subsys_name[atc->model];
        snd_printd("ctxfi: chip %s model %s (%04x:%04x) is found\n",
                   atc->chip_name, atc->model_name,
-                  atc->pci->subsystem_vendor,
-                  atc->pci->subsystem_device);
+                  vendor_id, device_id);
        return 0;
 }
 
@@ -1625,7 +1631,8 @@ static struct ct_atc atc_preset __devinitdata = {
 
 int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
                            unsigned int rsr, unsigned int msr,
-                           int chip_type, struct ct_atc **ratc)
+                           int chip_type, unsigned int ssid,
+                           struct ct_atc **ratc)
 {
        struct ct_atc *atc;
        static struct snd_device_ops ops = {
@@ -1651,7 +1658,7 @@ int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
        mutex_init(&atc->atc_mutex);
 
        /* Find card model */
-       err = atc_identify_card(atc);
+       err = atc_identify_card(atc, ssid);
        if (err < 0) {
                printk(KERN_ERR "ctatc: Card not recognised\n");
                goto error1;
index 9fd8a57..7167c01 100644 (file)
@@ -148,7 +148,7 @@ struct ct_atc {
 
 int __devinit ct_atc_create(struct snd_card *card, struct pci_dev *pci,
                            unsigned int rsr, unsigned int msr, int chip_type,
-                           struct ct_atc **ratc);
+                           unsigned int subsysid, struct ct_atc **ratc);
 int __devinit ct_atc_create_alsa_devs(struct ct_atc *atc);
 
 #endif /* CTATC_H */
index 7654174..f42e7e1 100644 (file)
@@ -32,6 +32,7 @@ module_param(multiple, uint, S_IRUGO);
 static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
 static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
 static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static unsigned int subsystem[SNDRV_CARDS];
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for Creative X-Fi driver");
@@ -39,8 +40,10 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string for Creative X-Fi driver");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable Creative X-Fi driver");
+module_param_array(subsystem, int, NULL, 0444);
+MODULE_PARM_DESC(subsystem, "Override subsystem ID for Creative X-Fi driver");
 
-static struct pci_device_id ct_pci_dev_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(ct_pci_dev_ids) = {
        /* only X-Fi is supported, so... */
        { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_DEVICE_ID_CREATIVE_20K1),
          .driver_data = ATC20K1,
@@ -85,7 +88,7 @@ ct_card_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
                multiple = 2;
        }
        err = ct_atc_create(card, pci, reference_rate, multiple,
-                           pci_id->driver_data, &atc);
+                           pci_id->driver_data, subsystem[dev], &atc);
        if (err < 0)
                goto error;
 
index 8c6db3a..a65bafe 100644 (file)
@@ -63,7 +63,7 @@ static const struct firmware card_fw[] = {
        {0, "darla20_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x1801, 0xECC0, 0x0010, 0, 0, 0},      /* DSP 56301 Darla20 rev.0 */
        {0,}
 };
index 2904330..20c7cbc 100644 (file)
@@ -45,7 +45,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_DARLA20_DSP];
+       chip->dsp_code_to_load = FW_DARLA20_DSP;
        chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
        chip->clock_state = GD_CLOCK_UNDEF;
        /* Since this card has no ASIC, mark it as loaded so everything
@@ -57,15 +57,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
+
+
+
 /* The Darla20 has no external clock sources */
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
index 04cbf3e..0a6c50b 100644 (file)
@@ -67,7 +67,7 @@ static const struct firmware card_fw[] = {
        {0, "darla24_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x1801, 0xECC0, 0x0040, 0, 0, 0},      /* DSP 56301 Darla24 rev.0 */
        {0x1057, 0x1801, 0xECC0, 0x0041, 0, 0, 0},      /* DSP 56301 Darla24 rev.1 */
        {0,}
index 6022873..6da6663 100644 (file)
@@ -45,7 +45,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_DARLA24_DSP];
+       chip->dsp_code_to_load = FW_DARLA24_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -56,15 +56,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
index 4022e43..f514279 100644 (file)
@@ -81,7 +81,7 @@ static const struct firmware card_fw[] = {
        {0, "3g_asic.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x0100, 0, 0, 0},      /* Echo 3G */
        {0,}
 };
index 57967e5..3cdc2ee 100644 (file)
@@ -61,7 +61,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
        chip->has_midi = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_ECHO3G_DSP];
+       chip->dsp_code_to_load = FW_ECHO3G_DSP;
 
        /* Load the DSP code and the ASIC on the PCI card and get
        what type of external box is attached */
@@ -97,20 +97,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->digital_modes =   ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
                                ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
                                ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
-       chip->digital_mode =    DIGITAL_MODE_SPDIF_RCA;
-       chip->professional_spdif = FALSE;
-       chip->non_audio_spdif = FALSE;
-       chip->bad_board = FALSE;
-
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-       if (err < 0)
-               return err;
-       err = set_phantom_power(chip, 0);
-       if (err < 0)
-               return err;
-       err = set_professional_spdif(chip, TRUE);
 
        DE_INIT(("init_hw done\n"));
        return err;
@@ -118,6 +104,18 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->non_audio_spdif = FALSE;
+       chip->bad_board = FALSE;
+       chip->phantom_power = FALSE;
+       return init_line_levels(chip);
+}
+
+
+
 static int set_phantom_power(struct echoaudio *chip, char on)
 {
        u32 control_reg = le32_to_cpu(chip->comm_page->control_register);
index 641d7f0..8dab82d 100644 (file)
@@ -36,22 +36,61 @@ MODULE_PARM_DESC(enable, "Enable " ECHOCARD_NAME " soundcard.");
 static unsigned int channels_list[10] = {1, 2, 4, 6, 8, 10, 12, 14, 16, 999999};
 static const DECLARE_TLV_DB_SCALE(db_scale_output_gain, -12800, 100, 1);
 
+
+
 static int get_firmware(const struct firmware **fw_entry,
-                       const struct firmware *frm, struct echoaudio *chip)
+                       struct echoaudio *chip, const short fw_index)
 {
        int err;
        char name[30];
-       DE_ACT(("firmware requested: %s\n", frm->data));
-       snprintf(name, sizeof(name), "ea/%s", frm->data);
-       if ((err = request_firmware(fw_entry, name, pci_device(chip))) < 0)
+
+#ifdef CONFIG_PM
+       if (chip->fw_cache[fw_index]) {
+               DE_ACT(("firmware requested: %s is cached\n", card_fw[fw_index].data));
+               *fw_entry = chip->fw_cache[fw_index];
+               return 0;
+       }
+#endif
+
+       DE_ACT(("firmware requested: %s\n", card_fw[fw_index].data));
+       snprintf(name, sizeof(name), "ea/%s", card_fw[fw_index].data);
+       err = request_firmware(fw_entry, name, pci_device(chip));
+       if (err < 0)
                snd_printk(KERN_ERR "get_firmware(): Firmware not available (%d)\n", err);
+#ifdef CONFIG_PM
+       else
+               chip->fw_cache[fw_index] = *fw_entry;
+#endif
        return err;
 }
 
+
+
 static void free_firmware(const struct firmware *fw_entry)
 {
+#ifdef CONFIG_PM
+       DE_ACT(("firmware not released (kept in cache)\n"));
+#else
        release_firmware(fw_entry);
        DE_ACT(("firmware released\n"));
+#endif
+}
+
+
+
+static void free_firmware_cache(struct echoaudio *chip)
+{
+#ifdef CONFIG_PM
+       int i;
+
+       for (i = 0; i < 8 ; i++)
+               if (chip->fw_cache[i]) {
+                       release_firmware(chip->fw_cache[i]);
+                       DE_ACT(("release_firmware(%d)\n", i));
+               }
+
+       DE_ACT(("firmware_cache released\n"));
+#endif
 }
 
 
@@ -714,6 +753,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 
        spin_lock(&chip->lock);
        switch (cmd) {
+       case SNDRV_PCM_TRIGGER_RESUME:
+               DE_ACT(("pcm_trigger resume\n"));
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
                DE_ACT(("pcm_trigger start\n"));
@@ -737,6 +778,8 @@ static int pcm_trigger(struct snd_pcm_substream *substream, int cmd)
                err = start_transport(chip, channelmask,
                                      chip->pipe_cyclic_mask);
                break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               DE_ACT(("pcm_trigger suspend\n"));
        case SNDRV_PCM_TRIGGER_STOP:
                DE_ACT(("pcm_trigger stop\n"));
                for (i = 0; i < DSP_MAXPIPES; i++) {
@@ -1876,6 +1919,7 @@ static int snd_echo_free(struct echoaudio *chip)
        pci_disable_device(chip->pci);
 
        /* release chip data */
+       free_firmware_cache(chip);
        kfree(chip);
        DE_INIT(("Chip freed.\n"));
        return 0;
@@ -1913,18 +1957,27 @@ static __devinit int snd_echo_create(struct snd_card *card,
                return err;
        pci_set_master(pci);
 
-       /* allocate a chip-specific data */
-       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
-       if (!chip) {
-               pci_disable_device(pci);
-               return -ENOMEM;
+       /* Allocate chip if needed */
+       if (!*rchip) {
+               chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+               if (!chip) {
+                       pci_disable_device(pci);
+                       return -ENOMEM;
+               }
+               DE_INIT(("chip=%p\n", chip));
+               spin_lock_init(&chip->lock);
+               chip->card = card;
+               chip->pci = pci;
+               chip->irq = -1;
+               atomic_set(&chip->opencount, 0);
+               mutex_init(&chip->mode_mutex);
+               chip->can_set_rate = 1;
+       } else {
+               /* If this was called from the resume function, chip is
+                * already allocated and it contains current card settings.
+                */
+               chip = *rchip;
        }
-       DE_INIT(("chip=%p\n", chip));
-
-       spin_lock_init(&chip->lock);
-       chip->card = card;
-       chip->pci = pci;
-       chip->irq = -1;
 
        /* PCI resource allocation */
        chip->dsp_registers_phys = pci_resource_start(pci, 0);
@@ -1964,7 +2017,9 @@ static __devinit int snd_echo_create(struct snd_card *card,
        chip->comm_page = (struct comm_page *)chip->commpage_dma_buf.area;
 
        err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
-       if (err) {
+       if (err >= 0)
+               err = set_mixer_defaults(chip);
+       if (err < 0) {
                DE_INIT(("init_hw err=%d\n", err));
                snd_echo_free(chip);
                return err;
@@ -1975,9 +2030,6 @@ static __devinit int snd_echo_create(struct snd_card *card,
                snd_echo_free(chip);
                return err;
        }
-       atomic_set(&chip->opencount, 0);
-       mutex_init(&chip->mode_mutex);
-       chip->can_set_rate = 1;
        *rchip = chip;
        /* Init done ! */
        return 0;
@@ -2010,6 +2062,7 @@ static int __devinit snd_echo_probe(struct pci_dev *pci,
 
        snd_card_set_dev(card, &pci->dev);
 
+       chip = NULL;    /* Tells snd_echo_create to allocate chip */
        if ((err = snd_echo_create(card, pci, &chip)) < 0) {
                snd_card_free(card);
                return err;
@@ -2149,6 +2202,112 @@ ctl_error:
 
 
 
+#if defined(CONFIG_PM)
+
+static int snd_echo_suspend(struct pci_dev *pci, pm_message_t state)
+{
+       struct echoaudio *chip = pci_get_drvdata(pci);
+
+       DE_INIT(("suspend start\n"));
+       snd_pcm_suspend_all(chip->analog_pcm);
+       snd_pcm_suspend_all(chip->digital_pcm);
+
+#ifdef ECHOCARD_HAS_MIDI
+       /* This call can sleep */
+       if (chip->midi_out)
+               snd_echo_midi_output_trigger(chip->midi_out, 0);
+#endif
+       spin_lock_irq(&chip->lock);
+       if (wait_handshake(chip)) {
+               spin_unlock_irq(&chip->lock);
+               return -EIO;
+       }
+       clear_handshake(chip);
+       if (send_vector(chip, DSP_VC_GO_COMATOSE) < 0) {
+               spin_unlock_irq(&chip->lock);
+               return -EIO;
+       }
+       spin_unlock_irq(&chip->lock);
+
+       chip->dsp_code = NULL;
+       free_irq(chip->irq, chip);
+       chip->irq = -1;
+       pci_save_state(pci);
+       pci_disable_device(pci);
+
+       DE_INIT(("suspend done\n"));
+       return 0;
+}
+
+
+
+static int snd_echo_resume(struct pci_dev *pci)
+{
+       struct echoaudio *chip = pci_get_drvdata(pci);
+       struct comm_page *commpage, *commpage_bak;
+       u32 pipe_alloc_mask;
+       int err;
+
+       DE_INIT(("resume start\n"));
+       pci_restore_state(pci);
+       commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+       commpage = chip->comm_page;
+       memcpy(commpage_bak, commpage, sizeof(struct comm_page));
+
+       err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
+       if (err < 0) {
+               kfree(commpage_bak);
+               DE_INIT(("resume init_hw err=%d\n", err));
+               snd_echo_free(chip);
+               return err;
+       }
+       DE_INIT(("resume init OK\n"));
+
+       /* Temporarily set chip->pipe_alloc_mask=0 otherwise
+        * restore_dsp_settings() fails.
+        */
+       pipe_alloc_mask = chip->pipe_alloc_mask;
+       chip->pipe_alloc_mask = 0;
+       err = restore_dsp_rettings(chip);
+       chip->pipe_alloc_mask = pipe_alloc_mask;
+       if (err < 0) {
+               kfree(commpage_bak);
+               return err;
+       }
+       DE_INIT(("resume restore OK\n"));
+
+       memcpy(&commpage->audio_format, &commpage_bak->audio_format,
+               sizeof(commpage->audio_format));
+       memcpy(&commpage->sglist_addr, &commpage_bak->sglist_addr,
+               sizeof(commpage->sglist_addr));
+       memcpy(&commpage->midi_output, &commpage_bak->midi_output,
+               sizeof(commpage->midi_output));
+       kfree(commpage_bak);
+
+       if (request_irq(pci->irq, snd_echo_interrupt, IRQF_SHARED,
+                       ECHOCARD_NAME, chip)) {
+               snd_echo_free(chip);
+               snd_printk(KERN_ERR "cannot grab irq\n");
+               return -EBUSY;
+       }
+       chip->irq = pci->irq;
+       DE_INIT(("resume irq=%d\n", chip->irq));
+
+#ifdef ECHOCARD_HAS_MIDI
+       if (chip->midi_input_enabled)
+               enable_midi_input(chip, TRUE);
+       if (chip->midi_out)
+               snd_echo_midi_output_trigger(chip->midi_out, 1);
+#endif
+
+       DE_INIT(("resume done\n"));
+       return 0;
+}
+
+#endif /* CONFIG_PM */
+
+
+
 static void __devexit snd_echo_remove(struct pci_dev *pci)
 {
        struct echoaudio *chip;
@@ -2171,6 +2330,10 @@ static struct pci_driver driver = {
        .id_table = snd_echo_ids,
        .probe = snd_echo_probe,
        .remove = __devexit_p(snd_echo_remove),
+#ifdef CONFIG_PM
+       .suspend = snd_echo_suspend,
+       .resume = snd_echo_resume,
+#endif /* CONFIG_PM */
 };
 
 
index f9490ae..1df974d 100644 (file)
@@ -442,13 +442,16 @@ struct echoaudio {
        u16 device_id, subdevice_id;
        u16 *dsp_code;                  /* Current DSP code loaded,
                                         * NULL if nothing loaded */
-       const struct firmware *dsp_code_to_load;/* DSP code to load */
-       const struct firmware *asic_code;       /* Current ASIC code */
+       short dsp_code_to_load;         /* DSP code to load */
+       short asic_code;                /* Current ASIC code */
        u32 comm_page_phys;                     /* Physical address of the
                                                 * memory seen by DSP */
        volatile u32 __iomem *dsp_registers;    /* DSP's register base */
        u32 active_mask;                        /* Chs. active mask or
                                                 * punks out */
+#ifdef CONFIG_PM
+       const struct firmware *fw_cache[8];     /* Cached firmwares */
+#endif
 
 #ifdef ECHOCARD_HAS_MIDI
        u16 mtc_state;                          /* State for MIDI input parsing state machine */
@@ -464,11 +467,13 @@ static int load_firmware(struct echoaudio *chip);
 static int wait_handshake(struct echoaudio *chip);
 static int send_vector(struct echoaudio *chip, u32 command);
 static int get_firmware(const struct firmware **fw_entry,
-                       const struct firmware *frm, struct echoaudio *chip);
+                       struct echoaudio *chip, const short fw_index);
 static void free_firmware(const struct firmware *fw_entry);
 
 #ifdef ECHOCARD_HAS_MIDI
 static int enable_midi_input(struct echoaudio *chip, char enable);
+static void snd_echo_midi_output_trigger(
+                       struct snd_rawmidi_substream *substream, int up);
 static int midi_service_irq(struct echoaudio *chip);
 static int __devinit snd_echo_midi_create(struct snd_card *card,
                                          struct echoaudio *chip);
index e32a748..658db44 100644 (file)
@@ -227,12 +227,11 @@ static int load_asic(struct echoaudio *chip)
        /* Give the DSP a few milliseconds to settle down */
        mdelay(2);
 
-       err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC,
-                               &card_fw[FW_3G_ASIC]);
+       err = load_asic_generic(chip, DSP_FNC_LOAD_3G_ASIC, FW_3G_ASIC);
        if (err < 0)
                return err;
 
-       chip->asic_code = &card_fw[FW_3G_ASIC];
+       chip->asic_code = FW_3G_ASIC;
 
        /* Now give the new ASIC some time to set up */
        msleep(1000);
index 4df51ef..64417a7 100644 (file)
@@ -175,15 +175,15 @@ static inline int check_asic_status(struct echoaudio *chip)
 #ifdef ECHOCARD_HAS_ASIC
 
 /* Load ASIC code - done after the DSP is loaded */
-static int load_asic_generic(struct echoaudio *chip, u32 cmd,
-                            const struct firmware *asic)
+static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic)
 {
        const struct firmware *fw;
        int err;
        u32 i, size;
        u8 *code;
 
-       if ((err = get_firmware(&fw, asic, chip)) < 0) {
+       err = get_firmware(&fw, chip, asic);
+       if (err < 0) {
                snd_printk(KERN_WARNING "Firmware not found !\n");
                return err;
        }
@@ -245,7 +245,8 @@ static int install_resident_loader(struct echoaudio *chip)
                return 0;
        }
 
-       if ((i = get_firmware(&fw, &card_fw[FW_361_LOADER], chip)) < 0) {
+       i = get_firmware(&fw, chip, FW_361_LOADER);
+       if (i < 0) {
                snd_printk(KERN_WARNING "Firmware not found !\n");
                return i;
        }
@@ -485,7 +486,8 @@ static int load_firmware(struct echoaudio *chip)
                chip->dsp_code = NULL;
        }
 
-       if ((err = get_firmware(&fw, chip->dsp_code_to_load, chip)) < 0)
+       err = get_firmware(&fw, chip, chip->dsp_code_to_load);
+       if (err < 0)
                return err;
        err = load_dsp(chip, (u16 *)fw->data);
        free_firmware(fw);
@@ -495,9 +497,6 @@ static int load_firmware(struct echoaudio *chip)
        if ((box_type = load_asic(chip)) < 0)
                return box_type;        /* error */
 
-       if ((err = restore_dsp_rettings(chip)) < 0)
-               return err;
-
        return box_type;
 }
 
@@ -657,51 +656,106 @@ static void get_audio_meters(struct echoaudio *chip, long *meters)
 
 static int restore_dsp_rettings(struct echoaudio *chip)
 {
-       int err;
+       int i, o, err;
        DE_INIT(("restore_dsp_settings\n"));
 
        if ((err = check_asic_status(chip)) < 0)
                return err;
 
-       /* Gina20/Darla20 only. Should be harmless for other cards. */
+       /* Gina20/Darla20 only. Should be harmless for other cards. */
        chip->comm_page->gd_clock_state = GD_CLOCK_UNDEF;
        chip->comm_page->gd_spdif_status = GD_SPDIF_STATUS_UNDEF;
        chip->comm_page->handshake = 0xffffffff;
 
-       if ((err = set_sample_rate(chip, chip->sample_rate)) < 0)
+       /* Restore output busses */
+       for (i = 0; i < num_busses_out(chip); i++) {
+               err = set_output_gain(chip, i, chip->output_gain[i]);
+               if (err < 0)
+                       return err;
+       }
+
+#ifdef ECHOCARD_HAS_VMIXER
+       for (i = 0; i < num_pipes_out(chip); i++)
+               for (o = 0; o < num_busses_out(chip); o++) {
+                       err = set_vmixer_gain(chip, o, i,
+                                               chip->vmixer_gain[o][i]);
+                       if (err < 0)
+                               return err;
+               }
+       if (update_vmixer_level(chip) < 0)
+               return -EIO;
+#endif /* ECHOCARD_HAS_VMIXER */
+
+#ifdef ECHOCARD_HAS_MONITOR
+       for (o = 0; o < num_busses_out(chip); o++)
+               for (i = 0; i < num_busses_in(chip); i++) {
+                       err = set_monitor_gain(chip, o, i,
+                                               chip->monitor_gain[o][i]);
+                       if (err < 0)
+                               return err;
+               }
+#endif /* ECHOCARD_HAS_MONITOR */
+
+#ifdef ECHOCARD_HAS_INPUT_GAIN
+       for (i = 0; i < num_busses_in(chip); i++) {
+               err = set_input_gain(chip, i, chip->input_gain[i]);
+               if (err < 0)
+                       return err;
+       }
+#endif /* ECHOCARD_HAS_INPUT_GAIN */
+
+       err = update_output_line_level(chip);
+       if (err < 0)
                return err;
 
-       if (chip->meters_enabled)
-               if (send_vector(chip, DSP_VC_METERS_ON) < 0)
-                       return -EIO;
+       err = update_input_line_level(chip);
+       if (err < 0)
+               return err;
 
-#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
-       if (set_input_clock(chip, chip->input_clock) < 0)
+       err = set_sample_rate(chip, chip->sample_rate);
+       if (err < 0)
+               return err;
+
+       if (chip->meters_enabled) {
+               err = send_vector(chip, DSP_VC_METERS_ON);
+               if (err < 0)
+                       return err;
+       }
+
+#ifdef ECHOCARD_HAS_DIGITAL_MODE_SWITCH
+       if (set_digital_mode(chip, chip->digital_mode) < 0)
                return -EIO;
 #endif
 
-#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
-       if (set_output_clock(chip, chip->output_clock) < 0)
+#ifdef ECHOCARD_HAS_DIGITAL_IO
+       if (set_professional_spdif(chip, chip->professional_spdif) < 0)
                return -EIO;
 #endif
 
-       if (update_output_line_level(chip) < 0)
+#ifdef ECHOCARD_HAS_PHANTOM_POWER
+       if (set_phantom_power(chip, chip->phantom_power) < 0)
                return -EIO;
+#endif
 
-       if (update_input_line_level(chip) < 0)
+#ifdef ECHOCARD_HAS_EXTERNAL_CLOCK
+       /* set_input_clock() also restores automute setting */
+       if (set_input_clock(chip, chip->input_clock) < 0)
                return -EIO;
+#endif
 
-#ifdef ECHOCARD_HAS_VMIXER
-       if (update_vmixer_level(chip) < 0)
+#ifdef ECHOCARD_HAS_OUTPUT_CLOCK_SWITCH
+       if (set_output_clock(chip, chip->output_clock) < 0)
                return -EIO;
 #endif
 
        if (wait_handshake(chip) < 0)
                return -EIO;
        clear_handshake(chip);
+       if (send_vector(chip, DSP_VC_UPDATE_FLAGS) < 0)
+               return -EIO;
 
        DE_INIT(("restore_dsp_rettings done\n"));
-       return send_vector(chip, DSP_VC_UPDATE_FLAGS);
+       return 0;
 }
 
 
@@ -918,9 +972,6 @@ static int init_dsp_comm_page(struct echoaudio *chip)
        chip->card_name = ECHOCARD_NAME;
        chip->bad_board = TRUE; /* Set TRUE until DSP loaded */
        chip->dsp_code = NULL;  /* Current DSP code not loaded */
-       chip->digital_mode = DIGITAL_MODE_NONE;
-       chip->input_clock = ECHO_CLOCK_INTERNAL;
-       chip->output_clock = ECHO_CLOCK_WORD;
        chip->asic_loaded = FALSE;
        memset(chip->comm_page, 0, sizeof(struct comm_page));
 
@@ -931,7 +982,6 @@ static int init_dsp_comm_page(struct echoaudio *chip)
        chip->comm_page->midi_out_free_count =
                cpu_to_le32(DSP_MIDI_OUT_FIFO_SIZE);
        chip->comm_page->sample_rate = cpu_to_le32(44100);
-       chip->sample_rate = 44100;
 
        /* Set line levels so we don't blast any inputs on startup */
        memset(chip->comm_page->monitors, ECHOGAIN_MUTED, MONITOR_ARRAY_SIZE);
@@ -942,50 +992,21 @@ static int init_dsp_comm_page(struct echoaudio *chip)
 
 
 
-/* This function initializes the several volume controls for busses and pipes.
-This MUST be called after the DSP is up and running ! */
+/* This function initializes the chip structure with default values, ie. all
+ * muted and internal clock source. Then it copies the settings to the DSP.
+ * This MUST be called after the DSP is up and running !
+ */
 static int init_line_levels(struct echoaudio *chip)
 {
-       int st, i, o;
-
        DE_INIT(("init_line_levels\n"));
-
-       /* Mute output busses */
-       for (i = 0; i < num_busses_out(chip); i++)
-               if ((st = set_output_gain(chip, i, ECHOGAIN_MUTED)))
-                       return st;
-       if ((st = update_output_line_level(chip)))
-               return st;
-
-#ifdef ECHOCARD_HAS_VMIXER
-       /* Mute the Vmixer */
-       for (i = 0; i < num_pipes_out(chip); i++)
-               for (o = 0; o < num_busses_out(chip); o++)
-                       if ((st = set_vmixer_gain(chip, o, i, ECHOGAIN_MUTED)))
-                               return st;
-       if ((st = update_vmixer_level(chip)))
-               return st;
-#endif /* ECHOCARD_HAS_VMIXER */
-
-#ifdef ECHOCARD_HAS_MONITOR
-       /* Mute the monitor mixer */
-       for (o = 0; o < num_busses_out(chip); o++)
-               for (i = 0; i < num_busses_in(chip); i++)
-                       if ((st = set_monitor_gain(chip, o, i, ECHOGAIN_MUTED)))
-                               return st;
-       if ((st = update_output_line_level(chip)))
-               return st;
-#endif /* ECHOCARD_HAS_MONITOR */
-
-#ifdef ECHOCARD_HAS_INPUT_GAIN
-       for (i = 0; i < num_busses_in(chip); i++)
-               if ((st = set_input_gain(chip, i, ECHOGAIN_MUTED)))
-                       return st;
-       if ((st = update_input_line_level(chip)))
-               return st;
-#endif /* ECHOCARD_HAS_INPUT_GAIN */
-
-       return 0;
+       memset(chip->output_gain, ECHOGAIN_MUTED, sizeof(chip->output_gain));
+       memset(chip->input_gain, ECHOGAIN_MUTED, sizeof(chip->input_gain));
+       memset(chip->monitor_gain, ECHOGAIN_MUTED, sizeof(chip->monitor_gain));
+       memset(chip->vmixer_gain, ECHOGAIN_MUTED, sizeof(chip->vmixer_gain));
+       chip->input_clock = ECHO_CLOCK_INTERNAL;
+       chip->output_clock = ECHO_CLOCK_WORD;
+       chip->sample_rate = 44100;
+       return restore_dsp_rettings(chip);
 }
 
 
index c0e64b8..2364f8a 100644 (file)
@@ -67,7 +67,7 @@ static const struct firmware card_fw[] = {
        {0, "gina20_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x1801, 0xECC0, 0x0020, 0, 0, 0},      /* DSP 56301 Gina20 rev.0 */
        {0,}
 };
index 3f1e747..d1615a0 100644 (file)
@@ -49,7 +49,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_GINA20_DSP];
+       chip->dsp_code_to_load = FW_GINA20_DSP;
        chip->spdif_status = GD_SPDIF_STATUS_UNDEF;
        chip->clock_state = GD_CLOCK_UNDEF;
        /* Since this card has no ASIC, mark it as loaded so everything
@@ -62,17 +62,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
-       err = set_professional_spdif(chip, TRUE);
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       chip->professional_spdif = FALSE;
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
index c36a78d..616b558 100644 (file)
@@ -85,7 +85,7 @@ static const struct firmware card_fw[] = {
        {0, "gina24_361_asic.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x1801, 0xECC0, 0x0050, 0, 0, 0},      /* DSP 56301 Gina24 rev.0 */
        {0x1057, 0x1801, 0xECC0, 0x0051, 0, 0, 0},      /* DSP 56301 Gina24 rev.1 */
        {0x1057, 0x3410, 0xECC0, 0x0050, 0, 0, 0},      /* DSP 56361 Gina24 rev.0 */
index 2fef37a..98f7cfa 100644 (file)
@@ -33,8 +33,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force);
 static int set_input_clock(struct echoaudio *chip, u16 clock);
 static int set_professional_spdif(struct echoaudio *chip, char prof);
 static int set_digital_mode(struct echoaudio *chip, u8 mode);
-static int load_asic_generic(struct echoaudio *chip, u32 cmd,
-                            const struct firmware *asic);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic);
 static int check_asic_status(struct echoaudio *chip);
 
 
@@ -58,19 +57,16 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
                ECHO_CLOCK_BIT_ESYNC | ECHO_CLOCK_BIT_ESYNC96 |
                ECHO_CLOCK_BIT_ADAT;
-       chip->professional_spdif = FALSE;
-       chip->digital_in_automute = TRUE;
-       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
 
        /* Gina24 comes in both '301 and '361 flavors */
        if (chip->device_id == DEVICE_ID_56361) {
-               chip->dsp_code_to_load = &card_fw[FW_GINA24_361_DSP];
+               chip->dsp_code_to_load = FW_GINA24_361_DSP;
                chip->digital_modes =
                        ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
                        ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
                        ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
        } else {
-               chip->dsp_code_to_load = &card_fw[FW_GINA24_301_DSP];
+               chip->dsp_code_to_load = FW_GINA24_301_DSP;
                chip->digital_modes =
                        ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
                        ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
@@ -82,19 +78,22 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-       if (err < 0)
-               return err;
-       err = set_professional_spdif(chip, TRUE);
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->digital_in_automute = TRUE;
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
@@ -125,7 +124,7 @@ static int load_asic(struct echoaudio *chip)
 {
        u32 control_reg;
        int err;
-       const struct firmware *fw;
+       short asic;
 
        if (chip->asic_loaded)
                return 1;
@@ -135,14 +134,15 @@ static int load_asic(struct echoaudio *chip)
 
        /* Pick the correct ASIC for '301 or '361 Gina24 */
        if (chip->device_id == DEVICE_ID_56361)
-               fw = &card_fw[FW_GINA24_361_ASIC];
+               asic = FW_GINA24_361_ASIC;
        else
-               fw = &card_fw[FW_GINA24_301_ASIC];
+               asic = FW_GINA24_301_ASIC;
 
-       if ((err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, fw)) < 0)
+       err = load_asic_generic(chip, DSP_FNC_LOAD_GINA24_ASIC, asic);
+       if (err < 0)
                return err;
 
-       chip->asic_code = fw;
+       chip->asic_code = asic;
 
        /* Now give the new ASIC a little time to set up */
        mdelay(10);
index 0a58a7c..776175c 100644 (file)
@@ -68,7 +68,7 @@ static const struct firmware card_fw[] = {
        {0, "indigo_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x0090, 0, 0, 0},      /* Indigo */
        {0,}
 };
index 0b2cd9c..5e85f14 100644 (file)
@@ -50,7 +50,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_INDIGO_DSP];
+       chip->dsp_code_to_load = FW_INDIGO_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -60,15 +60,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        return ECHO_CLOCK_BIT_INTERNAL;
index 9ab625e..2e4ab3e 100644 (file)
@@ -61,6 +61,7 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
 
        control_reg |= clock;
        if (control_reg != old_control_reg) {
+               DE_ACT(("set_sample_rate: %d clock %d\n", rate, clock));
                chip->comm_page->control_register = cpu_to_le32(control_reg);
                chip->sample_rate = rate;
                clear_handshake(chip);
index 2db24d2..8816b0b 100644 (file)
@@ -68,7 +68,7 @@ static const struct firmware card_fw[] = {
        {0, "indigo_dj_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x00B0, 0, 0, 0},      /* Indigo DJ*/
        {0,}
 };
index 0839291..68f3c8c 100644 (file)
@@ -50,7 +50,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJ_DSP];
+       chip->dsp_code_to_load = FW_INDIGO_DJ_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -60,15 +60,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        return ECHO_CLOCK_BIT_INTERNAL;
index 2e44316..b1e3652 100644 (file)
@@ -68,7 +68,7 @@ static const struct firmware card_fw[] = {
        {0, "indigo_djx_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x00E0, 0, 0, 0},      /* Indigo DJx*/
        {0,}
 };
index f591fc2..bb9632c 100644 (file)
@@ -48,7 +48,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_INDIGO_DJX_DSP];
+       chip->dsp_code_to_load = FW_INDIGO_DJX_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -59,10 +59,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       err = init_line_levels(chip);
-       if (err < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
+
+
+
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
index a60c0a0..1035125 100644 (file)
@@ -69,7 +69,7 @@ static const struct firmware card_fw[] = {
        {0, "indigo_io_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x00A0, 0, 0, 0},      /* Indigo IO*/
        {0,}
 };
index 0604c8a..beb9a5b 100644 (file)
@@ -50,7 +50,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_INDIGO_IO_DSP];
+       chip->dsp_code_to_load = FW_INDIGO_IO_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -60,15 +60,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        return ECHO_CLOCK_BIT_INTERNAL;
index eb3819f..60b7cb2 100644 (file)
@@ -69,7 +69,7 @@ static const struct firmware card_fw[] = {
        {0, "indigo_iox_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x00D0, 0, 0, 0},      /* Indigo IOx */
        {0,}
 };
index f357521..394c6e7 100644 (file)
@@ -48,7 +48,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_INDIGO_IOX_DSP];
+       chip->dsp_code_to_load = FW_INDIGO_IOX_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -59,10 +59,13 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       err = init_line_levels(chip);
-       if (err < 0)
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
+
+
+
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
index 5061946..8c3f5c5 100644 (file)
@@ -76,7 +76,7 @@ static const struct firmware card_fw[] = {
        {0, "layla20_asic.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x1801, 0xECC0, 0x0030, 0, 0, 0},      /* DSP 56301 Layla20 rev.0 */
        {0x1057, 0x1801, 0xECC0, 0x0031, 0, 0, 0},      /* DSP 56301 Layla20 rev.1 */
        {0,}
index 83750e9..53ce946 100644 (file)
@@ -31,8 +31,7 @@
 
 static int read_dsp(struct echoaudio *chip, u32 *data);
 static int set_professional_spdif(struct echoaudio *chip, char prof);
-static int load_asic_generic(struct echoaudio *chip, u32 cmd,
-                            const struct firmware *asic);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic);
 static int check_asic_status(struct echoaudio *chip);
 static int update_flags(struct echoaudio *chip);
 
@@ -54,7 +53,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
        chip->has_midi = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_LAYLA20_DSP];
+       chip->dsp_code_to_load = FW_LAYLA20_DSP;
        chip->input_clock_types =
                ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
                ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_SUPER;
@@ -65,17 +64,20 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
-       err = set_professional_spdif(chip, TRUE);
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       chip->professional_spdif = FALSE;
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
@@ -144,7 +146,7 @@ static int load_asic(struct echoaudio *chip)
                return 0;
 
        err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA_ASIC,
-                               &card_fw[FW_LAYLA20_ASIC]);
+                               FW_LAYLA20_ASIC);
        if (err < 0)
                return err;
 
index e09e3ea..ed1cc0a 100644 (file)
@@ -87,7 +87,7 @@ static const struct firmware card_fw[] = {
        {0, "layla24_2S_asic.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x0060, 0, 0, 0},      /* DSP 56361 Layla24 rev.0 */
        {0,}
 };
index d61b5cb..8c04164 100644 (file)
@@ -32,8 +32,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force);
 static int set_input_clock(struct echoaudio *chip, u16 clock);
 static int set_professional_spdif(struct echoaudio *chip, char prof);
 static int set_digital_mode(struct echoaudio *chip, u8 mode);
-static int load_asic_generic(struct echoaudio *chip, u32 cmd,
-                            const struct firmware *asic);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic);
 static int check_asic_status(struct echoaudio *chip);
 
 
@@ -54,7 +53,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
        chip->has_midi = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_LAYLA24_DSP];
+       chip->dsp_code_to_load = FW_LAYLA24_DSP;
        chip->input_clock_types =
                ECHO_CLOCK_BIT_INTERNAL | ECHO_CLOCK_BIT_SPDIF |
                ECHO_CLOCK_BIT_WORD | ECHO_CLOCK_BIT_ADAT;
@@ -62,9 +61,6 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_RCA |
                ECHOCAPS_HAS_DIGITAL_MODE_SPDIF_OPTICAL |
                ECHOCAPS_HAS_DIGITAL_MODE_ADAT;
-       chip->digital_mode =            DIGITAL_MODE_SPDIF_RCA;
-       chip->professional_spdif = FALSE;
-       chip->digital_in_automute = TRUE;
 
        if ((err = load_firmware(chip)) < 0)
                return err;
@@ -73,17 +69,22 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        if ((err = init_line_levels(chip)) < 0)
                return err;
 
-       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-       if (err < 0)
-               return err;
-       err = set_professional_spdif(chip, TRUE);
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->digital_in_automute = TRUE;
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
@@ -123,18 +124,18 @@ static int load_asic(struct echoaudio *chip)
 
        /* Load the ASIC for the PCI card */
        err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_PCI_CARD_ASIC,
-                               &card_fw[FW_LAYLA24_1_ASIC]);
+                               FW_LAYLA24_1_ASIC);
        if (err < 0)
                return err;
 
-       chip->asic_code = &card_fw[FW_LAYLA24_2S_ASIC];
+       chip->asic_code = FW_LAYLA24_2S_ASIC;
 
        /* Now give the new ASIC a little time to set up */
        mdelay(10);
 
        /* Do the external one */
        err = load_asic_generic(chip, DSP_FNC_LOAD_LAYLA24_EXTERNAL_ASIC,
-                               &card_fw[FW_LAYLA24_2S_ASIC]);
+                               FW_LAYLA24_2S_ASIC);
        if (err < 0)
                return FALSE;
 
@@ -299,7 +300,7 @@ static int set_input_clock(struct echoaudio *chip, u16 clock)
 /* Depending on what digital mode you want, Layla24 needs different ASICs
 loaded.  This function checks the ASIC needed for the new mode and sees
 if it matches the one already loaded. */
-static int switch_asic(struct echoaudio *chip, const struct firmware *asic)
+static int switch_asic(struct echoaudio *chip, short asic)
 {
        s8 *monitors;
 
@@ -335,7 +336,7 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
 {
        u32 control_reg;
        int err, incompatible_clock;
-       const struct firmware *asic;
+       short asic;
 
        /* Set clock to "internal" if it's not compatible with the new mode */
        incompatible_clock = FALSE;
@@ -344,12 +345,12 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
        case DIGITAL_MODE_SPDIF_RCA:
                if (chip->input_clock == ECHO_CLOCK_ADAT)
                        incompatible_clock = TRUE;
-               asic = &card_fw[FW_LAYLA24_2S_ASIC];
+               asic = FW_LAYLA24_2S_ASIC;
                break;
        case DIGITAL_MODE_ADAT:
                if (chip->input_clock == ECHO_CLOCK_SPDIF)
                        incompatible_clock = TRUE;
-               asic = &card_fw[FW_LAYLA24_2A_ASIC];
+               asic = FW_LAYLA24_2A_ASIC;
                break;
        default:
                DE_ACT(("Digital mode not supported: %d\n", mode));
index f05c8c0..cc2bbfc 100644 (file)
@@ -77,7 +77,7 @@ static const struct firmware card_fw[] = {
        {0, "mia_dsp.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x3410, 0xECC0, 0x0080, 0, 0, 0},      /* DSP 56361 Mia rev.0 */
        {0x1057, 0x3410, 0xECC0, 0x0081, 0, 0, 0},      /* DSP 56361 Mia rev.1 */
        {0,}
index 5514051..6ebfa6e 100644 (file)
@@ -53,7 +53,7 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
        chip->device_id = device_id;
        chip->subdevice_id = subdevice_id;
        chip->bad_board = TRUE;
-       chip->dsp_code_to_load = &card_fw[FW_MIA_DSP];
+       chip->dsp_code_to_load = FW_MIA_DSP;
        /* Since this card has no ASIC, mark it as loaded so everything
           works OK */
        chip->asic_loaded = TRUE;
@@ -66,15 +66,19 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)))
-               return err;
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
index b05bad9..3e7e018 100644 (file)
@@ -92,7 +92,7 @@ static const struct firmware card_fw[] = {
        {0, "mona_2_asic.fw"}
 };
 
-static struct pci_device_id snd_echo_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_echo_ids) = {
        {0x1057, 0x1801, 0xECC0, 0x0070, 0, 0, 0},      /* DSP 56301 Mona rev.0 */
        {0x1057, 0x1801, 0xECC0, 0x0071, 0, 0, 0},      /* DSP 56301 Mona rev.1 */
        {0x1057, 0x1801, 0xECC0, 0x0072, 0, 0, 0},      /* DSP 56301 Mona rev.2 */
index eaa619b..6e6a7eb 100644 (file)
@@ -33,8 +33,7 @@ static int write_control_reg(struct echoaudio *chip, u32 value, char force);
 static int set_input_clock(struct echoaudio *chip, u16 clock);
 static int set_professional_spdif(struct echoaudio *chip, char prof);
 static int set_digital_mode(struct echoaudio *chip, u8 mode);
-static int load_asic_generic(struct echoaudio *chip, u32 cmd,
-                            const struct firmware *asic);
+static int load_asic_generic(struct echoaudio *chip, u32 cmd, short asic);
 static int check_asic_status(struct echoaudio *chip);
 
 
@@ -64,32 +63,30 @@ static int init_hw(struct echoaudio *chip, u16 device_id, u16 subdevice_id)
 
        /* Mona comes in both '301 and '361 flavors */
        if (chip->device_id == DEVICE_ID_56361)
-               chip->dsp_code_to_load = &card_fw[FW_MONA_361_DSP];
+               chip->dsp_code_to_load = FW_MONA_361_DSP;
        else
-               chip->dsp_code_to_load = &card_fw[FW_MONA_301_DSP];
-
-       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
-       chip->professional_spdif = FALSE;
-       chip->digital_in_automute = TRUE;
+               chip->dsp_code_to_load = FW_MONA_301_DSP;
 
        if ((err = load_firmware(chip)) < 0)
                return err;
        chip->bad_board = FALSE;
 
-       if ((err = init_line_levels(chip)) < 0)
-               return err;
-
-       err = set_digital_mode(chip, DIGITAL_MODE_SPDIF_RCA);
-       if (err < 0)
-               return err;
-       err = set_professional_spdif(chip, TRUE);
-
        DE_INIT(("init_hw done\n"));
        return err;
 }
 
 
 
+static int set_mixer_defaults(struct echoaudio *chip)
+{
+       chip->digital_mode = DIGITAL_MODE_SPDIF_RCA;
+       chip->professional_spdif = FALSE;
+       chip->digital_in_automute = TRUE;
+       return init_line_levels(chip);
+}
+
+
+
 static u32 detect_input_clocks(const struct echoaudio *chip)
 {
        u32 clocks_from_dsp, clock_bits;
@@ -120,7 +117,7 @@ static int load_asic(struct echoaudio *chip)
 {
        u32 control_reg;
        int err;
-       const struct firmware *asic;
+       short asic;
 
        if (chip->asic_loaded)
                return 0;
@@ -128,9 +125,9 @@ static int load_asic(struct echoaudio *chip)
        mdelay(10);
 
        if (chip->device_id == DEVICE_ID_56361)
-               asic = &card_fw[FW_MONA_361_1_ASIC48];
+               asic = FW_MONA_361_1_ASIC48;
        else
-               asic = &card_fw[FW_MONA_301_1_ASIC48];
+               asic = FW_MONA_301_1_ASIC48;
 
        err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_PCI_CARD_ASIC, asic);
        if (err < 0)
@@ -141,7 +138,7 @@ static int load_asic(struct echoaudio *chip)
 
        /* Do the external one */
        err = load_asic_generic(chip, DSP_FNC_LOAD_MONA_EXTERNAL_ASIC,
-                               &card_fw[FW_MONA_2_ASIC]);
+                               FW_MONA_2_ASIC);
        if (err < 0)
                return err;
 
@@ -165,22 +162,22 @@ loaded.  This function checks the ASIC needed for the new mode and sees
 if it matches the one already loaded. */
 static int switch_asic(struct echoaudio *chip, char double_speed)
 {
-       const struct firmware *asic;
        int err;
+       short asic;
 
        /* Check the clock detect bits to see if this is
        a single-speed clock or a double-speed clock; load
        a new ASIC if necessary. */
        if (chip->device_id == DEVICE_ID_56361) {
                if (double_speed)
-                       asic = &card_fw[FW_MONA_361_1_ASIC96];
+                       asic = FW_MONA_361_1_ASIC96;
                else
-                       asic = &card_fw[FW_MONA_361_1_ASIC48];
+                       asic = FW_MONA_361_1_ASIC48;
        } else {
                if (double_speed)
-                       asic = &card_fw[FW_MONA_301_1_ASIC96];
+                       asic = FW_MONA_301_1_ASIC96;
                else
-                       asic = &card_fw[FW_MONA_301_1_ASIC48];
+                       asic = FW_MONA_301_1_ASIC48;
        }
 
        if (asic != chip->asic_code) {
@@ -200,7 +197,7 @@ static int switch_asic(struct echoaudio *chip, char double_speed)
 static int set_sample_rate(struct echoaudio *chip, u32 rate)
 {
        u32 control_reg, clock;
-       const struct firmware *asic;
+       short asic;
        char force_write;
 
        /* Only set the clock for internal mode. */
@@ -218,14 +215,14 @@ static int set_sample_rate(struct echoaudio *chip, u32 rate)
                if (chip->digital_mode == DIGITAL_MODE_ADAT)
                        return -EINVAL;
                if (chip->device_id == DEVICE_ID_56361)
-                       asic = &card_fw[FW_MONA_361_1_ASIC96];
+                       asic = FW_MONA_361_1_ASIC96;
                else
-                       asic = &card_fw[FW_MONA_301_1_ASIC96];
+                       asic = FW_MONA_301_1_ASIC96;
        } else {
                if (chip->device_id == DEVICE_ID_56361)
-                       asic = &card_fw[FW_MONA_361_1_ASIC48];
+                       asic = FW_MONA_361_1_ASIC48;
                else
-                       asic = &card_fw[FW_MONA_301_1_ASIC48];
+                       asic = FW_MONA_301_1_ASIC48;
        }
 
        force_write = 0;
@@ -410,8 +407,8 @@ static int dsp_set_digital_mode(struct echoaudio *chip, u8 mode)
        case DIGITAL_MODE_ADAT:
                /* If the current ASIC is the 96KHz ASIC, switch the ASIC
                   and set to 48 KHz */
-               if (chip->asic_code == &card_fw[FW_MONA_361_1_ASIC96] ||
-                   chip->asic_code == &card_fw[FW_MONA_301_1_ASIC96]) {
+               if (chip->asic_code == FW_MONA_361_1_ASIC96 ||
+                   chip->asic_code == FW_MONA_301_1_ASIC96) {
                        set_sample_rate(chip, 48000);
                }
                control_reg |= GML_ADAT_MODE;
index 168af67..4203782 100644 (file)
@@ -76,7 +76,7 @@ MODULE_PARM_DESC(subsystem, "Force card subsystem model.");
 /*
  * Class 0401: 1102:0008 (rev 00) Subsystem: 1102:1001 -> Audigy2 Value  Model:SB0400
  */
-static struct pci_device_id snd_emu10k1_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1_ids) = {
        { PCI_VDEVICE(CREATIVE, 0x0002), 0 },   /* EMU10K1 */
        { PCI_VDEVICE(CREATIVE, 0x0004), 1 },   /* Audigy */
        { PCI_VDEVICE(CREATIVE, 0x0008), 1 },   /* Audigy 2 Value SB0400 */
index 1d369ff..df47f73 100644 (file)
@@ -1605,7 +1605,7 @@ static void __devexit snd_emu10k1x_remove(struct pci_dev *pci)
 }
 
 // PCI IDs
-static struct pci_device_id snd_emu10k1x_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1x_ids) = {
        { PCI_VDEVICE(CREATIVE, 0x0006), 0 },   /* Dell OEM version (EMU10K1) */
        { 0, }
 };
index 2b82c5c..c7fba53 100644 (file)
@@ -443,7 +443,7 @@ struct ensoniq {
 
 static irqreturn_t snd_audiopci_interrupt(int irq, void *dev_id);
 
-static struct pci_device_id snd_audiopci_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_audiopci_ids) = {
 #ifdef CHIP1370
        { PCI_VDEVICE(ENSONIQ, 0x5000), 0, },   /* ES1370 */
 #endif
index fb83e1f..553b752 100644 (file)
@@ -243,7 +243,7 @@ struct es1938 {
 
 static irqreturn_t snd_es1938_interrupt(int irq, void *dev_id);
 
-static struct pci_device_id snd_es1938_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_es1938_ids) = {
        { PCI_VDEVICE(ESS, 0x1969), 0, },   /* Solo-1 */
        { 0, }
 };
index a11f453..ecaea9f 100644 (file)
@@ -551,7 +551,7 @@ struct es1968 {
 
 static irqreturn_t snd_es1968_interrupt(int irq, void *dev_id);
 
-static struct pci_device_id snd_es1968_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_es1968_ids) = {
        /* Maestro 1 */
         { 0x1285, 0x0100, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, TYPE_MAESTRO },
        /* Maestro 2 */
index 83508b3..e1baad7 100644 (file)
@@ -205,7 +205,7 @@ struct fm801 {
 #endif
 };
 
-static struct pci_device_id snd_fm801_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_fm801_ids) = {
        { 0x1319, 0x0801, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* FM801 */
        { 0x5213, 0x0510, PCI_ANY_ID, PCI_ANY_ID, PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0, },   /* Gallant Odyssey Sound 4 */
        { 0, }
index f98b47c..26ceace 100644 (file)
@@ -824,6 +824,9 @@ int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
        struct hda_pincfg *pin;
        unsigned int oldcfg;
 
+       if (get_wcaps_type(get_wcaps(codec, nid)) != AC_WID_PIN)
+               return -EINVAL;
+
        oldcfg = snd_hda_codec_get_pincfg(codec, nid);
        pin = look_up_pincfg(codec, list, nid);
        if (!pin) {
@@ -899,6 +902,25 @@ static void restore_pincfgs(struct hda_codec *codec)
        }
 }
 
+/**
+ * snd_hda_shutup_pins - Shut up all pins
+ * @codec: the HDA codec
+ *
+ * Clear all pin controls to shup up before suspend for avoiding click noise.
+ * The controls aren't cached so that they can be resumed properly.
+ */
+void snd_hda_shutup_pins(struct hda_codec *codec)
+{
+       int i;
+       for (i = 0; i < codec->init_pins.used; i++) {
+               struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
+               /* use read here for syncing after issuing each verb */
+               snd_hda_codec_read(codec, pin->nid, 0,
+                                  AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_shutup_pins);
+
 static void init_hda_cache(struct hda_cache_rec *cache,
                           unsigned int record_size);
 static void free_hda_cache(struct hda_cache_rec *cache);
@@ -931,6 +953,7 @@ static void snd_hda_codec_free(struct hda_codec *codec)
 #endif
        list_del(&codec->list);
        snd_array_free(&codec->mixers);
+       snd_array_free(&codec->nids);
        codec->bus->caddr_tbl[codec->addr] = NULL;
        if (codec->patch_ops.free)
                codec->patch_ops.free(codec);
@@ -985,7 +1008,8 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr
        mutex_init(&codec->control_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
-       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 60);
+       snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
+       snd_array_init(&codec->nids, sizeof(struct hda_nid_item), 32);
        snd_array_init(&codec->init_pins, sizeof(struct hda_pincfg), 16);
        snd_array_init(&codec->driver_pins, sizeof(struct hda_pincfg), 16);
        if (codec->bus->modelname) {
@@ -1708,7 +1732,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
 EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
 
 /**
- * snd_hda_ctl-add - Add a control element and assign to the codec
+ * snd_hda_ctl_add - Add a control element and assign to the codec
  * @codec: HD-audio codec
  * @nid: corresponding NID (optional)
  * @kctl: the control element to assign
@@ -1723,19 +1747,25 @@ EXPORT_SYMBOL_HDA(snd_hda_find_mixer_ctl);
  *
  * snd_hda_ctl_add() checks the control subdev id field whether
  * #HDA_SUBDEV_NID_FLAG bit is set.  If set (and @nid is zero), the lower
- * bits value is taken as the NID to assign.
+ * bits value is taken as the NID to assign. The #HDA_NID_ITEM_AMP bit
+ * specifies if kctl->private_value is a HDA amplifier value.
  */
 int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
                    struct snd_kcontrol *kctl)
 {
        int err;
+       unsigned short flags = 0;
        struct hda_nid_item *item;
 
-       if (kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) {
+       if (kctl->id.subdevice & HDA_SUBDEV_AMP_FLAG) {
+               flags |= HDA_NID_ITEM_AMP;
                if (nid == 0)
-                       nid = kctl->id.subdevice & 0xffff;
-               kctl->id.subdevice = 0;
+                       nid = get_amp_nid_(kctl->private_value);
        }
+       if ((kctl->id.subdevice & HDA_SUBDEV_NID_FLAG) != 0 && nid == 0)
+               nid = kctl->id.subdevice & 0xffff;
+       if (kctl->id.subdevice & (HDA_SUBDEV_NID_FLAG|HDA_SUBDEV_AMP_FLAG))
+               kctl->id.subdevice = 0;
        err = snd_ctl_add(codec->bus->card, kctl);
        if (err < 0)
                return err;
@@ -1744,10 +1774,40 @@ int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
                return -ENOMEM;
        item->kctl = kctl;
        item->nid = nid;
+       item->flags = flags;
        return 0;
 }
 EXPORT_SYMBOL_HDA(snd_hda_ctl_add);
 
+/**
+ * snd_hda_add_nid - Assign a NID to a control element
+ * @codec: HD-audio codec
+ * @nid: corresponding NID (optional)
+ * @kctl: the control element to assign
+ * @index: index to kctl
+ *
+ * Add the given control element to an array inside the codec instance.
+ * This function is used when #snd_hda_ctl_add cannot be used for 1:1
+ * NID:KCTL mapping - for example "Capture Source" selector.
+ */
+int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
+                   unsigned int index, hda_nid_t nid)
+{
+       struct hda_nid_item *item;
+
+       if (nid > 0) {
+               item = snd_array_new(&codec->nids);
+               if (!item)
+                       return -ENOMEM;
+               item->kctl = kctl;
+               item->index = index;
+               item->nid = nid;
+               return 0;
+       }
+       return -EINVAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_add_nid);
+
 /**
  * snd_hda_ctls_clear - Clear all controls assigned to the given codec
  * @codec: HD-audio codec
@@ -1759,6 +1819,7 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
        for (i = 0; i < codec->mixers.used; i++)
                snd_ctl_remove(codec->bus->card, items[i].kctl);
        snd_array_free(&codec->mixers);
+       snd_array_free(&codec->nids);
 }
 
 /* pseudo device locking
@@ -3478,6 +3539,8 @@ int snd_hda_add_new_ctls(struct hda_codec *codec, struct snd_kcontrol_new *knew)
 
        for (; knew->name; knew++) {
                struct snd_kcontrol *kctl;
+               if (knew->iface == -1)  /* skip this codec private value */
+                       continue;
                kctl = snd_ctl_new1(knew, codec);
                if (!kctl)
                        return -ENOMEM;
index 0a770a2..0c8f05c 100644 (file)
@@ -789,6 +789,7 @@ struct hda_codec {
        u32 *wcaps;
 
        struct snd_array mixers;        /* list of assigned mixer elements */
+       struct snd_array nids;          /* list of mapped mixer elements */
 
        struct hda_cache_rec amp_cache; /* cache for amp access */
        struct hda_cache_rec cmd_cache; /* cache for other commands */
@@ -898,6 +899,7 @@ int snd_hda_codec_set_pincfg(struct hda_codec *codec, hda_nid_t nid,
                             unsigned int cfg);
 int snd_hda_add_pincfg(struct hda_codec *codec, struct snd_array *list,
                       hda_nid_t nid, unsigned int cfg); /* for hwdep */
+void snd_hda_shutup_pins(struct hda_codec *codec);
 
 /*
  * Mixer
index 092c6a7..5ea2128 100644 (file)
@@ -861,7 +861,8 @@ static int build_input_controls(struct hda_codec *codec)
        }
 
        /* create input MUX if multiple sources are available */
-       err = snd_hda_ctl_add(codec, 0, snd_ctl_new1(&cap_sel, codec));
+       err = snd_hda_ctl_add(codec, spec->adc_node->nid,
+                             snd_ctl_new1(&cap_sel, codec));
        if (err < 0)
                return err;
 
index 40ccb41..b36919c 100644 (file)
@@ -293,8 +293,11 @@ static ssize_t type##_store(struct device *dev,                    \
 {                                                              \
        struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
        struct hda_codec *codec = hwdep->private_data;          \
-       char *after;                                            \
-       codec->type = simple_strtoul(buf, &after, 0);           \
+       unsigned long val;                                      \
+       int err = strict_strtoul(buf, 0, &val);                 \
+       if (err < 0)                                            \
+               return err;                                     \
+       codec->type = val;                                      \
        return count;                                           \
 }
 
index 3600e9c..8d8e0b5 100644 (file)
@@ -2683,7 +2683,7 @@ static void __devexit azx_remove(struct pci_dev *pci)
 }
 
 /* PCI IDs */
-static struct pci_device_id azx_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
        /* ICH 6..10 */
        { PCI_DEVICE(0x8086, 0x2668), .driver_data = AZX_DRIVER_ICH },
        { PCI_DEVICE(0x8086, 0x27d8), .driver_data = AZX_DRIVER_ICH },
@@ -2723,32 +2723,10 @@ static struct pci_device_id azx_ids[] = {
        /* ULI M5461 */
        { PCI_DEVICE(0x10b9, 0x5461), .driver_data = AZX_DRIVER_ULI },
        /* NVIDIA MCP */
-       { PCI_DEVICE(0x10de, 0x026c), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0371), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x03e4), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x03f0), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x044a), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x044b), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x055c), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x055d), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0590), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0774), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0775), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0776), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0777), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x07fc), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x07fd), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0ac0), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0ac1), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0ac2), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0ac3), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0be2), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0be3), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0be4), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0d94), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0d95), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0d96), .driver_data = AZX_DRIVER_NVIDIA },
-       { PCI_DEVICE(0x10de, 0x0d97), .driver_data = AZX_DRIVER_NVIDIA },
+       { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
+         .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
+         .class_mask = 0xffffff,
+         .driver_data = AZX_DRIVER_NVIDIA },
        /* Teradici */
        { PCI_DEVICE(0x6549, 0x1200), .driver_data = AZX_DRIVER_TERA },
        /* Creative X-Fi (CA0110-IBG) */
index 5778ae8..7cee364 100644 (file)
@@ -31,6 +31,7 @@
  * in snd_hda_ctl_add(), so that this value won't appear in the outside.
  */
 #define HDA_SUBDEV_NID_FLAG    (1U << 31)
+#define HDA_SUBDEV_AMP_FLAG    (1U << 30)
 
 /*
  * for mixer controls
@@ -42,7 +43,7 @@
 /* mono volume with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_VOLUME_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx,  \
-         .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
+         .subdevice = HDA_SUBDEV_AMP_FLAG, \
          .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
                    SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
                    SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
@@ -63,7 +64,7 @@
 /* mono mute switch with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_MUTE_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
-         .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
+         .subdevice = HDA_SUBDEV_AMP_FLAG, \
          .info = snd_hda_mixer_amp_switch_info, \
          .get = snd_hda_mixer_amp_switch_get, \
          .put = snd_hda_mixer_amp_switch_put, \
@@ -81,7 +82,7 @@
 /* special beep mono mute switch with index (index=0,1,...) (channel=1,2) */
 #define HDA_CODEC_MUTE_BEEP_MONO_IDX(xname, xcidx, nid, channel, xindex, direction) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xcidx, \
-         .subdevice = HDA_SUBDEV_NID_FLAG | (nid), \
+         .subdevice = HDA_SUBDEV_AMP_FLAG, \
          .info = snd_hda_mixer_amp_switch_info, \
          .get = snd_hda_mixer_amp_switch_get, \
          .put = snd_hda_mixer_amp_switch_put_beep, \
@@ -464,13 +465,20 @@ u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid);
 u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
 int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
 
+/* flags for hda_nid_item */
+#define HDA_NID_ITEM_AMP       (1<<0)
+
 struct hda_nid_item {
        struct snd_kcontrol *kctl;
+       unsigned int index;
        hda_nid_t nid;
+       unsigned short flags;
 };
 
 int snd_hda_ctl_add(struct hda_codec *codec, hda_nid_t nid,
                    struct snd_kcontrol *kctl);
+int snd_hda_add_nid(struct hda_codec *codec, struct snd_kcontrol *kctl,
+                   unsigned int index, hda_nid_t nid);
 void snd_hda_ctls_clear(struct hda_codec *codec);
 
 /*
index c9afc04..f97d35d 100644 (file)
@@ -61,18 +61,29 @@ static const char *get_wid_type_name(unsigned int wid_value)
                return "UNKNOWN Widget";
 }
 
-static void print_nid_mixers(struct snd_info_buffer *buffer,
-                            struct hda_codec *codec, hda_nid_t nid)
+static void print_nid_array(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid,
+                           struct snd_array *array)
 {
        int i;
-       struct hda_nid_item *items = codec->mixers.list;
+       struct hda_nid_item *items = array->list, *item;
        struct snd_kcontrol *kctl;
-       for (i = 0; i < codec->mixers.used; i++) {
-               if (items[i].nid == nid) {
-                       kctl = items[i].kctl;
+       for (i = 0; i < array->used; i++) {
+               item = &items[i];
+               if (item->nid == nid) {
+                       kctl = item->kctl;
                        snd_iprintf(buffer,
                          "  Control: name=\"%s\", index=%i, device=%i\n",
-                         kctl->id.name, kctl->id.index, kctl->id.device);
+                         kctl->id.name, kctl->id.index + item->index,
+                         kctl->id.device);
+                       if (item->flags & HDA_NID_ITEM_AMP)
+                               snd_iprintf(buffer,
+                                 "    ControlAmp: chs=%lu, dir=%s, "
+                                 "idx=%lu, ofs=%lu\n",
+                                 get_amp_channels(kctl),
+                                 get_amp_direction(kctl) ? "Out" : "In",
+                                 get_amp_index(kctl),
+                                 get_amp_offset(kctl));
                }
        }
 }
@@ -528,7 +539,8 @@ static void print_gpio(struct snd_info_buffer *buffer,
                            (data & (1<<i)) ? 1 : 0,
                            (unsol & (1<<i)) ? 1 : 0);
        /* FIXME: add GPO and GPI pin information */
-       print_nid_mixers(buffer, codec, nid);
+       print_nid_array(buffer, codec, nid, &codec->mixers);
+       print_nid_array(buffer, codec, nid, &codec->nids);
 }
 
 static void print_codec_info(struct snd_info_entry *entry,
@@ -608,7 +620,8 @@ static void print_codec_info(struct snd_info_entry *entry,
                        snd_iprintf(buffer, " CP");
                snd_iprintf(buffer, "\n");
 
-               print_nid_mixers(buffer, codec, nid);
+               print_nid_array(buffer, codec, nid, &codec->mixers);
+               print_nid_array(buffer, codec, nid, &codec->nids);
                print_nid_pcms(buffer, codec, nid);
 
                /* volume knob is a special widget that always have connection
index 69a941c..214301d 100644 (file)
@@ -174,6 +174,7 @@ static struct snd_kcontrol_new ad_beep_mixer[] = {
 static int ad198x_build_controls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
+       struct snd_kcontrol *kctl;
        unsigned int i;
        int err;
 
@@ -208,9 +209,7 @@ static int ad198x_build_controls(struct hda_codec *codec)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->private_value = spec->beep_amp;
-                       err = snd_hda_ctl_add(codec,
-                                               get_amp_nid_(spec->beep_amp),
-                                               kctl);
+                       err = snd_hda_ctl_add(codec, 0, kctl);
                        if (err < 0)
                                return err;
                }
@@ -239,6 +238,27 @@ static int ad198x_build_controls(struct hda_codec *codec)
        }
 
        ad198x_free_kctls(codec); /* no longer needed */
+
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+       if (!kctl)
+               kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       /* assign IEC958 enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec,
+                       SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
+       if (kctl) {
+               err = snd_hda_add_nid(codec, kctl, 0,
+                                     spec->multiout.dig_out_nid);
+               if (err < 0)
+                       return err;
+       }
+
        return 0;
 }
 
@@ -421,6 +441,11 @@ static int ad198x_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
+static inline void ad198x_shutup(struct hda_codec *codec)
+{
+       snd_hda_shutup_pins(codec);
+}
+
 static void ad198x_free_kctls(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -434,6 +459,46 @@ static void ad198x_free_kctls(struct hda_codec *codec)
        snd_array_free(&spec->kctls);
 }
 
+static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
+                               hda_nid_t hp)
+{
+       struct ad198x_spec *spec = codec->spec;
+       snd_hda_codec_write(codec, front, 0, AC_VERB_SET_EAPD_BTLENABLE,
+                           !spec->inv_eapd ? 0x00 : 0x02);
+       snd_hda_codec_write(codec, hp, 0, AC_VERB_SET_EAPD_BTLENABLE,
+                           !spec->inv_eapd ? 0x00 : 0x02);
+}
+
+static void ad198x_power_eapd(struct hda_codec *codec)
+{
+       /* We currently only handle front, HP */
+       switch (codec->vendor_id) {
+       case 0x11d41882:
+       case 0x11d4882a:
+       case 0x11d41884:
+       case 0x11d41984:
+       case 0x11d41883:
+       case 0x11d4184a:
+       case 0x11d4194a:
+       case 0x11d4194b:
+               ad198x_power_eapd_write(codec, 0x12, 0x11);
+               break;
+       case 0x11d41981:
+       case 0x11d41983:
+               ad198x_power_eapd_write(codec, 0x05, 0x06);
+               break;
+       case 0x11d41986:
+               ad198x_power_eapd_write(codec, 0x1b, 0x1a);
+               break;
+       case 0x11d41988:
+       case 0x11d4198b:
+       case 0x11d4989a:
+       case 0x11d4989b:
+               ad198x_power_eapd_write(codec, 0x29, 0x22);
+               break;
+       }
+}
+
 static void ad198x_free(struct hda_codec *codec)
 {
        struct ad198x_spec *spec = codec->spec;
@@ -441,11 +506,29 @@ static void ad198x_free(struct hda_codec *codec)
        if (!spec)
                return;
 
+       ad198x_shutup(codec);
        ad198x_free_kctls(codec);
        kfree(spec);
        snd_hda_detach_beep_device(codec);
 }
 
+#ifdef SND_HDA_NEEDS_RESUME
+static int ad198x_suspend(struct hda_codec *codec, pm_message_t state)
+{
+       ad198x_shutup(codec);
+       ad198x_power_eapd(codec);
+       return 0;
+}
+
+static int ad198x_resume(struct hda_codec *codec)
+{
+       ad198x_init(codec);
+       snd_hda_codec_resume_amp(codec);
+       snd_hda_codec_resume_cache(codec);
+       return 0;
+}
+#endif
+
 static struct hda_codec_ops ad198x_patch_ops = {
        .build_controls = ad198x_build_controls,
        .build_pcms = ad198x_build_pcms,
@@ -454,6 +537,11 @@ static struct hda_codec_ops ad198x_patch_ops = {
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        .check_power_status = ad198x_check_power_status,
 #endif
+#ifdef SND_HDA_NEEDS_RESUME
+       .suspend = ad198x_suspend,
+       .resume = ad198x_resume,
+#endif
+       .reboot_notify = ad198x_shutup,
 };
 
 
@@ -701,6 +789,7 @@ static struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "External Amplifier",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
@@ -808,6 +897,7 @@ static struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = ad1986a_hp_master_sw_put,
@@ -1612,6 +1702,7 @@ static struct snd_kcontrol_new ad1981_hp_mixers[] = {
        HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
                .name = "Master Playback Switch",
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
@@ -2136,6 +2227,7 @@ static struct snd_kcontrol_new ad1988_laptop_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "External Amplifier",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
                .info = ad198x_eapd_info,
                .get = ad198x_eapd_get,
                .put = ad198x_eapd_put,
@@ -2257,6 +2349,7 @@ static struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "IEC958 Playback Source",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
                .info = ad1988_spdif_playback_source_info,
                .get = ad1988_spdif_playback_source_get,
                .put = ad1988_spdif_playback_source_put,
@@ -2372,6 +2465,12 @@ static struct hda_verb ad1988_spdif_init_verbs[] = {
        { }
 };
 
+static struct hda_verb ad1988_spdif_in_init_verbs[] = {
+       /* unmute SPDIF input pin */
+       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
+       { }
+};
+
 /* AD1989 has no ADC -> SPDIF route */
 static struct hda_verb ad1989_spdif_init_verbs[] = {
        /* SPDIF-1 out pin */
@@ -2589,7 +2688,7 @@ static int add_control(struct ad198x_spec *spec, int type, const char *name,
        if (! knew->name)
                return -ENOMEM;
        if (get_amp_nid_(val))
-               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
+               knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
        return 0;
 }
@@ -3107,8 +3206,11 @@ static int patch_ad1988(struct hda_codec *codec)
                                ad1988_spdif_init_verbs;
                }
        }
-       if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a)
+       if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
                spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
+               spec->init_verbs[spec->num_init_verbs++] =
+                       ad1988_spdif_in_init_verbs;
+       }
 
        codec->patch_ops = ad198x_patch_ops;
        switch (board_config) {
@@ -3747,6 +3849,7 @@ static struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = ad1884a_mobile_master_sw_put,
@@ -3775,6 +3878,7 @@ static struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = ad1884a_mobile_master_sw_put,
@@ -4116,6 +4220,7 @@ static struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
 /*     HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .name = "Master Playback Switch",
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
index fe0423c..7de782a 100644 (file)
@@ -501,7 +501,8 @@ static int add_mute(struct hda_codec *codec, const char *name, int index,
        knew.private_value = pval;
        snprintf(tmp, sizeof(tmp), "%s %s Switch", name, dir_sfx[dir]);
        *kctlp = snd_ctl_new1(&knew, codec);
-       return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
+       (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
+       return snd_hda_ctl_add(codec, 0, *kctlp);
 }
 
 static int add_volume(struct hda_codec *codec, const char *name,
@@ -514,7 +515,8 @@ static int add_volume(struct hda_codec *codec, const char *name,
        knew.private_value = pval;
        snprintf(tmp, sizeof(tmp), "%s %s Volume", name, dir_sfx[dir]);
        *kctlp = snd_ctl_new1(&knew, codec);
-       return snd_hda_ctl_add(codec, get_amp_nid_(pval), *kctlp);
+       (*kctlp)->id.subdevice = HDA_SUBDEV_AMP_FLAG;
+       return snd_hda_ctl_add(codec, 0, *kctlp);
 }
 
 static void fix_volume_caps(struct hda_codec *codec, hda_nid_t dac)
@@ -751,6 +753,7 @@ static int build_input(struct hda_codec *codec)
        spec->capture_bind[1] = make_bind_capture(codec, &snd_hda_bind_vol);
        for (i = 0; i < 2; i++) {
                struct snd_kcontrol *kctl;
+               int n;
                if (!spec->capture_bind[i])
                        return -ENOMEM;
                kctl = snd_ctl_new1(&cs_capture_ctls[i], codec);
@@ -760,6 +763,13 @@ static int build_input(struct hda_codec *codec)
                err = snd_hda_ctl_add(codec, 0, kctl);
                if (err < 0)
                        return err;
+               for (n = 0; n < AUTO_PIN_LAST; n++) {
+                       if (!spec->adc_nid[n])
+                               continue;
+                       err = snd_hda_add_nid(codec, kctl, 0, spec->adc_nid[i]);
+                       if (err < 0)
+                               return err;
+               }
        }
        
        if (spec->num_inputs > 1 && !spec->mic_detect) {
index a45c116..ff60908 100644 (file)
@@ -315,7 +315,8 @@ static struct hda_verb cmi9880_allout_init[] = {
 static int cmi9880_build_controls(struct hda_codec *codec)
 {
        struct cmi_spec *spec = codec->spec;
-       int err;
+       struct snd_kcontrol *kctl;
+       int i, err;
 
        err = snd_hda_add_new_ctls(codec, cmi9880_basic_mixer);
        if (err < 0)
@@ -340,6 +341,14 @@ static int cmi9880_build_controls(struct hda_codec *codec)
                if (err < 0)
                        return err;
        }
+
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nid(codec, kctl, i, spec->adc_nids[i]);
+               if (err < 0)
+                       return err;
+       }
        return 0;
 }
 
index c578c28..685015a 100644 (file)
@@ -111,8 +111,22 @@ struct conexant_spec {
 
        unsigned int dell_automute;
        unsigned int port_d_mode;
-       unsigned char ext_mic_bias;
        unsigned int dell_vostro;
+
+       unsigned int ext_mic_present;
+       unsigned int recording;
+       void (*capture_prepare)(struct hda_codec *codec);
+       void (*capture_cleanup)(struct hda_codec *codec);
+
+       /* OLPC XO-1.5 supports DC input mode (e.g. for use with analog sensors)
+        * through the microphone jack.
+        * When the user enables this through a mixer switch, both internal and
+        * external microphones are disabled. Gain is fixed at 0dB. In this mode,
+        * we also allow the bias to be configured through a separate mixer
+        * control. */
+       unsigned int dc_enable;
+       unsigned int dc_input_bias; /* offset into cxt5066_olpc_dc_bias */
+       unsigned int mic_boost; /* offset into cxt5066_analog_mic_boost */
 };
 
 static int conexant_playback_pcm_open(struct hda_pcm_stream *hinfo,
@@ -185,6 +199,8 @@ static int conexant_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
                                      struct snd_pcm_substream *substream)
 {
        struct conexant_spec *spec = codec->spec;
+       if (spec->capture_prepare)
+               spec->capture_prepare(codec);
        snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
                                   stream_tag, 0, format);
        return 0;
@@ -196,6 +212,8 @@ static int conexant_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
 {
        struct conexant_spec *spec = codec->spec;
        snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
+       if (spec->capture_cleanup)
+               spec->capture_cleanup(codec);
        return 0;
 }
 
@@ -1723,6 +1741,22 @@ static struct snd_kcontrol_new cxt5051_hp_dv6736_mixers[] = {
        {}
 };
 
+static struct snd_kcontrol_new cxt5051_f700_mixers[] = {
+       HDA_CODEC_VOLUME("Mic Volume", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_MUTE("Mic Switch", 0x14, 0x01, HDA_INPUT),
+       HDA_CODEC_VOLUME("Master Playback Volume", 0x10, 0x00, HDA_OUTPUT),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Master Playback Switch",
+               .info = cxt_eapd_info,
+               .get = cxt_eapd_get,
+               .put = cxt5051_hp_master_sw_put,
+               .private_value = 0x1a,
+       },
+
+       {}
+};
+
 static struct hda_verb cxt5051_init_verbs[] = {
        /* Line in, Mic */
        {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0) | 0x03},
@@ -1813,6 +1847,32 @@ static struct hda_verb cxt5051_lenovo_x200_init_verbs[] = {
        { } /* end */
 };
 
+static struct hda_verb cxt5051_f700_init_verbs[] = {
+       /* Line in, Mic */
+       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x03},
+       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0},
+       /* SPK  */
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
+       {0x1a, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* HP, Amp  */
+       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
+       {0x16, AC_VERB_SET_CONNECT_SEL, 0x00},
+       /* DAC1 */
+       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+       /* Record selector: Int mic */
+       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1) | 0x44},
+       {0x14, AC_VERB_SET_CONNECT_SEL, 0x1},
+       /* SPDIF route: PCM */
+       {0x1c, AC_VERB_SET_CONNECT_SEL, 0x0},
+       /* EAPD */
+       {0x1a, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
+       {0x16, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CONEXANT_HP_EVENT},
+       {0x17, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN|CXT5051_PORTB_EVENT},
+       { } /* end */
+};
+
 /* initialize jack-sensing, too */
 static int cxt5051_init(struct hda_codec *codec)
 {
@@ -1832,6 +1892,7 @@ enum {
        CXT5051_HP,     /* no docking */
        CXT5051_HP_DV6736,      /* HP without mic switch */
        CXT5051_LENOVO_X200,    /* Lenovo X200 laptop */
+       CXT5051_F700,       /* HP Compaq Presario F700 */
        CXT5051_MODELS
 };
 
@@ -1840,6 +1901,7 @@ static const char *cxt5051_models[CXT5051_MODELS] = {
        [CXT5051_HP]            = "hp",
        [CXT5051_HP_DV6736]     = "hp-dv6736",
        [CXT5051_LENOVO_X200]   = "lenovo-x200",
+       [CXT5051_F700]          = "hp 700"
 };
 
 static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
@@ -1849,6 +1911,7 @@ static struct snd_pci_quirk cxt5051_cfg_tbl[] = {
                      CXT5051_LAPTOP),
        SND_PCI_QUIRK(0x14f1, 0x5051, "HP Spartan 1.1", CXT5051_HP),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo X200", CXT5051_LENOVO_X200),
+       SND_PCI_QUIRK(0x103c, 0x30ea, "Compaq Presario F700", CXT5051_F700),
        {}
 };
 
@@ -1899,6 +1962,11 @@ static int patch_cxt5051(struct hda_codec *codec)
        case CXT5051_LENOVO_X200:
                spec->init_verbs[0] = cxt5051_lenovo_x200_init_verbs;
                break;
+       case CXT5051_F700:
+               spec->init_verbs[0] = cxt5051_f700_init_verbs;
+               spec->mixers[0] = cxt5051_f700_mixers;
+               spec->no_auto_mic = 1;
+               break;
        }
 
        return 0;
@@ -1966,53 +2034,97 @@ static int cxt5066_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
-/* toggle input of built-in and mic jack appropriately */
-static void cxt5066_automic(struct hda_codec *codec)
+static const struct hda_input_mux cxt5066_olpc_dc_bias = {
+       .num_items = 3,
+       .items = {
+               { "Off", PIN_IN },
+               { "50%", PIN_VREF50 },
+               { "80%", PIN_VREF80 },
+       },
+};
+
+static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
-       struct hda_verb ext_mic_present[] = {
-               /* enable external mic, port B */
-               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+       /* Even though port F is the DC input, the bias is controlled on port B.
+        * we also leave that port as an active input (but unselected) in DC mode
+        * just in case that is necessary to make the bias setting take effect. */
+       return snd_hda_codec_write_cache(codec, 0x1a, 0,
+               AC_VERB_SET_PIN_WIDGET_CONTROL,
+               cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
+}
 
-               /* switch to external mic input */
-               {0x17, AC_VERB_SET_CONNECT_SEL, 0},
+/* OLPC defers mic widget control until when capture is started because the
+ * microphone LED comes on as soon as these settings are put in place. if we
+ * did this before recording, it would give the false indication that recording
+ * is happening when it is not. */
+static void cxt5066_olpc_select_mic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       if (!spec->recording)
+               return;
 
-               /* disable internal mic, port C */
-               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-               {}
-       };
-       static struct hda_verb ext_mic_absent[] = {
-               /* enable internal mic, port C */
-               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       if (spec->dc_enable) {
+               /* in DC mode we ignore presence detection and just use the jack
+                * through our special DC port */
+               const struct hda_verb enable_dc_mode[] = {
+                       /* disble internal mic, port C */
+                       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+                       /* enable DC capture, port F */
+                       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
+                       {},
+               };
+
+               snd_hda_sequence_write(codec, enable_dc_mode);
+               /* port B input disabled (and bias set) through the following call */
+               cxt5066_set_olpc_dc_bias(codec);
+               return;
+       }
 
-               /* switch to internal mic input */
-               {0x17, AC_VERB_SET_CONNECT_SEL, 1},
+       /* disable DC (port F) */
+       snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
 
-               /* disable external mic, port B */
-               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
-               {}
-       };
+       /* external mic, port B */
+       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+               spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
+
+       /* internal mic, port C */
+       snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+               spec->ext_mic_present ? 0 : PIN_VREF80);
+}
+
+/* toggle input of built-in and mic jack appropriately */
+static void cxt5066_olpc_automic(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
        unsigned int present;
 
-       present = snd_hda_jack_detect(codec, 0x1a);
-       if (present) {
+       if (spec->dc_enable) /* don't do presence detection in DC mode */
+               return;
+
+       present = snd_hda_codec_read(codec, 0x1a, 0,
+                                    AC_VERB_GET_PIN_SENSE, 0) & 0x80000000;
+       if (present)
                snd_printdd("CXT5066: external microphone detected\n");
-               snd_hda_sequence_write(codec, ext_mic_present);
-       } else {
+       else
                snd_printdd("CXT5066: external microphone absent\n");
-               snd_hda_sequence_write(codec, ext_mic_absent);
-       }
+
+       snd_hda_codec_write(codec, 0x17, 0, AC_VERB_SET_CONNECT_SEL,
+               present ? 0 : 1);
+       spec->ext_mic_present = !!present;
+
+       cxt5066_olpc_select_mic(codec);
 }
 
 /* toggle input of built-in digital mic and mic jack appropriately */
 static void cxt5066_vostro_automic(struct hda_codec *codec)
 {
-       struct conexant_spec *spec = codec->spec;
        unsigned int present;
 
        struct hda_verb ext_mic_present[] = {
                /* enable external mic, port B */
-               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, spec->ext_mic_bias},
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
 
                /* switch to external mic input */
                {0x17, AC_VERB_SET_CONNECT_SEL, 0},
@@ -2063,15 +2175,18 @@ static void cxt5066_hp_automute(struct hda_codec *codec)
 }
 
 /* unsolicited event for jack sensing */
-static void cxt5066_unsol_event(struct hda_codec *codec, unsigned int res)
+static void cxt5066_olpc_unsol_event(struct hda_codec *codec, unsigned int res)
 {
+       struct conexant_spec *spec = codec->spec;
        snd_printdd("CXT5066: unsol event %x (%x)\n", res, res >> 26);
        switch (res >> 26) {
        case CONEXANT_HP_EVENT:
                cxt5066_hp_automute(codec);
                break;
        case CONEXANT_MIC_EVENT:
-               cxt5066_automic(codec);
+               /* ignore mic events in DC mode; we're always using the jack */
+               if (!spec->dc_enable)
+                       cxt5066_olpc_automic(codec);
                break;
        }
 }
@@ -2101,6 +2216,15 @@ static const struct hda_input_mux cxt5066_analog_mic_boost = {
        },
 };
 
+static int cxt5066_set_mic_boost(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       return snd_hda_codec_write_cache(codec, 0x17, 0,
+               AC_VERB_SET_AMP_GAIN_MUTE,
+               AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | AC_AMP_SET_OUTPUT |
+                       cxt5066_analog_mic_boost.items[spec->mic_boost].index);
+}
+
 static int cxt5066_mic_boost_mux_enum_info(struct snd_kcontrol *kcontrol,
                                           struct snd_ctl_elem_info *uinfo)
 {
@@ -2111,15 +2235,8 @@ static int cxt5066_mic_boost_mux_enum_get(struct snd_kcontrol *kcontrol,
                                          struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int val;
-       hda_nid_t nid = kcontrol->private_value & 0xff;
-       int inout = (kcontrol->private_value & 0x100) ?
-               AC_AMP_GET_INPUT : AC_AMP_GET_OUTPUT;
-
-       val = snd_hda_codec_read(codec, nid, 0,
-               AC_VERB_GET_AMP_GAIN_MUTE, inout);
-
-       ucontrol->value.enumerated.item[0] = val & AC_AMP_GAIN;
+       struct conexant_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->mic_boost;
        return 0;
 }
 
@@ -2127,26 +2244,132 @@ static int cxt5066_mic_boost_mux_enum_put(struct snd_kcontrol *kcontrol,
                                          struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
        const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
        unsigned int idx;
-       hda_nid_t nid = kcontrol->private_value & 0xff;
-       int inout = (kcontrol->private_value & 0x100) ?
-               AC_AMP_SET_INPUT : AC_AMP_SET_OUTPUT;
+       idx = ucontrol->value.enumerated.item[0];
+       if (idx >= imux->num_items)
+               idx = imux->num_items - 1;
+
+       spec->mic_boost = idx;
+       if (!spec->dc_enable)
+               cxt5066_set_mic_boost(codec);
+       return 1;
+}
+
+static void cxt5066_enable_dc(struct hda_codec *codec)
+{
+       const struct hda_verb enable_dc_mode[] = {
+               /* disable gain */
+               {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
+
+               /* switch to DC input */
+               {0x17, AC_VERB_SET_CONNECT_SEL, 3},
+               {}
+       };
+
+       /* configure as input source */
+       snd_hda_sequence_write(codec, enable_dc_mode);
+       cxt5066_olpc_select_mic(codec); /* also sets configured bias */
+}
+
+static void cxt5066_disable_dc(struct hda_codec *codec)
+{
+       /* reconfigure input source */
+       cxt5066_set_mic_boost(codec);
+       /* automic also selects the right mic if we're recording */
+       cxt5066_olpc_automic(codec);
+}
+
+static int cxt5066_olpc_dc_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       ucontrol->value.integer.value[0] = spec->dc_enable;
+       return 0;
+}
+
+static int cxt5066_olpc_dc_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       int dc_enable = !!ucontrol->value.integer.value[0];
 
-       if (!imux->num_items)
+       if (dc_enable == spec->dc_enable)
                return 0;
+
+       spec->dc_enable = dc_enable;
+       if (dc_enable)
+               cxt5066_enable_dc(codec);
+       else
+               cxt5066_disable_dc(codec);
+
+       return 1;
+}
+
+static int cxt5066_olpc_dc_bias_enum_info(struct snd_kcontrol *kcontrol,
+                                          struct snd_ctl_elem_info *uinfo)
+{
+       return snd_hda_input_mux_info(&cxt5066_olpc_dc_bias, uinfo);
+}
+
+static int cxt5066_olpc_dc_bias_enum_get(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       ucontrol->value.enumerated.item[0] = spec->dc_input_bias;
+       return 0;
+}
+
+static int cxt5066_olpc_dc_bias_enum_put(struct snd_kcontrol *kcontrol,
+                                         struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct conexant_spec *spec = codec->spec;
+       const struct hda_input_mux *imux = &cxt5066_analog_mic_boost;
+       unsigned int idx;
+
        idx = ucontrol->value.enumerated.item[0];
        if (idx >= imux->num_items)
                idx = imux->num_items - 1;
 
-       snd_hda_codec_write_cache(codec, nid, 0,
-               AC_VERB_SET_AMP_GAIN_MUTE,
-               AC_AMP_SET_RIGHT | AC_AMP_SET_LEFT | inout |
-                       imux->items[idx].index);
-
+       spec->dc_input_bias = idx;
+       if (spec->dc_enable)
+               cxt5066_set_olpc_dc_bias(codec);
        return 1;
 }
 
+static void cxt5066_olpc_capture_prepare(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       /* mark as recording and configure the microphone widget so that the
+        * recording LED comes on. */
+       spec->recording = 1;
+       cxt5066_olpc_select_mic(codec);
+}
+
+static void cxt5066_olpc_capture_cleanup(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       const struct hda_verb disable_mics[] = {
+               /* disable external mic, port B */
+               {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+               /* disble internal mic, port C */
+               {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+
+               /* disable DC capture, port F */
+               {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
+               {},
+       };
+
+       snd_hda_sequence_write(codec, disable_mics);
+       spec->recording = 0;
+}
+
 static struct hda_input_mux cxt5066_capture_source = {
        .num_items = 4,
        .items = {
@@ -2187,6 +2410,7 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
                .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
                                  SNDRV_CTL_ELEM_ACCESS_TLV_READ |
                                  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK,
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_volume_info,
                .get = snd_hda_mixer_amp_volume_get,
                .put = snd_hda_mixer_amp_volume_put,
@@ -2198,6 +2422,24 @@ static struct snd_kcontrol_new cxt5066_mixer_master_olpc[] = {
        {}
 };
 
+static struct snd_kcontrol_new cxt5066_mixer_olpc_dc[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DC Mode Enable Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = cxt5066_olpc_dc_get,
+               .put = cxt5066_olpc_dc_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "DC Input Bias Enum",
+               .info = cxt5066_olpc_dc_bias_enum_info,
+               .get = cxt5066_olpc_dc_bias_enum_get,
+               .put = cxt5066_olpc_dc_bias_enum_put,
+       },
+       {}
+};
+
 static struct snd_kcontrol_new cxt5066_mixers[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -2210,11 +2452,10 @@ static struct snd_kcontrol_new cxt5066_mixers[] = {
 
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Ext Mic Boost Capture Enum",
+               .name = "Analog Mic Boost Capture Enum",
                .info = cxt5066_mic_boost_mux_enum_info,
                .get = cxt5066_mic_boost_mux_enum_get,
                .put = cxt5066_mic_boost_mux_enum_put,
-               .private_value = 0x17,
        },
 
        HDA_BIND_VOL("Capture Volume", &cxt5066_bind_capture_vol_others),
@@ -2296,10 +2537,10 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
        {0x19, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC1 */
 
        /* Port B: external microphone */
-       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, CXT5066_OLPC_EXT_MIC_BIAS},
+       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
 
        /* Port C: internal microphone */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
+       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
 
        /* Port D: unused */
        {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
@@ -2308,7 +2549,7 @@ static struct hda_verb cxt5066_init_verbs_olpc[] = {
        {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
        {0x1d, AC_VERB_SET_EAPD_BTLENABLE, 0x2}, /* default on */
 
-       /* Port F: unused */
+       /* Port F: external DC input through microphone port */
        {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0},
 
        /* Port G: internal speakers */
@@ -2428,8 +2669,22 @@ static int cxt5066_init(struct hda_codec *codec)
                cxt5066_hp_automute(codec);
                if (spec->dell_vostro)
                        cxt5066_vostro_automic(codec);
-               else
-                       cxt5066_automic(codec);
+       }
+       cxt5066_set_mic_boost(codec);
+       return 0;
+}
+
+static int cxt5066_olpc_init(struct hda_codec *codec)
+{
+       struct conexant_spec *spec = codec->spec;
+       snd_printdd("CXT5066: init\n");
+       conexant_init(codec);
+       cxt5066_hp_automute(codec);
+       if (!spec->dc_enable) {
+               cxt5066_set_mic_boost(codec);
+               cxt5066_olpc_automic(codec);
+       } else {
+               cxt5066_enable_dc(codec);
        }
        return 0;
 }
@@ -2470,7 +2725,7 @@ static int patch_cxt5066(struct hda_codec *codec)
        codec->spec = spec;
 
        codec->patch_ops = conexant_patch_ops;
-       codec->patch_ops.init = cxt5066_init;
+       codec->patch_ops.init = conexant_init;
 
        spec->dell_automute = 0;
        spec->multiout.max_channels = 2;
@@ -2483,7 +2738,6 @@ static int patch_cxt5066(struct hda_codec *codec)
        spec->input_mux = &cxt5066_capture_source;
 
        spec->port_d_mode = PIN_HP;
-       spec->ext_mic_bias = PIN_VREF80;
 
        spec->num_init_verbs = 1;
        spec->init_verbs[0] = cxt5066_init_verbs;
@@ -2510,20 +2764,28 @@ static int patch_cxt5066(struct hda_codec *codec)
                spec->dell_automute = 1;
                break;
        case CXT5066_OLPC_XO_1_5:
-               codec->patch_ops.unsol_event = cxt5066_unsol_event;
+               codec->patch_ops.init = cxt5066_olpc_init;
+               codec->patch_ops.unsol_event = cxt5066_olpc_unsol_event;
                spec->init_verbs[0] = cxt5066_init_verbs_olpc;
                spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
+               spec->mixers[spec->num_mixers++] = cxt5066_mixer_olpc_dc;
                spec->mixers[spec->num_mixers++] = cxt5066_mixers;
                spec->port_d_mode = 0;
-               spec->ext_mic_bias = CXT5066_OLPC_EXT_MIC_BIAS;
+               spec->mic_boost = 3; /* default 30dB gain */
 
                /* no S/PDIF out */
                spec->multiout.dig_out_nid = 0;
 
                /* input source automatically selected */
                spec->input_mux = NULL;
+
+               /* our capture hooks which allow us to turn on the microphone LED
+                * at the right time */
+               spec->capture_prepare = cxt5066_olpc_capture_prepare;
+               spec->capture_cleanup = cxt5066_olpc_capture_cleanup;
                break;
        case CXT5066_DELL_VOSTO:
+               codec->patch_ops.init = cxt5066_init;
                codec->patch_ops.unsol_event = cxt5066_vostro_event;
                spec->init_verbs[0] = cxt5066_init_verbs_vostro;
                spec->mixers[spec->num_mixers++] = cxt5066_mixer_master_olpc;
@@ -2531,6 +2793,7 @@ static int patch_cxt5066(struct hda_codec *codec)
                spec->mixers[spec->num_mixers++] = cxt5066_vostro_mixers;
                spec->port_d_mode = 0;
                spec->dell_vostro = 1;
+               spec->mic_boost = 3; /* default 30dB gain */
                snd_hda_attach_beep_device(codec, 0x13);
 
                /* no S/PDIF out */
index da34095..f628c33 100644 (file)
@@ -338,7 +338,7 @@ struct alc_spec {
        void (*init_hook)(struct hda_codec *codec);
        void (*unsol_event)(struct hda_codec *codec, unsigned int res);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-       void (*power_hook)(struct hda_codec *codec, int power);
+       void (*power_hook)(struct hda_codec *codec);
 #endif
 
        /* for pin sensing */
@@ -391,7 +391,7 @@ struct alc_config_preset {
        void (*init_hook)(struct hda_codec *);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        struct hda_amp_list *loopbacks;
-       void (*power_hook)(struct hda_codec *codec, int power);
+       void (*power_hook)(struct hda_codec *codec);
 #endif
 };
 
@@ -633,6 +633,7 @@ static int alc_pin_mode_put(struct snd_kcontrol *kcontrol,
 
 #define ALC_PIN_MODE(xname, nid, dir) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_pin_mode_info, \
          .get = alc_pin_mode_get, \
          .put = alc_pin_mode_put, \
@@ -684,6 +685,7 @@ static int alc_gpio_data_put(struct snd_kcontrol *kcontrol,
 }
 #define ALC_GPIO_DATA_SWITCH(xname, nid, mask) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_gpio_data_info, \
          .get = alc_gpio_data_get, \
          .put = alc_gpio_data_put, \
@@ -738,6 +740,7 @@ static int alc_spdif_ctrl_put(struct snd_kcontrol *kcontrol,
 }
 #define ALC_SPDIF_CTRL_SWITCH(xname, nid, mask) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_spdif_ctrl_info, \
          .get = alc_spdif_ctrl_get, \
          .put = alc_spdif_ctrl_put, \
@@ -791,6 +794,7 @@ static int alc_eapd_ctrl_put(struct snd_kcontrol *kcontrol,
 
 #define ALC_EAPD_CTRL_SWITCH(xname, nid, mask) \
        { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = 0,  \
+         .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
          .info = alc_eapd_ctrl_info, \
          .get = alc_eapd_ctrl_get, \
          .put = alc_eapd_ctrl_put, \
@@ -1840,14 +1844,6 @@ static void alc889_acer_aspire_8930g_setup(struct hda_codec *codec)
        spec->autocfg.speaker_pins[2] = 0x1b;
 }
 
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-static void alc889_power_eapd(struct hda_codec *codec, int power)
-{
-       set_eapd(codec, 0x14, power);
-       set_eapd(codec, 0x15, power);
-}
-#endif
-
 /*
  * ALC880 3-stack model
  *
@@ -2450,6 +2446,15 @@ static const char *alc_slave_sws[] = {
  * build control elements
  */
 
+#define NID_MAPPING            (-1)
+
+#define SUBDEV_SPEAKER_                (0 << 6)
+#define SUBDEV_HP_             (1 << 6)
+#define SUBDEV_LINE_           (2 << 6)
+#define SUBDEV_SPEAKER(x)      (SUBDEV_SPEAKER_ | ((x) & 0x3f))
+#define SUBDEV_HP(x)           (SUBDEV_HP_ | ((x) & 0x3f))
+#define SUBDEV_LINE(x)         (SUBDEV_LINE_ | ((x) & 0x3f))
+
 static void alc_free_kctls(struct hda_codec *codec);
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
@@ -2464,8 +2469,11 @@ static struct snd_kcontrol_new alc_beep_mixer[] = {
 static int alc_build_controls(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       int err;
-       int i;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new *knew;
+       int i, j, err;
+       unsigned int u;
+       hda_nid_t nid;
 
        for (i = 0; i < spec->num_mixers; i++) {
                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -2506,8 +2514,7 @@ static int alc_build_controls(struct hda_codec *codec)
                        if (!kctl)
                                return -ENOMEM;
                        kctl->private_value = spec->beep_amp;
-                       err = snd_hda_ctl_add(codec,
-                                       get_amp_nid_(spec->beep_amp), kctl);
+                       err = snd_hda_ctl_add(codec, 0, kctl);
                        if (err < 0)
                                return err;
                }
@@ -2534,6 +2541,75 @@ static int alc_build_controls(struct hda_codec *codec)
        }
 
        alc_free_kctls(codec); /* no longer needed */
+
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
+       if (!kctl)
+               kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               hda_nid_t *nids = spec->capsrc_nids;
+               if (!nids)
+                       nids = spec->adc_nids;
+               err = snd_hda_add_nid(codec, kctl, i, nids[i]);
+               if (err < 0)
+                       return err;
+       }
+       if (spec->cap_mixer) {
+               const char *kname = kctl ? kctl->id.name : NULL;
+               for (knew = spec->cap_mixer; knew->name; knew++) {
+                       if (kname && strcmp(knew->name, kname) == 0)
+                               continue;
+                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+                       for (i = 0; kctl && i < kctl->count; i++) {
+                               err = snd_hda_add_nid(codec, kctl, i,
+                                                     spec->adc_nids[i]);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
+
+       /* other nid->control mapping */
+       for (i = 0; i < spec->num_mixers; i++) {
+               for (knew = spec->mixers[i]; knew->name; knew++) {
+                       if (knew->iface != NID_MAPPING)
+                               continue;
+                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+                       if (kctl == NULL)
+                               continue;
+                       u = knew->subdevice;
+                       for (j = 0; j < 4; j++, u >>= 8) {
+                               nid = u & 0x3f;
+                               if (nid == 0)
+                                       continue;
+                               switch (u & 0xc0) {
+                               case SUBDEV_SPEAKER_:
+                                       nid = spec->autocfg.speaker_pins[nid];
+                                       break;
+                               case SUBDEV_LINE_:
+                                       nid = spec->autocfg.line_out_pins[nid];
+                                       break;
+                               case SUBDEV_HP_:
+                                       nid = spec->autocfg.hp_pins[nid];
+                                       break;
+                               default:
+                                       continue;
+                               }
+                               err = snd_hda_add_nid(codec, kctl, 0, nid);
+                               if (err < 0)
+                                       return err;
+                       }
+                       u = knew->private_value;
+                       for (j = 0; j < 4; j++, u >>= 8) {
+                               nid = u & 0xff;
+                               if (nid == 0)
+                                       continue;
+                               err = snd_hda_add_nid(codec, kctl, 0, nid);
+                               if (err < 0)
+                                       return err;
+                       }
+               }
+       }
        return 0;
 }
 
@@ -3616,6 +3692,11 @@ static int alc_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
+static inline void alc_shutup(struct hda_codec *codec)
+{
+       snd_hda_shutup_pins(codec);
+}
+
 static void alc_free_kctls(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
@@ -3636,17 +3717,47 @@ static void alc_free(struct hda_codec *codec)
        if (!spec)
                return;
 
+       alc_shutup(codec);
        alc_free_kctls(codec);
        kfree(spec);
        snd_hda_detach_beep_device(codec);
 }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+static void alc_power_eapd(struct hda_codec *codec)
+{
+       /* We currently only handle front, HP */
+       switch (codec->vendor_id) {
+       case 0x10ec0260:
+               snd_hda_codec_write(codec, 0x0f, 0,
+                                   AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+               snd_hda_codec_write(codec, 0x10, 0,
+                                   AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+               break;
+       case 0x10ec0262:
+       case 0x10ec0267:
+       case 0x10ec0268:
+       case 0x10ec0269:
+       case 0x10ec0272:
+       case 0x10ec0660:
+       case 0x10ec0662:
+       case 0x10ec0663:
+       case 0x10ec0862:
+       case 0x10ec0889:
+               snd_hda_codec_write(codec, 0x14, 0,
+                                   AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+               snd_hda_codec_write(codec, 0x15, 0,
+                                   AC_VERB_SET_EAPD_BTLENABLE, 0x00);
+               break;
+       }
+}
+
 static int alc_suspend(struct hda_codec *codec, pm_message_t state)
 {
        struct alc_spec *spec = codec->spec;
+       alc_shutup(codec);
        if (spec && spec->power_hook)
-               spec->power_hook(codec, 0);
+               spec->power_hook(codec);
        return 0;
 }
 #endif
@@ -3654,16 +3765,9 @@ static int alc_suspend(struct hda_codec *codec, pm_message_t state)
 #ifdef SND_HDA_NEEDS_RESUME
 static int alc_resume(struct hda_codec *codec)
 {
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       struct alc_spec *spec = codec->spec;
-#endif
        codec->patch_ops.init(codec);
        snd_hda_codec_resume_amp(codec);
        snd_hda_codec_resume_cache(codec);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       if (spec && spec->power_hook)
-               spec->power_hook(codec, 1);
-#endif
        return 0;
 }
 #endif
@@ -3683,6 +3787,7 @@ static struct hda_codec_ops alc_patch_ops = {
        .suspend = alc_suspend,
        .check_power_status = alc_check_power_status,
 #endif
+       .reboot_notify = alc_shutup,
 };
 
 
@@ -3839,6 +3944,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
 #define PIN_CTL_TEST(xname,nid) {                      \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,    \
                        .name = xname,                 \
+                       .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
                        .info = alc_test_pin_ctl_info, \
                        .get = alc_test_pin_ctl_get,   \
                        .put = alc_test_pin_ctl_put,   \
@@ -3848,6 +3954,7 @@ static int alc_test_pin_src_put(struct snd_kcontrol *kcontrol,
 #define PIN_SRC_TEST(xname,nid) {                      \
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,    \
                        .name = xname,                 \
+                       .subdevice = HDA_SUBDEV_NID_FLAG | nid, \
                        .info = alc_test_pin_src_info, \
                        .get = alc_test_pin_src_get,   \
                        .put = alc_test_pin_src_put,   \
@@ -4387,7 +4494,7 @@ static int add_control(struct alc_spec *spec, int type, const char *name,
        if (!knew->name)
                return -ENOMEM;
        if (get_amp_nid_(val))
-               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
+               knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
        return 0;
 }
@@ -5182,6 +5289,7 @@ static struct snd_kcontrol_new alc260_hp_output_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
@@ -5220,6 +5328,7 @@ static struct snd_kcontrol_new alc260_hp_3013_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_NID_FLAG | 0x11,
                .info = snd_ctl_boolean_mono_info,
                .get = alc260_hp_master_sw_get,
                .put = alc260_hp_master_sw_put,
@@ -9528,7 +9637,7 @@ static struct alc_config_preset alc882_presets[] = {
                .setup = alc889_acer_aspire_8930g_setup,
                .init_hook = alc_automute_amp,
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-               .power_hook = alc889_power_eapd,
+               .power_hook = alc_power_eapd,
 #endif
        },
        [ALC888_ACER_ASPIRE_7730G] = {
@@ -10324,8 +10433,14 @@ static int alc262_hp_master_sw_put(struct snd_kcontrol *kcontrol,
                .info = snd_ctl_boolean_mono_info,              \
                .get = alc262_hp_master_sw_get,                 \
                .put = alc262_hp_master_sw_put,                 \
+       }, \
+       {                                                       \
+               .iface = NID_MAPPING,                           \
+               .name = "Master Playback Switch",               \
+               .private_value = 0x15 | (0x16 << 8) | (0x1b << 16),     \
        }
 
+
 static struct snd_kcontrol_new alc262_HP_BPC_mixer[] = {
        ALC262_HP_MASTER_SWITCH,
        HDA_CODEC_VOLUME("Front Playback Volume", 0x0c, 0x0, HDA_OUTPUT),
@@ -10483,6 +10598,12 @@ static int alc262_hippo_master_sw_put(struct snd_kcontrol *kcontrol,
                .info = snd_ctl_boolean_mono_info,              \
                .get = alc262_hippo_master_sw_get,              \
                .put = alc262_hippo_master_sw_put,              \
+       },                                                      \
+       {                                                       \
+               .iface = NID_MAPPING,                           \
+               .name = "Master Playback Switch",               \
+               .subdevice = SUBDEV_HP(0) | (SUBDEV_LINE(0) << 8) | \
+                            (SUBDEV_SPEAKER(0) << 16), \
        }
 
 static struct snd_kcontrol_new alc262_hippo_mixer[] = {
@@ -10963,11 +11084,17 @@ static struct snd_kcontrol_new alc262_fujitsu_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc262_fujitsu_master_sw_put,
                .private_value = HDA_COMPOSE_AMP_VAL(0x14, 3, 0, HDA_OUTPUT),
        },
+       {
+               .iface = NID_MAPPING,
+               .name = "Master Playback Switch",
+               .private_value = 0x1b,
+       },
        HDA_CODEC_VOLUME("CD Playback Volume", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_MUTE("CD Playback Switch", 0x0b, 0x04, HDA_INPUT),
        HDA_CODEC_VOLUME("Mic Boost", 0x18, 0, HDA_INPUT),
@@ -10998,6 +11125,7 @@ static struct snd_kcontrol_new alc262_lenovo_3000_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc262_lenovo_3000_master_sw_put,
@@ -11152,6 +11280,11 @@ static struct snd_kcontrol_new alc262_ultra_capture_mixer[] = {
                .get = alc_mux_enum_get,
                .put = alc262_ultra_mux_enum_put,
        },
+       {
+               .iface = NID_MAPPING,
+               .name = "Capture Source",
+               .private_value = 0x15,
+       },
        { } /* end */
 };
 
@@ -12170,6 +12303,7 @@ static struct snd_kcontrol_new alc268_acer_aspire_one_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -12185,6 +12319,7 @@ static struct snd_kcontrol_new alc268_acer_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -12202,6 +12337,7 @@ static struct snd_kcontrol_new alc268_acer_dmic_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -13155,6 +13291,7 @@ static struct snd_kcontrol_new alc269_quanta_fl1_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -13175,6 +13312,7 @@ static struct snd_kcontrol_new alc269_lifebook_mixer[] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Master Playback Switch",
+               .subdevice = HDA_SUBDEV_AMP_FLAG,
                .info = snd_hda_mixer_amp_switch_info,
                .get = snd_hda_mixer_amp_switch_get,
                .put = alc268_acer_master_sw_put,
@@ -14939,8 +15077,12 @@ static int patch_alc861(struct hda_codec *codec)
        spec->vmaster_nid = 0x03;
 
        codec->patch_ops = alc_patch_ops;
-       if (board_config == ALC861_AUTO)
+       if (board_config == ALC861_AUTO) {
                spec->init_hook = alc861_auto_init;
+#ifdef CONFIG_SND_HDA_POWER_SAVE
+               spec->power_hook = alc_power_eapd;
+#endif
+       }
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        if (!spec->loopback.amplist)
                spec->loopback.amplist = alc861_loopbacks;
index 43b436c..f419ee8 100644 (file)
@@ -122,6 +122,7 @@ static int si3054_switch_put(struct snd_kcontrol *kcontrol,
 #define SI3054_KCONTROL(kname,reg,mask) { \
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
        .name = kname, \
+       .subdevice = HDA_SUBDEV_NID_FLAG | reg, \
        .info = si3054_switch_info, \
        .get  = si3054_switch_get, \
        .put  = si3054_switch_put, \
index 799ba25..dbffb5b 100644 (file)
@@ -2688,7 +2688,7 @@ static struct snd_kcontrol_new *
 stac_control_new(struct sigmatel_spec *spec,
                 struct snd_kcontrol_new *ktemp,
                 const char *name,
-                hda_nid_t nid)
+                unsigned int subdev)
 {
        struct snd_kcontrol_new *knew;
 
@@ -2704,8 +2704,7 @@ stac_control_new(struct sigmatel_spec *spec,
                spec->kctls.alloced--;
                return NULL;
        }
-       if (nid)
-               knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
+       knew->subdevice = subdev;
        return knew;
 }
 
@@ -2715,7 +2714,7 @@ static int stac92xx_add_control_temp(struct sigmatel_spec *spec,
                                     unsigned long val)
 {
        struct snd_kcontrol_new *knew = stac_control_new(spec, ktemp, name,
-                                                        get_amp_nid_(val));
+                                                        HDA_SUBDEV_AMP_FLAG);
        if (!knew)
                return -ENOMEM;
        knew->index = idx;
@@ -4160,34 +4159,52 @@ static void stac92xx_power_down(struct hda_codec *codec)
 static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
                                  int enable);
 
+static inline int get_int_hint(struct hda_codec *codec, const char *key,
+                              int *valp)
+{
+       const char *p;
+       p = snd_hda_get_hint(codec, key);
+       if (p) {
+               unsigned long val;
+               if (!strict_strtoul(p, 0, &val)) {
+                       *valp = val;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
 /* override some hints from the hwdep entry */
 static void stac_store_hints(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       const char *p;
        int val;
 
        val = snd_hda_get_bool_hint(codec, "hp_detect");
        if (val >= 0)
                spec->hp_detect = val;
-       p = snd_hda_get_hint(codec, "gpio_mask");
-       if (p) {
-               spec->gpio_mask = simple_strtoul(p, NULL, 0);
+       if (get_int_hint(codec, "gpio_mask", &spec->gpio_mask)) {
                spec->eapd_mask = spec->gpio_dir = spec->gpio_data =
                        spec->gpio_mask;
        }
-       p = snd_hda_get_hint(codec, "gpio_dir");
-       if (p)
-               spec->gpio_dir = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
-       p = snd_hda_get_hint(codec, "gpio_data");
-       if (p)
-               spec->gpio_data = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
-       p = snd_hda_get_hint(codec, "eapd_mask");
-       if (p)
-               spec->eapd_mask = simple_strtoul(p, NULL, 0) & spec->gpio_mask;
+       if (get_int_hint(codec, "gpio_dir", &spec->gpio_dir))
+               spec->gpio_mask &= spec->gpio_mask;
+       if (get_int_hint(codec, "gpio_data", &spec->gpio_data))
+               spec->gpio_dir &= spec->gpio_mask;
+       if (get_int_hint(codec, "eapd_mask", &spec->eapd_mask))
+               spec->eapd_mask &= spec->gpio_mask;
+       if (get_int_hint(codec, "gpio_mute", &spec->gpio_mute))
+               spec->gpio_mute &= spec->gpio_mask;
        val = snd_hda_get_bool_hint(codec, "eapd_switch");
        if (val >= 0)
                spec->eapd_switch = val;
+       get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
+       if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+               spec->gpio_mask |= spec->gpio_led;
+               spec->gpio_dir |= spec->gpio_led;
+               if (spec->gpio_led_polarity)
+                       spec->gpio_data |= spec->gpio_led;
+       }
 }
 
 static int stac92xx_init(struct hda_codec *codec)
@@ -4372,18 +4389,8 @@ static void stac92xx_free_kctls(struct hda_codec *codec)
 static void stac92xx_shutup(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec = codec->spec;
-       int i;
-       hda_nid_t nid;
 
-       /* reset each pin before powering down DAC/ADC to avoid click noise */
-       nid = codec->start_nid;
-       for (i = 0; i < codec->num_nodes; i++, nid++) {
-               unsigned int wcaps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wcaps);
-               if (wid_type == AC_WID_PIN)
-                       snd_hda_codec_read(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
-       }
+       snd_hda_shutup_pins(codec);
 
        if (spec->eapd_mask)
                stac_gpio_set(codec, spec->gpio_mask,
@@ -5431,6 +5438,54 @@ static int stac92hd71bxx_connected_smuxes(struct hda_codec *codec,
                return 0;
 }
 
+/* HP dv7 bass switch - GPIO5 */
+#define stac_hp_bass_gpio_info snd_ctl_boolean_mono_info
+static int stac_hp_bass_gpio_get(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       ucontrol->value.integer.value[0] = !!(spec->gpio_data & 0x20);
+       return 0;
+}
+
+static int stac_hp_bass_gpio_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct sigmatel_spec *spec = codec->spec;
+       unsigned int gpio_data;
+
+       gpio_data = (spec->gpio_data & ~0x20) |
+               (ucontrol->value.integer.value[0] ? 0x20 : 0);
+       if (gpio_data == spec->gpio_data)
+               return 0;
+       spec->gpio_data = gpio_data;
+       stac_gpio_set(codec, spec->gpio_mask, spec->gpio_dir, spec->gpio_data);
+       return 1;
+}
+
+static struct snd_kcontrol_new stac_hp_bass_sw_ctrl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .info = stac_hp_bass_gpio_info,
+       .get = stac_hp_bass_gpio_get,
+       .put = stac_hp_bass_gpio_put,
+};
+
+static int stac_add_hp_bass_switch(struct hda_codec *codec)
+{
+       struct sigmatel_spec *spec = codec->spec;
+
+       if (!stac_control_new(spec, &stac_hp_bass_sw_ctrl,
+                             "Bass Speaker Playback Switch", 0))
+               return -ENOMEM;
+
+       spec->gpio_mask |= 0x20;
+       spec->gpio_dir |= 0x20;
+       spec->gpio_data |= 0x20;
+       return 0;
+}
+
 static int patch_stac92hd71bxx(struct hda_codec *codec)
 {
        struct sigmatel_spec *spec;
@@ -5674,6 +5729,15 @@ again:
                return err;
        }
 
+       /* enable bass on HP dv7 */
+       if (spec->board_config == STAC_HP_DV5) {
+               unsigned int cap;
+               cap = snd_hda_param_read(codec, 0x1, AC_PAR_GPIO_CAP);
+               cap &= AC_GPIO_IO_COUNT;
+               if (cap >= 6)
+                       stac_add_hp_bass_switch(codec);
+       }
+
        codec->proc_widget_hook = stac92hd7x_proc_hook;
 
        return 0;
index b70e26a..9ddc373 100644 (file)
@@ -54,6 +54,8 @@
 #include "hda_codec.h"
 #include "hda_local.h"
 
+#define NID_MAPPING            (-1)
+
 /* amp values */
 #define AMP_VAL_IDX_SHIFT      19
 #define AMP_VAL_IDX_MASK       (0x0f<<19)
@@ -157,6 +159,19 @@ struct via_spec {
 #endif
 };
 
+static struct via_spec * via_new_spec(struct hda_codec *codec)
+{
+       struct via_spec *spec;
+
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (spec == NULL)
+               return NULL;
+
+       codec->spec = spec;
+       spec->codec = codec;
+       return spec;
+}
+
 static enum VIA_HDA_CODEC get_codec_type(struct hda_codec *codec)
 {
        u32 vendor_id = codec->vendor_id;
@@ -443,11 +458,27 @@ static int via_add_control(struct via_spec *spec, int type, const char *name,
        if (!knew->name)
                return -ENOMEM;
        if (get_amp_nid_(val))
-               knew->subdevice = HDA_SUBDEV_NID_FLAG | get_amp_nid_(val);
+               knew->subdevice = HDA_SUBDEV_AMP_FLAG;
        knew->private_value = val;
        return 0;
 }
 
+static struct snd_kcontrol_new *via_clone_control(struct via_spec *spec,
+                                               struct snd_kcontrol_new *tmpl)
+{
+       struct snd_kcontrol_new *knew;
+
+       snd_array_init(&spec->kctls, sizeof(*knew), 32);
+       knew = snd_array_new(&spec->kctls);
+       if (!knew)
+               return NULL;
+       *knew = *tmpl;
+       knew->name = kstrdup(tmpl->name, GFP_KERNEL);
+       if (!knew->name)
+               return NULL;
+       return 0;
+}
+
 static void via_free_kctls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
@@ -1088,24 +1119,9 @@ static int via_independent_hp_get(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct via_spec *spec = codec->spec;
-       hda_nid_t nid;
+       hda_nid_t nid = kcontrol->private_value;
        unsigned int pinsel;
 
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               break;
-       case VT2002P:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
        /* use !! to translate conn sel 2 for VT1718S */
        pinsel = !!snd_hda_codec_read(codec, nid, 0,
                                      AC_VERB_GET_CONNECT_SEL,
@@ -1127,29 +1143,24 @@ static void activate_ctl(struct hda_codec *codec, const char *name, int active)
        }
 }
 
+static hda_nid_t side_mute_channel(struct via_spec *spec)
+{
+       switch (spec->codec_type) {
+       case VT1708:            return 0x1b;
+       case VT1709_10CH:       return 0x29;
+       case VT1708B_8CH:       /* fall thru */
+       case VT1708S:           return 0x27;
+       default:                return 0;
+       }
+}
+
 static int update_side_mute_status(struct hda_codec *codec)
 {
        /* mute side channel */
        struct via_spec *spec = codec->spec;
        unsigned int parm = spec->hp_independent_mode
                ? AMP_OUT_MUTE : AMP_OUT_UNMUTE;
-       hda_nid_t sw3;
-
-       switch (spec->codec_type) {
-       case VT1708:
-               sw3 = 0x1b;
-               break;
-       case VT1709_10CH:
-               sw3 = 0x29;
-               break;
-       case VT1708B_8CH:
-       case VT1708S:
-               sw3 = 0x27;
-               break;
-       default:
-               sw3 = 0;
-               break;
-       }
+       hda_nid_t sw3 = side_mute_channel(spec);
 
        if (sw3)
                snd_hda_codec_write(codec, sw3, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -1162,28 +1173,11 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct via_spec *spec = codec->spec;
-       hda_nid_t nid = spec->autocfg.hp_pins[0];
+       hda_nid_t nid = kcontrol->private_value;
        unsigned int pinsel = ucontrol->value.enumerated.item[0];
        /* Get Independent Mode index of headphone pin widget */
        spec->hp_independent_mode = spec->hp_independent_mode_index == pinsel
                ? 1 : 0;
-
-       switch (spec->codec_type) {
-       case VT1718S:
-               nid = 0x34;
-               pinsel = pinsel ? 2 : 0; /* indep HP use AOW4 (index 2) */
-               spec->multiout.num_dacs = 4;
-               break;
-       case VT2002P:
-               nid = 0x35;
-               break;
-       case VT1812:
-               nid = 0x3d;
-               break;
-       default:
-               nid = spec->autocfg.hp_pins[0];
-               break;
-       }
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CONNECT_SEL, pinsel);
 
        if (spec->multiout.hp_nid && spec->multiout.hp_nid
@@ -1207,18 +1201,55 @@ static int via_independent_hp_put(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static struct snd_kcontrol_new via_hp_mixer[] = {
+static struct snd_kcontrol_new via_hp_mixer[2] = {
        {
                .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
                .name = "Independent HP",
-               .count = 1,
                .info = via_independent_hp_info,
                .get = via_independent_hp_get,
                .put = via_independent_hp_put,
        },
-       { } /* end */
+       {
+               .iface = NID_MAPPING,
+               .name = "Independent HP",
+       },
 };
 
+static int via_hp_build(struct via_spec *spec)
+{
+       struct snd_kcontrol_new *knew;
+       hda_nid_t nid;
+
+       knew = via_clone_control(spec, &via_hp_mixer[0]);
+       if (knew == NULL)
+               return -ENOMEM;
+
+       switch (spec->codec_type) {
+       case VT1718S:
+               nid = 0x34;
+               break;
+       case VT2002P:
+               nid = 0x35;
+               break;
+       case VT1812:
+               nid = 0x3d;
+               break;
+       default:
+               nid = spec->autocfg.hp_pins[0];
+               break;
+       }
+
+       knew->subdevice = HDA_SUBDEV_NID_FLAG | nid;
+       knew->private_value = nid;
+
+       knew = via_clone_control(spec, &via_hp_mixer[1]);
+       if (knew == NULL)
+               return -ENOMEM;
+       knew->subdevice = side_mute_channel(spec);
+
+       return 0;
+}
+
 static void notify_aa_path_ctls(struct hda_codec *codec)
 {
        int i;
@@ -1376,7 +1407,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
        return 1;
 }
 
-static struct snd_kcontrol_new via_smart51_mixer[] = {
+static struct snd_kcontrol_new via_smart51_mixer[2] = {
        {
         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
         .name = "Smart 5.1",
@@ -1385,9 +1416,36 @@ static struct snd_kcontrol_new via_smart51_mixer[] = {
         .get = via_smart51_get,
         .put = via_smart51_put,
         },
-       {}                      /* end */
+       {
+        .iface = NID_MAPPING,
+        .name = "Smart 5.1",
+       }
 };
 
+static int via_smart51_build(struct via_spec *spec)
+{
+       struct snd_kcontrol_new *knew;
+       int index[] = { AUTO_PIN_MIC, AUTO_PIN_FRONT_MIC, AUTO_PIN_LINE };
+       hda_nid_t nid;
+       int i;
+
+       knew = via_clone_control(spec, &via_smart51_mixer[0]);
+       if (knew == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < ARRAY_SIZE(index); i++) {
+               nid = spec->autocfg.input_pins[index[i]];
+               if (nid) {
+                       knew = via_clone_control(spec, &via_smart51_mixer[1]);
+                       if (knew == NULL)
+                               return -ENOMEM;
+                       knew->subdevice = nid;
+               }
+       }
+
+       return 0;
+}
+
 /* capture mixer elements */
 static struct snd_kcontrol_new vt1708_capture_mixer[] = {
        HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_INPUT),
@@ -1819,8 +1877,9 @@ static struct hda_pcm_stream vt1708_pcm_digital_capture = {
 static int via_build_controls(struct hda_codec *codec)
 {
        struct via_spec *spec = codec->spec;
-       int err;
-       int i;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_new *knew;
+       int err, i;
 
        for (i = 0; i < spec->num_mixers; i++) {
                err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
@@ -1845,6 +1904,27 @@ static int via_build_controls(struct hda_codec *codec)
                        return err;
        }
 
+       /* assign Capture Source enums to NID */
+       kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
+       for (i = 0; kctl && i < kctl->count; i++) {
+               err = snd_hda_add_nid(codec, kctl, i, spec->mux_nids[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       /* other nid->control mapping */
+       for (i = 0; i < spec->num_mixers; i++) {
+               for (knew = spec->mixers[i]; knew->name; knew++) {
+                       if (knew->iface != NID_MAPPING)
+                               continue;
+                       kctl = snd_hda_find_mixer_ctl(codec, knew->name);
+                       if (kctl == NULL)
+                               continue;
+                       err = snd_hda_add_nid(codec, kctl, 0,
+                                             knew->subdevice);
+               }
+       }
+
        /* init power states */
        set_jack_power_state(codec);
        analog_low_current_mode(codec, 1);
@@ -2481,9 +2561,9 @@ static int vt1708_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -2554,12 +2634,10 @@ static int patch_vt1708(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708_parse_auto_config(codec);
        if (err < 0) {
@@ -2597,7 +2675,6 @@ static int patch_vt1708(struct hda_codec *codec)
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        spec->loopback.amplist = vt1708_loopbacks;
 #endif
-       spec->codec = codec;
        INIT_DELAYED_WORK(&spec->vt1708_hp_work, vt1708_update_hp_jack_state);
        return 0;
 }
@@ -3010,9 +3087,9 @@ static int vt1709_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -3032,12 +3109,10 @@ static int patch_vt1709_10ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        err = vt1709_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
@@ -3126,12 +3201,10 @@ static int patch_vt1709_6ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        err = vt1709_parse_auto_config(codec);
        if (err < 0) {
                via_free(codec);
@@ -3581,9 +3654,9 @@ static int vt1708B_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -3605,12 +3678,10 @@ static int patch_vt1708B_8ch(struct hda_codec *codec)
        if (get_codec_type(codec) == VT1708BCE)
                return patch_vt1708S(codec);
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708B_parse_auto_config(codec);
        if (err < 0) {
@@ -3657,12 +3728,10 @@ static int patch_vt1708B_4ch(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708B_parse_auto_config(codec);
        if (err < 0) {
@@ -4071,9 +4140,9 @@ static int vt1708S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
        return 1;
 }
 
@@ -4103,12 +4172,10 @@ static int patch_vt1708S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1708S_parse_auto_config(codec);
        if (err < 0) {
@@ -4443,7 +4510,7 @@ static int vt1702_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
        return 1;
 }
@@ -4464,12 +4531,10 @@ static int patch_vt1702(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1702_parse_auto_config(codec);
        if (err < 0) {
@@ -4865,9 +4930,9 @@ static int vt1718S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
 
        return 1;
 }
@@ -4888,12 +4953,10 @@ static int patch_vt1718S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1718S_parse_auto_config(codec);
        if (err < 0) {
@@ -5014,6 +5077,7 @@ static struct snd_kcontrol_new vt1716s_dmic_mixer[] = {
        {
         .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
         .name = "Digital Mic Capture Switch",
+        .subdevice = HDA_SUBDEV_NID_FLAG | 0x26,
         .count = 1,
         .info = vt1716s_dmic_info,
         .get = vt1716s_dmic_get,
@@ -5361,9 +5425,9 @@ static int vt1716S_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
-       spec->mixers[spec->num_mixers++] = via_smart51_mixer;
+       via_smart51_build(spec);
 
        return 1;
 }
@@ -5384,12 +5448,10 @@ static int patch_vt1716S(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1716S_parse_auto_config(codec);
        if (err < 0) {
@@ -5719,7 +5781,7 @@ static int vt2002P_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
        return 1;
 }
@@ -5741,12 +5803,10 @@ static int patch_vt2002P(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt2002P_parse_auto_config(codec);
        if (err < 0) {
@@ -6070,7 +6130,7 @@ static int vt1812_parse_auto_config(struct hda_codec *codec)
        spec->input_mux = &spec->private_imux[0];
 
        if (spec->hp_mux)
-               spec->mixers[spec->num_mixers++] = via_hp_mixer;
+               via_hp_build(spec);
 
        return 1;
 }
@@ -6092,12 +6152,10 @@ static int patch_vt1812(struct hda_codec *codec)
        int err;
 
        /* create a codec specific record */
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       spec = via_new_spec(codec);
        if (spec == NULL)
                return -ENOMEM;
 
-       codec->spec = spec;
-
        /* automatic parse from the BIOS config */
        err = vt1812_parse_auto_config(codec);
        if (err < 0) {
index c7cff6f..4fc6d8b 100644 (file)
@@ -106,7 +106,7 @@ module_param_array(dxr_enable, int, NULL, 0444);
 MODULE_PARM_DESC(dxr_enable, "Enable DXR support for Terratec DMX6FIRE.");
 
 
-static const struct pci_device_id snd_ice1712_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_ice1712_ids) = {
        { PCI_VDEVICE(ICE, PCI_DEVICE_ID_ICE_1712), 0 },   /* ICE1712 */
        { 0, }
 };
@@ -1180,6 +1180,10 @@ static int snd_ice1712_playback_pro_open(struct snd_pcm_substream *substream)
        snd_pcm_set_sync(substream);
        snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+       if (is_pro_rate_locked(ice)) {
+               runtime->hw.rate_min = PRO_RATE_DEFAULT;
+               runtime->hw.rate_max = PRO_RATE_DEFAULT;
+       }
 
        if (ice->spdif.ops.open)
                ice->spdif.ops.open(ice, substream);
@@ -1197,6 +1201,11 @@ static int snd_ice1712_capture_pro_open(struct snd_pcm_substream *substream)
        snd_pcm_set_sync(substream);
        snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
        snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates);
+       if (is_pro_rate_locked(ice)) {
+               runtime->hw.rate_min = PRO_RATE_DEFAULT;
+               runtime->hw.rate_max = PRO_RATE_DEFAULT;
+       }
+
        return 0;
 }
 
index ae29073..c1498fa 100644 (file)
@@ -94,7 +94,7 @@ MODULE_PARM_DESC(model, "Use the given board model.");
 
 
 /* Both VT1720 and VT1724 have the same PCI IDs */
-static const struct pci_device_id snd_vt1724_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_vt1724_ids) = {
        { PCI_VDEVICE(ICE, PCI_DEVICE_ID_VT1724), 0 },
        { 0, }
 };
index b990143..6433e65 100644 (file)
@@ -420,7 +420,7 @@ struct intel8x0 {
        u32 int_sta_mask;               /* interrupt status mask */
 };
 
-static struct pci_device_id snd_intel8x0_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0_ids) = {
        { PCI_VDEVICE(INTEL, 0x2415), DEVICE_INTEL },   /* 82801AA */
        { PCI_VDEVICE(INTEL, 0x2425), DEVICE_INTEL },   /* 82901AB */
        { PCI_VDEVICE(INTEL, 0x2445), DEVICE_INTEL },   /* 82801BA */
index 9e7d12e..13cec1e 100644 (file)
@@ -219,7 +219,7 @@ struct intel8x0m {
        unsigned int pcm_pos_shift;
 };
 
-static struct pci_device_id snd_intel8x0m_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_intel8x0m_ids) = {
        { PCI_VDEVICE(INTEL, 0x2416), DEVICE_INTEL },   /* 82801AA */
        { PCI_VDEVICE(INTEL, 0x2426), DEVICE_INTEL },   /* 82901AB */
        { PCI_VDEVICE(INTEL, 0x2446), DEVICE_INTEL },   /* 82801BA */
index 7cc38a1..6d79570 100644 (file)
@@ -418,7 +418,7 @@ module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "Enable Korg 1212 soundcard.");
 MODULE_AUTHOR("Haroldo Gamal <gamal@alternex.com.br>");
 
-static struct pci_device_id snd_korg1212_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_korg1212_ids) = {
        {
                .vendor    = 0x10b5,
                .device    = 0x906d,
index 11b8c65..0cca560 100644 (file)
@@ -55,7 +55,7 @@ static const char card_name[] = "LX6464ES";
 
 #define PCI_DEVICE_ID_PLX_LX6464ES             PCI_DEVICE_ID_PLX_9056
 
-static struct pci_device_id snd_lx6464es_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_lx6464es_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_LX6464ES),
          .subvendor = PCI_VENDOR_ID_DIGIGRAM,
          .subdevice = PCI_SUBDEVICE_ID_DIGIGRAM_LX6464ES_SERIAL_SUBSYSTEM
index 75283fb..b64e781 100644 (file)
@@ -861,7 +861,7 @@ struct snd_m3 {
 /*
  * pci ids
  */
-static struct pci_device_id snd_m3_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_m3_ids) = {
        {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO_1, PCI_ANY_ID, PCI_ANY_ID,
         PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
        {PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ALLEGRO, PCI_ANY_ID, PCI_ANY_ID,
index a83d196..7e8e7da 100644 (file)
@@ -60,7 +60,7 @@ MODULE_PARM_DESC(enable, "Enable Digigram " CARD_NAME " soundcard.");
 /*
  */
 
-static struct pci_device_id snd_mixart_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_mixart_ids) = {
        { PCI_VDEVICE(MOTOROLA, 0x0003), 0, }, /* MC8240 */
        { 0, }
 };
index 97a0731..5a60492 100644 (file)
@@ -262,7 +262,7 @@ struct nm256 {
 /*
  * PCI ids
  */
-static struct pci_device_id snd_nm256_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_nm256_ids) = {
        {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256AV_AUDIO), 0},
        {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256ZX_AUDIO), 0},
        {PCI_VDEVICE(NEOMAGIC, PCI_DEVICE_ID_NEOMAGIC_NM256XL_PLUS_AUDIO), 0},
index 389941c..acd8f15 100644 (file)
@@ -2,7 +2,7 @@ snd-oxygen-lib-objs := oxygen_io.o oxygen_lib.o oxygen_mixer.o oxygen_pcm.o
 snd-hifier-objs := hifier.o
 snd-oxygen-objs := oxygen.o
 snd-virtuoso-objs := virtuoso.o xonar_lib.o \
-       xonar_pcm179x.o xonar_cs43xx.o xonar_hdmi.o
+       xonar_pcm179x.o xonar_cs43xx.o xonar_wm87x6.o xonar_hdmi.o
 
 obj-$(CONFIG_SND_OXYGEN_LIB) += snd-oxygen-lib.o
 obj-$(CONFIG_SND_HIFIER) += snd-hifier.o
index e3c229b..5a87d68 100644 (file)
@@ -48,7 +48,7 @@ MODULE_PARM_DESC(id, "ID string");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "enable card");
 
-static struct pci_device_id hifier_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(hifier_ids) = {
        { OXYGEN_PCI_SUBID(0x14c3, 0x1710) },
        { OXYGEN_PCI_SUBID(0x14c3, 0x1711) },
        { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
index acbedeb..289cb4d 100644 (file)
@@ -72,7 +72,7 @@ enum {
        MODEL_CLARO_HALO,       /* HT-Omega Claro halo */
 };
 
-static struct pci_device_id oxygen_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
        { OXYGEN_PCI_SUBID(0x10b0, 0x0216), .driver_data = MODEL_CMEDIA_REF },
        { OXYGEN_PCI_SUBID(0x10b0, 0x0218), .driver_data = MODEL_CMEDIA_REF },
        { OXYGEN_PCI_SUBID(0x10b0, 0x0219), .driver_data = MODEL_CMEDIA_REF },
index 6accaf9..f03a2f2 100644 (file)
@@ -40,7 +40,7 @@ MODULE_PARM_DESC(id, "ID string");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "enable card");
 
-static struct pci_device_id xonar_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(xonar_ids) = {
        { OXYGEN_PCI_SUBID(0x1043, 0x8269) },
        { OXYGEN_PCI_SUBID(0x1043, 0x8275) },
        { OXYGEN_PCI_SUBID(0x1043, 0x82b7) },
@@ -49,6 +49,7 @@ static struct pci_device_id xonar_ids[] __devinitdata = {
        { OXYGEN_PCI_SUBID(0x1043, 0x834f) },
        { OXYGEN_PCI_SUBID(0x1043, 0x835c) },
        { OXYGEN_PCI_SUBID(0x1043, 0x835d) },
+       { OXYGEN_PCI_SUBID(0x1043, 0x838e) },
        { OXYGEN_PCI_SUBID_BROKEN_EEPROM },
        { }
 };
@@ -61,6 +62,8 @@ static int __devinit get_xonar_model(struct oxygen *chip,
                return 0;
        if (get_xonar_cs43xx_model(chip, id) >= 0)
                return 0;
+       if (get_xonar_wm87x6_model(chip, id) >= 0)
+               return 0;
        return -EINVAL;
 }
 
diff --git a/sound/pci/oxygen/wm8766.h b/sound/pci/oxygen/wm8766.h
new file mode 100644 (file)
index 0000000..e0e849a
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef WM8766_H_INCLUDED
+#define WM8766_H_INCLUDED
+
+#define WM8766_LDA1            0x00
+#define WM8766_RDA1            0x01
+#define WM8766_DAC_CTRL                0x02
+#define WM8766_INT_CTRL                0x03
+#define WM8766_LDA2            0x04
+#define WM8766_RDA2            0x05
+#define WM8766_LDA3            0x06
+#define WM8766_RDA3            0x07
+#define WM8766_MASTDA          0x08
+#define WM8766_DAC_CTRL2       0x09
+#define WM8766_DAC_CTRL3       0x0a
+#define WM8766_MUTE1           0x0c
+#define WM8766_MUTE2           0x0f
+#define WM8766_RESET           0x1f
+
+/* LDAx/RDAx/MASTDA */
+#define WM8766_ATT_MASK                0x0ff
+#define WM8766_UPDATE          0x100
+/* DAC_CTRL */
+#define WM8766_MUTEALL         0x001
+#define WM8766_DEEMPALL                0x002
+#define WM8766_PWDN            0x004
+#define WM8766_ATC             0x008
+#define WM8766_IZD             0x010
+#define WM8766_PL_LEFT_MASK    0x060
+#define WM8766_PL_LEFT_MUTE    0x000
+#define WM8766_PL_LEFT_LEFT    0x020
+#define WM8766_PL_LEFT_RIGHT   0x040
+#define WM8766_PL_LEFT_LRMIX   0x060
+#define WM8766_PL_RIGHT_MASK   0x180
+#define WM8766_PL_RIGHT_MUTE   0x000
+#define WM8766_PL_RIGHT_LEFT   0x080
+#define WM8766_PL_RIGHT_RIGHT  0x100
+#define WM8766_PL_RIGHT_LRMIX  0x180
+/* INT_CTRL */
+#define WM8766_FMT_MASK                0x003
+#define WM8766_FMT_RJUST       0x000
+#define WM8766_FMT_LJUST       0x001
+#define WM8766_FMT_I2S         0x002
+#define WM8766_FMT_DSP         0x003
+#define WM8766_LRP             0x004
+#define WM8766_BCP             0x008
+#define WM8766_IWL_MASK                0x030
+#define WM8766_IWL_16          0x000
+#define WM8766_IWL_20          0x010
+#define WM8766_IWL_24          0x020
+#define WM8766_IWL_32          0x030
+#define WM8766_PHASE_MASK      0x1c0
+/* DAC_CTRL2 */
+#define WM8766_ZCD             0x001
+#define WM8766_DZFM_MASK       0x006
+#define WM8766_DMUTE_MASK      0x038
+#define WM8766_DEEMP_MASK      0x1c0
+/* DAC_CTRL3 */
+#define WM8766_DACPD_MASK      0x00e
+#define WM8766_PWRDNALL                0x010
+#define WM8766_MS              0x020
+#define WM8766_RATE_MASK       0x1c0
+#define WM8766_RATE_128                0x000
+#define WM8766_RATE_192                0x040
+#define WM8766_RATE_256                0x080
+#define WM8766_RATE_384                0x0c0
+#define WM8766_RATE_512                0x100
+#define WM8766_RATE_768                0x140
+/* MUTE1 */
+#define WM8766_MPD1            0x040
+/* MUTE2 */
+#define WM8766_MPD2            0x020
+
+#endif
diff --git a/sound/pci/oxygen/wm8776.h b/sound/pci/oxygen/wm8776.h
new file mode 100644 (file)
index 0000000..1a96f56
--- /dev/null
@@ -0,0 +1,177 @@
+#ifndef WM8776_H_INCLUDED
+#define WM8776_H_INCLUDED
+
+/*
+ * the following register names are from:
+ * wm8776.h  --  WM8776 ASoC driver
+ *
+ * Copyright 2009 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#define WM8776_HPLVOL          0x00
+#define WM8776_HPRVOL          0x01
+#define WM8776_HPMASTER                0x02
+#define WM8776_DACLVOL         0x03
+#define WM8776_DACRVOL         0x04
+#define WM8776_DACMASTER       0x05
+#define WM8776_PHASESWAP       0x06
+#define WM8776_DACCTRL1                0x07
+#define WM8776_DACMUTE         0x08
+#define WM8776_DACCTRL2                0x09
+#define WM8776_DACIFCTRL       0x0a
+#define WM8776_ADCIFCTRL       0x0b
+#define WM8776_MSTRCTRL                0x0c
+#define WM8776_PWRDOWN         0x0d
+#define WM8776_ADCLVOL         0x0e
+#define WM8776_ADCRVOL         0x0f
+#define WM8776_ALCCTRL1                0x10
+#define WM8776_ALCCTRL2                0x11
+#define WM8776_ALCCTRL3                0x12
+#define WM8776_NOISEGATE       0x13
+#define WM8776_LIMITER         0x14
+#define WM8776_ADCMUX          0x15
+#define WM8776_OUTMUX          0x16
+#define WM8776_RESET           0x17
+
+
+/* HPLVOL/HPRVOL/HPMASTER */
+#define WM8776_HPATT_MASK      0x07f
+#define WM8776_HPZCEN          0x080
+#define WM8776_UPDATE          0x100
+
+/* DACLVOL/DACRVOL/DACMASTER */
+#define WM8776_DATT_MASK       0x0ff
+/*#define WM8776_UPDATE                0x100*/
+
+/* PHASESWAP */
+#define WM8776_PH_MASK         0x003
+
+/* DACCTRL1 */
+#define WM8776_DZCEN           0x001
+#define WM8776_ATC             0x002
+#define WM8776_IZD             0x004
+#define WM8776_TOD             0x008
+#define WM8776_PL_LEFT_MASK    0x030
+#define WM8776_PL_LEFT_MUTE    0x000
+#define WM8776_PL_LEFT_LEFT    0x010
+#define WM8776_PL_LEFT_RIGHT   0x020
+#define WM8776_PL_LEFT_LRMIX   0x030
+#define WM8776_PL_RIGHT_MASK   0x0c0
+#define WM8776_PL_RIGHT_MUTE   0x000
+#define WM8776_PL_RIGHT_LEFT   0x040
+#define WM8776_PL_RIGHT_RIGHT  0x080
+#define WM8776_PL_RIGHT_LRMIX  0x0c0
+
+/* DACMUTE */
+#define WM8776_DMUTE           0x001
+
+/* DACCTRL2 */
+#define WM8776_DEEMPH          0x001
+#define WM8776_DZFM_MASK       0x006
+#define WM8776_DZFM_NONE       0x000
+#define WM8776_DZFM_LR         0x002
+#define WM8776_DZFM_BOTH       0x004
+#define WM8776_DZFM_EITHER     0x006
+
+/* DACIFCTRL */
+#define WM8776_DACFMT_MASK     0x003
+#define WM8776_DACFMT_RJUST    0x000
+#define WM8776_DACFMT_LJUST    0x001
+#define WM8776_DACFMT_I2S      0x002
+#define WM8776_DACFMT_DSP      0x003
+#define WM8776_DACLRP          0x004
+#define WM8776_DACBCP          0x008
+#define WM8776_DACWL_MASK      0x030
+#define WM8776_DACWL_16                0x000
+#define WM8776_DACWL_20                0x010
+#define WM8776_DACWL_24                0x020
+#define WM8776_DACWL_32                0x030
+
+/* ADCIFCTRL */
+#define WM8776_ADCFMT_MASK     0x003
+#define WM8776_ADCFMT_RJUST    0x000
+#define WM8776_ADCFMT_LJUST    0x001
+#define WM8776_ADCFMT_I2S      0x002
+#define WM8776_ADCFMT_DSP      0x003
+#define WM8776_ADCLRP          0x004
+#define WM8776_ADCBCP          0x008
+#define WM8776_ADCWL_MASK      0x030
+#define WM8776_ADCWL_16                0x000
+#define WM8776_ADCWL_20                0x010
+#define WM8776_ADCWL_24                0x020
+#define WM8776_ADCWL_32                0x030
+#define WM8776_ADCMCLK         0x040
+#define WM8776_ADCHPD          0x100
+
+/* MSTRCTRL */
+#define WM8776_ADCRATE_MASK    0x007
+#define WM8776_ADCRATE_256     0x002
+#define WM8776_ADCRATE_384     0x003
+#define WM8776_ADCRATE_512     0x004
+#define WM8776_ADCRATE_768     0x005
+#define WM8776_ADCOSR          0x008
+#define WM8776_DACRATE_MASK    0x070
+#define WM8776_DACRATE_128     0x000
+#define WM8776_DACRATE_192     0x010
+#define WM8776_DACRATE_256     0x020
+#define WM8776_DACRATE_384     0x030
+#define WM8776_DACRATE_512     0x040
+#define WM8776_DACRATE_768     0x050
+#define WM8776_DACMS           0x080
+#define WM8776_ADCMS           0x100
+
+/* PWRDOWN */
+#define WM8776_PDWN            0x001
+#define WM8776_ADCPD           0x002
+#define WM8776_DACPD           0x004
+#define WM8776_HPPD            0x008
+#define WM8776_AINPD           0x040
+
+/* ADCLVOL/ADCRVOL */
+#define WM8776_AGMASK          0x0ff
+#define WM8776_ZCA             0x100
+
+/* ALCCTRL1 */
+#define WM8776_LCT_MASK                0x00f
+#define WM8776_MAXGAIN_MASK    0x070
+#define WM8776_LCSEL_MASK      0x180
+#define WM8776_LCSEL_LIMITER   0x000
+#define WM8776_LCSEL_ALC_RIGHT 0x080
+#define WM8776_LCSEL_ALC_LEFT  0x100
+#define WM8776_LCSEL_ALC_STEREO        0x180
+
+/* ALCCTRL2 */
+#define WM8776_HLD_MASK                0x00f
+#define WM8776_ALCZC           0x080
+#define WM8776_LCEN            0x100
+
+/* ALCCTRL3 */
+#define WM8776_ATK_MASK                0x00f
+#define WM8776_DCY_MASK                0x0f0
+
+/* NOISEGATE */
+#define WM8776_NGAT            0x001
+#define WM8776_NGTH_MASK       0x01c
+
+/* LIMITER */
+#define WM8776_MAXATTEN_MASK   0x00f
+#define WM8776_TRANWIN_MASK    0x070
+
+/* ADCMUX */
+#define WM8776_AMX_MASK                0x01f
+#define WM8776_MUTERA          0x040
+#define WM8776_MUTELA          0x080
+#define WM8776_LRBOTH          0x100
+
+/* OUTMUX */
+#define WM8776_MX_DAC          0x001
+#define WM8776_MX_AUX          0x002
+#define WM8776_MX_BYPASS       0x004
+
+#endif
index 89b3ed8..b35343b 100644 (file)
@@ -35,6 +35,8 @@ int get_xonar_pcm179x_model(struct oxygen *chip,
                            const struct pci_device_id *id);
 int get_xonar_cs43xx_model(struct oxygen *chip,
                           const struct pci_device_id *id);
+int get_xonar_wm87x6_model(struct oxygen *chip,
+                          const struct pci_device_id *id);
 
 /* HDMI helper functions */
 
diff --git a/sound/pci/oxygen/xonar_wm87x6.c b/sound/pci/oxygen/xonar_wm87x6.c
new file mode 100644 (file)
index 0000000..7754db1
--- /dev/null
@@ -0,0 +1,1021 @@
+/*
+ * card driver for models with WM8776/WM8766 DACs (Xonar DS)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ *
+ *  This driver is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License, version 2.
+ *
+ *  This driver 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 driver; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Xonar DS
+ * --------
+ *
+ * CMI8788:
+ *
+ * SPI 0 -> WM8766 (surround, center/LFE, back)
+ * SPI 1 -> WM8776 (front, input)
+ *
+ * GPIO 4 <- headphone detect
+ * GPIO 6 -> route input jack to input 1/2 (1/0)
+ * GPIO 7 -> enable output to speakers
+ * GPIO 8 -> enable output to speakers
+ */
+
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "xonar.h"
+#include "wm8776.h"
+#include "wm8766.h"
+
+#define GPIO_DS_HP_DETECT      0x0010
+#define GPIO_DS_INPUT_ROUTE    0x0040
+#define GPIO_DS_OUTPUT_ENABLE  0x0180
+
+#define LC_CONTROL_LIMITER     0x40000000
+#define LC_CONTROL_ALC         0x20000000
+
+struct xonar_wm87x6 {
+       struct xonar_generic generic;
+       u16 wm8776_regs[0x17];
+       u16 wm8766_regs[0x10];
+       struct snd_kcontrol *lc_controls[13];
+};
+
+static void wm8776_write(struct oxygen *chip,
+                        unsigned int reg, unsigned int value)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+                        OXYGEN_SPI_DATA_LENGTH_2 |
+                        OXYGEN_SPI_CLOCK_160 |
+                        (1 << OXYGEN_SPI_CODEC_SHIFT) |
+                        OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
+                        (reg << 9) | value);
+       if (reg < ARRAY_SIZE(data->wm8776_regs)) {
+               if (reg >= WM8776_HPLVOL || reg <= WM8776_DACMASTER)
+                       value &= ~WM8776_UPDATE;
+               data->wm8776_regs[reg] = value;
+       }
+}
+
+static void wm8776_write_cached(struct oxygen *chip,
+                               unsigned int reg, unsigned int value)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       if (reg >= ARRAY_SIZE(data->wm8776_regs) ||
+           value != data->wm8776_regs[reg])
+               wm8776_write(chip, reg, value);
+}
+
+static void wm8766_write(struct oxygen *chip,
+                        unsigned int reg, unsigned int value)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       oxygen_write_spi(chip, OXYGEN_SPI_TRIGGER |
+                        OXYGEN_SPI_DATA_LENGTH_2 |
+                        OXYGEN_SPI_CLOCK_160 |
+                        (0 << OXYGEN_SPI_CODEC_SHIFT) |
+                        OXYGEN_SPI_CEN_LATCH_CLOCK_LO,
+                        (reg << 9) | value);
+       if (reg < ARRAY_SIZE(data->wm8766_regs))
+               data->wm8766_regs[reg] = value;
+}
+
+static void wm8766_write_cached(struct oxygen *chip,
+                               unsigned int reg, unsigned int value)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       if (reg >= ARRAY_SIZE(data->wm8766_regs) ||
+           value != data->wm8766_regs[reg]) {
+               if ((reg >= WM8766_LDA1 && reg <= WM8766_RDA1) ||
+                   (reg >= WM8766_LDA2 && reg <= WM8766_MASTDA))
+                       value &= ~WM8766_UPDATE;
+               wm8766_write(chip, reg, value);
+       }
+}
+
+static void wm8776_registers_init(struct oxygen *chip)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       wm8776_write(chip, WM8776_RESET, 0);
+       wm8776_write(chip, WM8776_DACCTRL1, WM8776_DZCEN |
+                    WM8776_PL_LEFT_LEFT | WM8776_PL_RIGHT_RIGHT);
+       wm8776_write(chip, WM8776_DACMUTE, chip->dac_mute ? WM8776_DMUTE : 0);
+       wm8776_write(chip, WM8776_DACIFCTRL,
+                    WM8776_DACFMT_LJUST | WM8776_DACWL_24);
+       wm8776_write(chip, WM8776_ADCIFCTRL,
+                    data->wm8776_regs[WM8776_ADCIFCTRL]);
+       wm8776_write(chip, WM8776_MSTRCTRL, data->wm8776_regs[WM8776_MSTRCTRL]);
+       wm8776_write(chip, WM8776_PWRDOWN, data->wm8776_regs[WM8776_PWRDOWN]);
+       wm8776_write(chip, WM8776_HPLVOL, data->wm8776_regs[WM8776_HPLVOL]);
+       wm8776_write(chip, WM8776_HPRVOL, data->wm8776_regs[WM8776_HPRVOL] |
+                    WM8776_UPDATE);
+       wm8776_write(chip, WM8776_ADCLVOL, data->wm8776_regs[WM8776_ADCLVOL]);
+       wm8776_write(chip, WM8776_ADCRVOL, data->wm8776_regs[WM8776_ADCRVOL]);
+       wm8776_write(chip, WM8776_ADCMUX, data->wm8776_regs[WM8776_ADCMUX]);
+       wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0]);
+       wm8776_write(chip, WM8776_DACRVOL, chip->dac_volume[1] | WM8776_UPDATE);
+}
+
+static void wm8766_registers_init(struct oxygen *chip)
+{
+       wm8766_write(chip, WM8766_RESET, 0);
+       wm8766_write(chip, WM8766_INT_CTRL, WM8766_FMT_LJUST | WM8766_IWL_24);
+       wm8766_write(chip, WM8766_DAC_CTRL2,
+                    WM8766_ZCD | (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
+       wm8766_write(chip, WM8766_LDA1, chip->dac_volume[2]);
+       wm8766_write(chip, WM8766_RDA1, chip->dac_volume[3]);
+       wm8766_write(chip, WM8766_LDA2, chip->dac_volume[4]);
+       wm8766_write(chip, WM8766_RDA2, chip->dac_volume[5]);
+       wm8766_write(chip, WM8766_LDA3, chip->dac_volume[6]);
+       wm8766_write(chip, WM8766_RDA3, chip->dac_volume[7] | WM8766_UPDATE);
+}
+
+static void wm8776_init(struct oxygen *chip)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       data->wm8776_regs[WM8776_HPLVOL] = (0x79 - 60) | WM8776_HPZCEN;
+       data->wm8776_regs[WM8776_HPRVOL] = (0x79 - 60) | WM8776_HPZCEN;
+       data->wm8776_regs[WM8776_ADCIFCTRL] =
+               WM8776_ADCFMT_LJUST | WM8776_ADCWL_24 | WM8776_ADCMCLK;
+       data->wm8776_regs[WM8776_MSTRCTRL] =
+               WM8776_ADCRATE_256 | WM8776_DACRATE_256;
+       data->wm8776_regs[WM8776_PWRDOWN] = WM8776_HPPD;
+       data->wm8776_regs[WM8776_ADCLVOL] = 0xa5 | WM8776_ZCA;
+       data->wm8776_regs[WM8776_ADCRVOL] = 0xa5 | WM8776_ZCA;
+       data->wm8776_regs[WM8776_ADCMUX] = 0x001;
+       wm8776_registers_init(chip);
+}
+
+static void xonar_ds_init(struct oxygen *chip)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       data->generic.anti_pop_delay = 300;
+       data->generic.output_enable_bit = GPIO_DS_OUTPUT_ENABLE;
+
+       wm8776_init(chip);
+       wm8766_registers_init(chip);
+
+       oxygen_write16_masked(chip, OXYGEN_GPIO_CONTROL, GPIO_DS_INPUT_ROUTE,
+                             GPIO_DS_HP_DETECT | GPIO_DS_INPUT_ROUTE);
+       oxygen_set_bits16(chip, OXYGEN_GPIO_DATA, GPIO_DS_INPUT_ROUTE);
+       oxygen_set_bits16(chip, OXYGEN_GPIO_INTERRUPT_MASK, GPIO_DS_HP_DETECT);
+       chip->interrupt_mask |= OXYGEN_INT_GPIO;
+
+       xonar_enable_output(chip);
+
+       snd_component_add(chip->card, "WM8776");
+       snd_component_add(chip->card, "WM8766");
+}
+
+static void xonar_ds_cleanup(struct oxygen *chip)
+{
+       xonar_disable_output(chip);
+}
+
+static void xonar_ds_suspend(struct oxygen *chip)
+{
+       xonar_ds_cleanup(chip);
+}
+
+static void xonar_ds_resume(struct oxygen *chip)
+{
+       wm8776_registers_init(chip);
+       wm8766_registers_init(chip);
+       xonar_enable_output(chip);
+}
+
+static void wm8776_adc_hardware_filter(unsigned int channel,
+                                      struct snd_pcm_hardware *hardware)
+{
+       if (channel == PCM_A) {
+               hardware->rates = SNDRV_PCM_RATE_32000 |
+                                 SNDRV_PCM_RATE_44100 |
+                                 SNDRV_PCM_RATE_48000 |
+                                 SNDRV_PCM_RATE_64000 |
+                                 SNDRV_PCM_RATE_88200 |
+                                 SNDRV_PCM_RATE_96000;
+               hardware->rate_max = 96000;
+       }
+}
+
+static void set_wm87x6_dac_params(struct oxygen *chip,
+                                 struct snd_pcm_hw_params *params)
+{
+}
+
+static void set_wm8776_adc_params(struct oxygen *chip,
+                                 struct snd_pcm_hw_params *params)
+{
+       u16 reg;
+
+       reg = WM8776_ADCRATE_256 | WM8776_DACRATE_256;
+       if (params_rate(params) > 48000)
+               reg |= WM8776_ADCOSR;
+       wm8776_write_cached(chip, WM8776_MSTRCTRL, reg);
+}
+
+static void update_wm8776_volume(struct oxygen *chip)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+       u8 to_change;
+
+       if (chip->dac_volume[0] == chip->dac_volume[1]) {
+               if (chip->dac_volume[0] != data->wm8776_regs[WM8776_DACLVOL] ||
+                   chip->dac_volume[1] != data->wm8776_regs[WM8776_DACRVOL]) {
+                       wm8776_write(chip, WM8776_DACMASTER,
+                                    chip->dac_volume[0] | WM8776_UPDATE);
+                       data->wm8776_regs[WM8776_DACLVOL] = chip->dac_volume[0];
+                       data->wm8776_regs[WM8776_DACRVOL] = chip->dac_volume[0];
+               }
+       } else {
+               to_change = (chip->dac_volume[0] !=
+                            data->wm8776_regs[WM8776_DACLVOL]) << 0;
+               to_change |= (chip->dac_volume[1] !=
+                             data->wm8776_regs[WM8776_DACLVOL]) << 1;
+               if (to_change & 1)
+                       wm8776_write(chip, WM8776_DACLVOL, chip->dac_volume[0] |
+                                    ((to_change & 2) ? 0 : WM8776_UPDATE));
+               if (to_change & 2)
+                       wm8776_write(chip, WM8776_DACRVOL,
+                                    chip->dac_volume[1] | WM8776_UPDATE);
+       }
+}
+
+static void update_wm87x6_volume(struct oxygen *chip)
+{
+       static const u8 wm8766_regs[6] = {
+               WM8766_LDA1, WM8766_RDA1,
+               WM8766_LDA2, WM8766_RDA2,
+               WM8766_LDA3, WM8766_RDA3,
+       };
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int i;
+       u8 to_change;
+
+       update_wm8776_volume(chip);
+       if (chip->dac_volume[2] == chip->dac_volume[3] &&
+           chip->dac_volume[2] == chip->dac_volume[4] &&
+           chip->dac_volume[2] == chip->dac_volume[5] &&
+           chip->dac_volume[2] == chip->dac_volume[6] &&
+           chip->dac_volume[2] == chip->dac_volume[7]) {
+               to_change = 0;
+               for (i = 0; i < 6; ++i)
+                       if (chip->dac_volume[2] !=
+                           data->wm8766_regs[wm8766_regs[i]])
+                               to_change = 1;
+               if (to_change) {
+                       wm8766_write(chip, WM8766_MASTDA,
+                                    chip->dac_volume[2] | WM8766_UPDATE);
+                       for (i = 0; i < 6; ++i)
+                               data->wm8766_regs[wm8766_regs[i]] =
+                                       chip->dac_volume[2];
+               }
+       } else {
+               to_change = 0;
+               for (i = 0; i < 6; ++i)
+                       to_change |= (chip->dac_volume[2 + i] !=
+                                     data->wm8766_regs[wm8766_regs[i]]) << i;
+               for (i = 0; i < 6; ++i)
+                       if (to_change & (1 << i))
+                               wm8766_write(chip, wm8766_regs[i],
+                                            chip->dac_volume[2 + i] |
+                                            ((to_change & (0x3e << i))
+                                             ? 0 : WM8766_UPDATE));
+       }
+}
+
+static void update_wm8776_mute(struct oxygen *chip)
+{
+       wm8776_write_cached(chip, WM8776_DACMUTE,
+                           chip->dac_mute ? WM8776_DMUTE : 0);
+}
+
+static void update_wm87x6_mute(struct oxygen *chip)
+{
+       update_wm8776_mute(chip);
+       wm8766_write_cached(chip, WM8766_DAC_CTRL2, WM8766_ZCD |
+                           (chip->dac_mute ? WM8766_DMUTE_MASK : 0));
+}
+
+static void xonar_ds_gpio_changed(struct oxygen *chip)
+{
+       u16 bits;
+
+       bits = oxygen_read16(chip, OXYGEN_GPIO_DATA);
+       snd_printk(KERN_INFO "HP detect: %d\n", !!(bits & GPIO_DS_HP_DETECT));
+}
+
+static int wm8776_bit_switch_get(struct snd_kcontrol *ctl,
+                                struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       u16 bit = ctl->private_value & 0xffff;
+       unsigned int reg_index = (ctl->private_value >> 16) & 0xff;
+       bool invert = (ctl->private_value >> 24) & 1;
+
+       value->value.integer.value[0] =
+               ((data->wm8776_regs[reg_index] & bit) != 0) ^ invert;
+       return 0;
+}
+
+static int wm8776_bit_switch_put(struct snd_kcontrol *ctl,
+                                struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       u16 bit = ctl->private_value & 0xffff;
+       u16 reg_value;
+       unsigned int reg_index = (ctl->private_value >> 16) & 0xff;
+       bool invert = (ctl->private_value >> 24) & 1;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       reg_value = data->wm8776_regs[reg_index] & ~bit;
+       if (value->value.integer.value[0] ^ invert)
+               reg_value |= bit;
+       changed = reg_value != data->wm8776_regs[reg_index];
+       if (changed)
+               wm8776_write(chip, reg_index, reg_value);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int wm8776_field_enum_info(struct snd_kcontrol *ctl,
+                                 struct snd_ctl_elem_info *info)
+{
+       static const char *const hld[16] = {
+               "0 ms", "2.67 ms", "5.33 ms", "10.6 ms",
+               "21.3 ms", "42.7 ms", "85.3 ms", "171 ms",
+               "341 ms", "683 ms", "1.37 s", "2.73 s",
+               "5.46 s", "10.9 s", "21.8 s", "43.7 s",
+       };
+       static const char *const atk_lim[11] = {
+               "0.25 ms", "0.5 ms", "1 ms", "2 ms",
+               "4 ms", "8 ms", "16 ms", "32 ms",
+               "64 ms", "128 ms", "256 ms",
+       };
+       static const char *const atk_alc[11] = {
+               "8.40 ms", "16.8 ms", "33.6 ms", "67.2 ms",
+               "134 ms", "269 ms", "538 ms", "1.08 s",
+               "2.15 s", "4.3 s", "8.6 s",
+       };
+       static const char *const dcy_lim[11] = {
+               "1.2 ms", "2.4 ms", "4.8 ms", "9.6 ms",
+               "19.2 ms", "38.4 ms", "76.8 ms", "154 ms",
+               "307 ms", "614 ms", "1.23 s",
+       };
+       static const char *const dcy_alc[11] = {
+               "33.5 ms", "67.0 ms", "134 ms", "268 ms",
+               "536 ms", "1.07 s", "2.14 s", "4.29 s",
+               "8.58 s", "17.2 s", "34.3 s",
+       };
+       static const char *const tranwin[8] = {
+               "0 us", "62.5 us", "125 us", "250 us",
+               "500 us", "1 ms", "2 ms", "4 ms",
+       };
+       u8 max;
+       const char *const *names;
+
+       max = (ctl->private_value >> 12) & 0xf;
+       info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       info->count = 1;
+       info->value.enumerated.items = max + 1;
+       if (info->value.enumerated.item > max)
+               info->value.enumerated.item = max;
+       switch ((ctl->private_value >> 24) & 0x1f) {
+       case WM8776_ALCCTRL2:
+               names = hld;
+               break;
+       case WM8776_ALCCTRL3:
+               if (((ctl->private_value >> 20) & 0xf) == 0) {
+                       if (ctl->private_value & LC_CONTROL_LIMITER)
+                               names = atk_lim;
+                       else
+                               names = atk_alc;
+               } else {
+                       if (ctl->private_value & LC_CONTROL_LIMITER)
+                               names = dcy_lim;
+                       else
+                               names = dcy_alc;
+               }
+               break;
+       case WM8776_LIMITER:
+               names = tranwin;
+               break;
+       default:
+               return -ENXIO;
+       }
+       strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+       return 0;
+}
+
+static int wm8776_field_volume_info(struct snd_kcontrol *ctl,
+                                   struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = 1;
+       info->value.integer.min = (ctl->private_value >> 8) & 0xf;
+       info->value.integer.max = (ctl->private_value >> 12) & 0xf;
+       return 0;
+}
+
+static void wm8776_field_set_from_ctl(struct snd_kcontrol *ctl)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int value, reg_index, mode;
+       u8 min, max, shift;
+       u16 mask, reg_value;
+       bool invert;
+
+       if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) ==
+           WM8776_LCSEL_LIMITER)
+               mode = LC_CONTROL_LIMITER;
+       else
+               mode = LC_CONTROL_ALC;
+       if (!(ctl->private_value & mode))
+               return;
+
+       value = ctl->private_value & 0xf;
+       min = (ctl->private_value >> 8) & 0xf;
+       max = (ctl->private_value >> 12) & 0xf;
+       mask = (ctl->private_value >> 16) & 0xf;
+       shift = (ctl->private_value >> 20) & 0xf;
+       reg_index = (ctl->private_value >> 24) & 0x1f;
+       invert = (ctl->private_value >> 29) & 0x1;
+
+       if (invert)
+               value = max - (value - min);
+       reg_value = data->wm8776_regs[reg_index];
+       reg_value &= ~(mask << shift);
+       reg_value |= value << shift;
+       wm8776_write_cached(chip, reg_index, reg_value);
+}
+
+static int wm8776_field_set(struct snd_kcontrol *ctl, unsigned int value)
+{
+       struct oxygen *chip = ctl->private_data;
+       u8 min, max;
+       int changed;
+
+       min = (ctl->private_value >> 8) & 0xf;
+       max = (ctl->private_value >> 12) & 0xf;
+       if (value < min || value > max)
+               return -EINVAL;
+       mutex_lock(&chip->mutex);
+       changed = value != (ctl->private_value & 0xf);
+       if (changed) {
+               ctl->private_value = (ctl->private_value & ~0xf) | value;
+               wm8776_field_set_from_ctl(ctl);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int wm8776_field_enum_get(struct snd_kcontrol *ctl,
+                                struct snd_ctl_elem_value *value)
+{
+       value->value.enumerated.item[0] = ctl->private_value & 0xf;
+       return 0;
+}
+
+static int wm8776_field_volume_get(struct snd_kcontrol *ctl,
+                                  struct snd_ctl_elem_value *value)
+{
+       value->value.integer.value[0] = ctl->private_value & 0xf;
+       return 0;
+}
+
+static int wm8776_field_enum_put(struct snd_kcontrol *ctl,
+                                struct snd_ctl_elem_value *value)
+{
+       return wm8776_field_set(ctl, value->value.enumerated.item[0]);
+}
+
+static int wm8776_field_volume_put(struct snd_kcontrol *ctl,
+                                  struct snd_ctl_elem_value *value)
+{
+       return wm8776_field_set(ctl, value->value.integer.value[0]);
+}
+
+static int wm8776_hp_vol_info(struct snd_kcontrol *ctl,
+                             struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = 2;
+       info->value.integer.min = 0x79 - 60;
+       info->value.integer.max = 0x7f;
+       return 0;
+}
+
+static int wm8776_hp_vol_get(struct snd_kcontrol *ctl,
+                            struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       mutex_lock(&chip->mutex);
+       value->value.integer.value[0] =
+               data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK;
+       value->value.integer.value[1] =
+               data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK;
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int wm8776_hp_vol_put(struct snd_kcontrol *ctl,
+                            struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       u8 to_update;
+
+       mutex_lock(&chip->mutex);
+       to_update = (value->value.integer.value[0] !=
+                    (data->wm8776_regs[WM8776_HPLVOL] & WM8776_HPATT_MASK))
+               << 0;
+       to_update |= (value->value.integer.value[1] !=
+                     (data->wm8776_regs[WM8776_HPRVOL] & WM8776_HPATT_MASK))
+               << 1;
+       if (value->value.integer.value[0] == value->value.integer.value[1]) {
+               if (to_update) {
+                       wm8776_write(chip, WM8776_HPMASTER,
+                                    value->value.integer.value[0] |
+                                    WM8776_HPZCEN | WM8776_UPDATE);
+                       data->wm8776_regs[WM8776_HPLVOL] =
+                               value->value.integer.value[0] | WM8776_HPZCEN;
+                       data->wm8776_regs[WM8776_HPRVOL] =
+                               value->value.integer.value[0] | WM8776_HPZCEN;
+               }
+       } else {
+               if (to_update & 1)
+                       wm8776_write(chip, WM8776_HPLVOL,
+                                    value->value.integer.value[0] |
+                                    WM8776_HPZCEN |
+                                    ((to_update & 2) ? 0 : WM8776_UPDATE));
+               if (to_update & 2)
+                       wm8776_write(chip, WM8776_HPRVOL,
+                                    value->value.integer.value[1] |
+                                    WM8776_HPZCEN | WM8776_UPDATE);
+       }
+       mutex_unlock(&chip->mutex);
+       return to_update != 0;
+}
+
+static int wm8776_input_mux_get(struct snd_kcontrol *ctl,
+                               struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int mux_bit = ctl->private_value;
+
+       value->value.integer.value[0] =
+               !!(data->wm8776_regs[WM8776_ADCMUX] & mux_bit);
+       return 0;
+}
+
+static int wm8776_input_mux_put(struct snd_kcontrol *ctl,
+                               struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int mux_bit = ctl->private_value;
+       u16 reg;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       reg = data->wm8776_regs[WM8776_ADCMUX];
+       if (value->value.integer.value[0]) {
+               reg &= ~0x003;
+               reg |= mux_bit;
+       } else
+               reg &= ~mux_bit;
+       changed = reg != data->wm8776_regs[WM8776_ADCMUX];
+       if (changed) {
+               oxygen_write16_masked(chip, OXYGEN_GPIO_DATA,
+                                     reg & 1 ? GPIO_DS_INPUT_ROUTE : 0,
+                                     GPIO_DS_INPUT_ROUTE);
+               wm8776_write(chip, WM8776_ADCMUX, reg);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int wm8776_input_vol_info(struct snd_kcontrol *ctl,
+                                struct snd_ctl_elem_info *info)
+{
+       info->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       info->count = 2;
+       info->value.integer.min = 0xa5;
+       info->value.integer.max = 0xff;
+       return 0;
+}
+
+static int wm8776_input_vol_get(struct snd_kcontrol *ctl,
+                               struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       mutex_lock(&chip->mutex);
+       value->value.integer.value[0] =
+               data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK;
+       value->value.integer.value[1] =
+               data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK;
+       mutex_unlock(&chip->mutex);
+       return 0;
+}
+
+static int wm8776_input_vol_put(struct snd_kcontrol *ctl,
+                               struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       int changed = 0;
+
+       mutex_lock(&chip->mutex);
+       changed = (value->value.integer.value[0] !=
+                  (data->wm8776_regs[WM8776_ADCLVOL] & WM8776_AGMASK)) ||
+                 (value->value.integer.value[1] !=
+                  (data->wm8776_regs[WM8776_ADCRVOL] & WM8776_AGMASK));
+       wm8776_write_cached(chip, WM8776_ADCLVOL,
+                           value->value.integer.value[0] | WM8776_ZCA);
+       wm8776_write_cached(chip, WM8776_ADCRVOL,
+                           value->value.integer.value[1] | WM8776_ZCA);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int wm8776_level_control_info(struct snd_kcontrol *ctl,
+                                    struct snd_ctl_elem_info *info)
+{
+       static const char *const names[3] = {
+               "None", "Peak Limiter", "Automatic Level Control"
+       };
+       info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       info->count = 1;
+       info->value.enumerated.items = 3;
+       if (info->value.enumerated.item >= 3)
+               info->value.enumerated.item = 2;
+       strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+       return 0;
+}
+
+static int wm8776_level_control_get(struct snd_kcontrol *ctl,
+                                   struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       if (!(data->wm8776_regs[WM8776_ALCCTRL2] & WM8776_LCEN))
+               value->value.enumerated.item[0] = 0;
+       else if ((data->wm8776_regs[WM8776_ALCCTRL1] & WM8776_LCSEL_MASK) ==
+                WM8776_LCSEL_LIMITER)
+               value->value.enumerated.item[0] = 1;
+       else
+               value->value.enumerated.item[0] = 2;
+       return 0;
+}
+
+static void activate_control(struct oxygen *chip,
+                            struct snd_kcontrol *ctl, unsigned int mode)
+{
+       unsigned int access;
+
+       if (ctl->private_value & mode)
+               access = 0;
+       else
+               access = SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+       if ((ctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE) != access) {
+               ctl->vd[0].access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+               snd_ctl_notify(chip->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id);
+       }
+}
+
+static int wm8776_level_control_put(struct snd_kcontrol *ctl,
+                                   struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int mode = 0, i;
+       u16 ctrl1, ctrl2;
+       int changed;
+
+       if (value->value.enumerated.item[0] >= 3)
+               return -EINVAL;
+       mutex_lock(&chip->mutex);
+       changed = value->value.enumerated.item[0] != ctl->private_value;
+       if (changed) {
+               ctl->private_value = value->value.enumerated.item[0];
+               ctrl1 = data->wm8776_regs[WM8776_ALCCTRL1];
+               ctrl2 = data->wm8776_regs[WM8776_ALCCTRL2];
+               switch (value->value.enumerated.item[0]) {
+               default:
+                       wm8776_write_cached(chip, WM8776_ALCCTRL2,
+                                           ctrl2 & ~WM8776_LCEN);
+                       break;
+               case 1:
+                       wm8776_write_cached(chip, WM8776_ALCCTRL1,
+                                           (ctrl1 & ~WM8776_LCSEL_MASK) |
+                                           WM8776_LCSEL_LIMITER);
+                       wm8776_write_cached(chip, WM8776_ALCCTRL2,
+                                           ctrl2 | WM8776_LCEN);
+                       mode = LC_CONTROL_LIMITER;
+                       break;
+               case 2:
+                       wm8776_write_cached(chip, WM8776_ALCCTRL1,
+                                           (ctrl1 & ~WM8776_LCSEL_MASK) |
+                                           WM8776_LCSEL_ALC_STEREO);
+                       wm8776_write_cached(chip, WM8776_ALCCTRL2,
+                                           ctrl2 | WM8776_LCEN);
+                       mode = LC_CONTROL_ALC;
+                       break;
+               }
+               for (i = 0; i < ARRAY_SIZE(data->lc_controls); ++i)
+                       activate_control(chip, data->lc_controls[i], mode);
+       }
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+static int hpf_info(struct snd_kcontrol *ctl, struct snd_ctl_elem_info *info)
+{
+       static const char *const names[2] = {
+               "None", "High-pass Filter"
+       };
+
+       info->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       info->count = 1;
+       info->value.enumerated.items = 2;
+       if (info->value.enumerated.item >= 2)
+               info->value.enumerated.item = 1;
+       strcpy(info->value.enumerated.name, names[info->value.enumerated.item]);
+       return 0;
+}
+
+static int hpf_get(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+
+       value->value.enumerated.item[0] =
+               !(data->wm8776_regs[WM8776_ADCIFCTRL] & WM8776_ADCHPD);
+       return 0;
+}
+
+static int hpf_put(struct snd_kcontrol *ctl, struct snd_ctl_elem_value *value)
+{
+       struct oxygen *chip = ctl->private_data;
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int reg;
+       int changed;
+
+       mutex_lock(&chip->mutex);
+       reg = data->wm8776_regs[WM8776_ADCIFCTRL] & ~WM8776_ADCHPD;
+       if (!value->value.enumerated.item[0])
+               reg |= WM8776_ADCHPD;
+       changed = reg != data->wm8776_regs[WM8776_ADCIFCTRL];
+       if (changed)
+               wm8776_write(chip, WM8776_ADCIFCTRL, reg);
+       mutex_unlock(&chip->mutex);
+       return changed;
+}
+
+#define WM8776_BIT_SWITCH(xname, reg, bit, invert, flags) { \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .info = snd_ctl_boolean_mono_info, \
+       .get = wm8776_bit_switch_get, \
+       .put = wm8776_bit_switch_put, \
+       .private_value = ((reg) << 16) | (bit) | ((invert) << 24) | (flags), \
+}
+#define _WM8776_FIELD_CTL(xname, reg, shift, initval, min, max, mask, flags) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .private_value = (initval) | ((min) << 8) | ((max) << 12) | \
+       ((mask) << 16) | ((shift) << 20) | ((reg) << 24) | (flags)
+#define WM8776_FIELD_CTL_ENUM(xname, reg, shift, init, min, max, mask, flags) {\
+       _WM8776_FIELD_CTL(xname " Capture Enum", \
+                         reg, shift, init, min, max, mask, flags), \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                 SNDRV_CTL_ELEM_ACCESS_INACTIVE, \
+       .info = wm8776_field_enum_info, \
+       .get = wm8776_field_enum_get, \
+       .put = wm8776_field_enum_put, \
+}
+#define WM8776_FIELD_CTL_VOLUME(a, b, c, d, e, f, g, h, tlv_p) { \
+       _WM8776_FIELD_CTL(a " Capture Volume", b, c, d, e, f, g, h), \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | \
+                 SNDRV_CTL_ELEM_ACCESS_INACTIVE | \
+                 SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+       .info = wm8776_field_volume_info, \
+       .get = wm8776_field_volume_get, \
+       .put = wm8776_field_volume_put, \
+       .tlv = { .p = tlv_p }, \
+}
+
+static const DECLARE_TLV_DB_SCALE(wm87x6_dac_db_scale, -6000, 50, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_adc_db_scale, -2100, 50, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_hp_db_scale, -6000, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_lct_db_scale, -1600, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxgain_db_scale, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_ngth_db_scale, -7800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_lim_db_scale, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(wm8776_maxatten_alc_db_scale, -2100, 400, 0);
+
+static const struct snd_kcontrol_new ds_controls[] = {
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Headphone Playback Volume",
+               .info = wm8776_hp_vol_info,
+               .get = wm8776_hp_vol_get,
+               .put = wm8776_hp_vol_put,
+               .tlv = { .p = wm8776_hp_db_scale },
+       },
+       WM8776_BIT_SWITCH("Headphone Playback Switch",
+                         WM8776_PWRDOWN, WM8776_HPPD, 1, 0),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Input Capture Volume",
+               .info = wm8776_input_vol_info,
+               .get = wm8776_input_vol_get,
+               .put = wm8776_input_vol_put,
+               .tlv = { .p = wm8776_adc_db_scale },
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Line Capture Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = wm8776_input_mux_get,
+               .put = wm8776_input_mux_put,
+               .private_value = 1 << 0,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Mic Capture Switch",
+               .info = snd_ctl_boolean_mono_info,
+               .get = wm8776_input_mux_get,
+               .put = wm8776_input_mux_put,
+               .private_value = 1 << 1,
+       },
+       WM8776_BIT_SWITCH("Aux", WM8776_ADCMUX, 1 << 2, 0, 0),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "ADC Filter Capture Enum",
+               .info = hpf_info,
+               .get = hpf_get,
+               .put = hpf_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Level Control Capture Enum",
+               .info = wm8776_level_control_info,
+               .get = wm8776_level_control_get,
+               .put = wm8776_level_control_put,
+               .private_value = 0,
+       },
+};
+static const struct snd_kcontrol_new lc_controls[] = {
+       WM8776_FIELD_CTL_VOLUME("Limiter Threshold",
+                               WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
+                               LC_CONTROL_LIMITER, wm8776_lct_db_scale),
+       WM8776_FIELD_CTL_ENUM("Limiter Attack Time",
+                             WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf,
+                             LC_CONTROL_LIMITER),
+       WM8776_FIELD_CTL_ENUM("Limiter Decay Time",
+                             WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf,
+                             LC_CONTROL_LIMITER),
+       WM8776_FIELD_CTL_ENUM("Limiter Transient Window",
+                             WM8776_LIMITER, 4, 2, 0, 7, 0x7,
+                             LC_CONTROL_LIMITER),
+       WM8776_FIELD_CTL_VOLUME("Limiter Maximum Attenuation",
+                               WM8776_LIMITER, 0, 6, 3, 12, 0xf,
+                               LC_CONTROL_LIMITER,
+                               wm8776_maxatten_lim_db_scale),
+       WM8776_FIELD_CTL_VOLUME("ALC Target Level",
+                               WM8776_ALCCTRL1, 0, 11, 0, 15, 0xf,
+                               LC_CONTROL_ALC, wm8776_lct_db_scale),
+       WM8776_FIELD_CTL_ENUM("ALC Attack Time",
+                             WM8776_ALCCTRL3, 0, 2, 0, 10, 0xf,
+                             LC_CONTROL_ALC),
+       WM8776_FIELD_CTL_ENUM("ALC Decay Time",
+                             WM8776_ALCCTRL3, 4, 3, 0, 10, 0xf,
+                             LC_CONTROL_ALC),
+       WM8776_FIELD_CTL_VOLUME("ALC Maximum Gain",
+                               WM8776_ALCCTRL1, 4, 7, 1, 7, 0x7,
+                               LC_CONTROL_ALC, wm8776_maxgain_db_scale),
+       WM8776_FIELD_CTL_VOLUME("ALC Maximum Attenuation",
+                               WM8776_LIMITER, 0, 10, 10, 15, 0xf,
+                               LC_CONTROL_ALC, wm8776_maxatten_alc_db_scale),
+       WM8776_FIELD_CTL_ENUM("ALC Hold Time",
+                             WM8776_ALCCTRL2, 0, 0, 0, 15, 0xf,
+                             LC_CONTROL_ALC),
+       WM8776_BIT_SWITCH("Noise Gate Capture Switch",
+                         WM8776_NOISEGATE, WM8776_NGAT, 0,
+                         LC_CONTROL_ALC),
+       WM8776_FIELD_CTL_VOLUME("Noise Gate Threshold",
+                               WM8776_NOISEGATE, 2, 0, 0, 7, 0x7,
+                               LC_CONTROL_ALC, wm8776_ngth_db_scale),
+};
+
+static int xonar_ds_control_filter(struct snd_kcontrol_new *template)
+{
+       if (!strncmp(template->name, "CD Capture ", 11))
+               return 1; /* no CD input */
+       return 0;
+}
+
+static int xonar_ds_mixer_init(struct oxygen *chip)
+{
+       struct xonar_wm87x6 *data = chip->model_data;
+       unsigned int i;
+       struct snd_kcontrol *ctl;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(ds_controls); ++i) {
+               ctl = snd_ctl_new1(&ds_controls[i], chip);
+               if (!ctl)
+                       return -ENOMEM;
+               err = snd_ctl_add(chip->card, ctl);
+               if (err < 0)
+                       return err;
+       }
+       BUILD_BUG_ON(ARRAY_SIZE(lc_controls) != ARRAY_SIZE(data->lc_controls));
+       for (i = 0; i < ARRAY_SIZE(lc_controls); ++i) {
+               ctl = snd_ctl_new1(&lc_controls[i], chip);
+               if (!ctl)
+                       return -ENOMEM;
+               err = snd_ctl_add(chip->card, ctl);
+               if (err < 0)
+                       return err;
+               data->lc_controls[i] = ctl;
+       }
+       return 0;
+}
+
+static const struct oxygen_model model_xonar_ds = {
+       .shortname = "Xonar DS",
+       .longname = "Asus Virtuoso 200",
+       .chip = "AV200",
+       .init = xonar_ds_init,
+       .control_filter = xonar_ds_control_filter,
+       .mixer_init = xonar_ds_mixer_init,
+       .cleanup = xonar_ds_cleanup,
+       .suspend = xonar_ds_suspend,
+       .resume = xonar_ds_resume,
+       .pcm_hardware_filter = wm8776_adc_hardware_filter,
+       .get_i2s_mclk = oxygen_default_i2s_mclk,
+       .set_dac_params = set_wm87x6_dac_params,
+       .set_adc_params = set_wm8776_adc_params,
+       .update_dac_volume = update_wm87x6_volume,
+       .update_dac_mute = update_wm87x6_mute,
+       .gpio_changed = xonar_ds_gpio_changed,
+       .dac_tlv = wm87x6_dac_db_scale,
+       .model_data_size = sizeof(struct xonar_wm87x6),
+       .device_config = PLAYBACK_0_TO_I2S |
+                        PLAYBACK_1_TO_SPDIF |
+                        CAPTURE_0_FROM_I2S_1,
+       .dac_channels = 8,
+       .dac_volume_min = 255 - 2*60,
+       .dac_volume_max = 255,
+       .function_flags = OXYGEN_FUNCTION_SPI,
+       .dac_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+       .adc_i2s_format = OXYGEN_I2S_FORMAT_LJUST,
+};
+
+int __devinit get_xonar_wm87x6_model(struct oxygen *chip,
+                                    const struct pci_device_id *id)
+{
+       switch (id->subdevice) {
+       case 0x838e:
+               chip->model = model_xonar_ds;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
index 833e9c7..95cfde2 100644 (file)
@@ -94,7 +94,7 @@ enum {
        PCI_ID_LAST
 };
 
-static struct pci_device_id pcxhr_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(pcxhr_ids) = {
        { 0x10b5, 0x9656, 0x1369, 0xb001, 0, 0, PCI_ID_VX882HR, },
        { 0x10b5, 0x9656, 0x1369, 0xb101, 0, 0, PCI_ID_PCX882HR, },
        { 0x10b5, 0x9656, 0x1369, 0xb201, 0, 0, PCI_ID_VX881HR, },
index e66ef2b..960a227 100644 (file)
@@ -506,7 +506,7 @@ static int riptide_reset(struct cmdif *cif, struct snd_riptide *chip);
 /*
  */
 
-static struct pci_device_id snd_riptide_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_riptide_ids) = {
        { PCI_DEVICE(0x127a, 0x4310) },
        { PCI_DEVICE(0x127a, 0x4320) },
        { PCI_DEVICE(0x127a, 0x4330) },
@@ -515,7 +515,7 @@ static struct pci_device_id snd_riptide_ids[] = {
 };
 
 #ifdef SUPPORT_JOYSTICK
-static struct pci_device_id snd_riptide_joystick_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(snd_riptide_joystick_ids) = {
        { PCI_DEVICE(0x127a, 0x4312) },
        { PCI_DEVICE(0x127a, 0x4322) },
        { PCI_DEVICE(0x127a, 0x4332) },
index f977dba..d5e1c6e 100644 (file)
@@ -226,7 +226,7 @@ struct rme32 {
        struct snd_kcontrol *spdif_ctl;
 };
 
-static struct pci_device_id snd_rme32_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_rme32_ids) = {
        {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32), 0,},
        {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_8), 0,},
        {PCI_VDEVICE(XILINX_RME, PCI_DEVICE_ID_RME_DIGI32_PRO), 0,},
index 2ba5c0f..9d5252b 100644 (file)
@@ -231,7 +231,7 @@ struct rme96 {
        struct snd_kcontrol   *spdif_ctl;
 };
 
-static struct pci_device_id snd_rme96_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_rme96_ids) = {
        { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96), 0, },
        { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8), 0, },
        { PCI_VDEVICE(XILINX, PCI_DEVICE_ID_RME_DIGI96_8_PRO), 0, },
index 7bb827c..52c6eb5 100644 (file)
@@ -585,7 +585,7 @@ static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_d
 }
 
 
-static struct pci_device_id snd_hdsp_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_hdsp_ids) = {
        {
                .vendor = PCI_VENDOR_ID_XILINX,
                .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP,
index a1b10d1..3d72c1e 100644 (file)
@@ -512,7 +512,7 @@ static char channel_map_madi_ss[HDSPM_MAX_CHANNELS] = {
 };
 
 
-static struct pci_device_id snd_hdspm_ids[] __devinitdata = {
+static DEFINE_PCI_DEVICE_TABLE(snd_hdspm_ids) = {
        {
         .vendor = PCI_VENDOR_ID_XILINX,
         .device = PCI_DEVICE_ID_XILINX_HAMMERFALL_DSP_MADI,
index bc539ab..44a3e2d 100644 (file)
@@ -314,7 +314,7 @@ static void snd_hammerfall_free_buffer(struct snd_dma_buffer *dmab, struct pci_d
 }
 
 
-static struct pci_device_id snd_rme9652_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_rme9652_ids) = {
        {
                .vendor    = 0x10ee,
                .device    = 0x3fc4,
index 1a5ff06..7e3e8fb 100644 (file)
@@ -48,7 +48,7 @@ MODULE_PARM_DESC(id, "ID string for SiS7019 Audio Accelerator.");
 module_param(enable, bool, 0444);
 MODULE_PARM_DESC(enable, "Enable SiS7019 Audio Accelerator.");
 
-static struct pci_device_id snd_sis7019_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_sis7019_ids) = {
        { PCI_DEVICE(PCI_VENDOR_ID_SI, 0x7019) },
        { 0, }
 };
index 1f6406c..337b9fa 100644 (file)
@@ -242,7 +242,7 @@ struct sonicvibes {
 #endif
 };
 
-static struct pci_device_id snd_sonic_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_sonic_ids) = {
        { PCI_VDEVICE(S3, 0xca00), 0, },
         { 0, }
 };
index 21cef97..6d05818 100644 (file)
@@ -62,7 +62,7 @@ MODULE_PARM_DESC(pcm_channels, "Number of hardware channels assigned for PCM.");
 module_param_array(wavetable_size, int, NULL, 0444);
 MODULE_PARM_DESC(wavetable_size, "Maximum memory size in kB for wavetable synth.");
 
-static struct pci_device_id snd_trident_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_trident_ids) = {
        {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_DX), 
                PCI_CLASS_MULTIMEDIA_AUDIO << 8, 0xffff00, 0},
        {PCI_DEVICE(PCI_VENDOR_ID_TRIDENT, PCI_DEVICE_ID_TRIDENT_4DWAVE_NX), 
index 8a332d2..9595b5b 100644 (file)
@@ -401,7 +401,7 @@ struct via82xx {
 #endif
 };
 
-static struct pci_device_id snd_via82xx_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_via82xx_ids) = {
        /* 0x1106, 0x3058 */
        { PCI_VDEVICE(VIA, PCI_DEVICE_ID_VIA_82C686_5), TYPE_CARD_VIA686, },    /* 686A */
        /* 0x1106, 0x3059 */
index 47eb615..f7e8bbb 100644 (file)
@@ -260,7 +260,7 @@ struct via82xx_modem {
        struct snd_info_entry *proc_entry;
 };
 
-static struct pci_device_id snd_via82xx_modem_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_via82xx_modem_ids) = {
        { PCI_VDEVICE(VIA, 0x3068), TYPE_CARD_VIA82XX_MODEM, },
        { 0, }
 };
index fc9136c..99a9a81 100644 (file)
@@ -60,7 +60,7 @@ enum {
        VX_PCI_VX222_NEW
 };
 
-static struct pci_device_id snd_vx222_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_vx222_ids) = {
        { 0x10b5, 0x9050, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_OLD, },   /* PLX */
        { 0x10b5, 0x9030, 0x1369, PCI_ANY_ID, 0, 0, VX_PCI_VX222_NEW, },   /* PLX */
        { 0, }
index e6b18b9..80c6821 100644 (file)
@@ -66,7 +66,7 @@ MODULE_PARM_DESC(joystick_port, "Joystick port address");
 module_param_array(rear_switch, bool, NULL, 0444);
 MODULE_PARM_DESC(rear_switch, "Enable shared rear/line-in switch");
 
-static struct pci_device_id snd_ymfpci_ids[] = {
+static DEFINE_PCI_DEVICE_TABLE(snd_ymfpci_ids) = {
        { PCI_VDEVICE(YAMAHA, 0x0004), 0, },   /* YMF724 */
        { PCI_VDEVICE(YAMAHA, 0x000d), 0, },   /* YMF724F */
        { PCI_VDEVICE(YAMAHA, 0x000a), 0, },   /* YMF740 */
index 5cfa608..0d668f4 100644 (file)
  */
 
 #include <linux/slab.h>
-#include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <sound/core.h>
 #include <sound/asoundef.h>
 #include "pdaudiocf.h"
 
 
-/*
- * we use a vmalloc'ed (sg-)buffer
- */
-
-/* get the physical page pointer on the given offset */
-static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs, unsigned long offset)
-{
-       void *pageptr = subs->runtime->dma_area + offset;
-       return vmalloc_to_page(pageptr);
-}
-
-/*
- * hw_params callback
- * NOTE: this may be called not only once per pcm open!
- */
-static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
-{
-       struct snd_pcm_runtime *runtime = subs->runtime;
-       if (runtime->dma_area) {
-               if (runtime->dma_bytes >= size)
-                       return 0; /* already enough large */
-               vfree(runtime->dma_area);
-       }
-       runtime->dma_area = vmalloc_32_user(size);
-       if (! runtime->dma_area)
-               return -ENOMEM;
-       runtime->dma_bytes = size;
-       return 0;
-}
-
-/*
- * hw_free callback
- * NOTE: this may be called not only once per pcm open!
- */
-static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream *subs)
-{
-       struct snd_pcm_runtime *runtime = subs->runtime;
-
-       vfree(runtime->dma_area);
-       runtime->dma_area = NULL;
-       return 0;
-}
-
 /*
  * clear the SRAM contents
  */
@@ -147,7 +103,8 @@ static int pdacf_pcm_trigger(struct snd_pcm_substream *subs, int cmd)
 static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs,
                                     struct snd_pcm_hw_params *hw_params)
 {
-       return snd_pcm_alloc_vmalloc_buffer(subs, params_buffer_bytes(hw_params));
+       return snd_pcm_lib_alloc_vmalloc_32_buffer
+                                       (subs, params_buffer_bytes(hw_params));
 }
 
 /*
@@ -155,7 +112,7 @@ static int pdacf_pcm_hw_params(struct snd_pcm_substream *subs,
  */
 static int pdacf_pcm_hw_free(struct snd_pcm_substream *subs)
 {
-       return snd_pcm_free_vmalloc_buffer(subs);
+       return snd_pcm_lib_free_vmalloc_buffer(subs);
 }
 
 /*
@@ -319,7 +276,8 @@ static struct snd_pcm_ops pdacf_pcm_capture_ops = {
        .prepare =      pdacf_pcm_prepare,
        .trigger =      pdacf_pcm_trigger,
        .pointer =      pdacf_pcm_capture_pointer,
-       .page =         snd_pcm_get_vmalloc_page,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
 };
 
 
index 73525c0..8c29258 100644 (file)
@@ -21,6 +21,18 @@ config SND_USB_AUDIO
          To compile this driver as a module, choose M here: the module
          will be called snd-usb-audio.
 
+config SND_USB_UA101
+       tristate "Edirol UA-101 driver (EXPERIMENTAL)"
+       depends on EXPERIMENTAL
+       select SND_PCM
+       select SND_RAWMIDI
+       help
+         Say Y here to include support for the Edirol UA-101 audio/MIDI
+         interface.
+
+         To compile this driver as a module, choose M here: the module
+         will be called snd-ua101.
+
 config SND_USB_USX2Y
        tristate "Tascam US-122, US-224 and US-428 USB driver"
        depends on X86 || PPC || ALPHA
index abb288b..5bf64ae 100644 (file)
@@ -4,9 +4,11 @@
 
 snd-usb-audio-objs := usbaudio.o usbmixer.o
 snd-usb-lib-objs := usbmidi.o
+snd-ua101-objs := ua101.o
 
 # Toplevel Module Dependency
 obj-$(CONFIG_SND_USB_AUDIO) += snd-usb-audio.o snd-usb-lib.o
+obj-$(CONFIG_SND_USB_UA101) += snd-ua101.o snd-usb-lib.o
 obj-$(CONFIG_SND_USB_USX2Y) += snd-usb-lib.o
 obj-$(CONFIG_SND_USB_US122L) += snd-usb-lib.o
 
diff --git a/sound/usb/ua101.c b/sound/usb/ua101.c
new file mode 100644 (file)
index 0000000..4f4ccdf
--- /dev/null
@@ -0,0 +1,1421 @@
+/*
+ * Edirol UA-101 driver
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ *
+ * This driver is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2.
+ *
+ * This driver 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 driver.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/audio.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "usbaudio.h"
+
+MODULE_DESCRIPTION("Edirol UA-101 driver");
+MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
+MODULE_LICENSE("GPL v2");
+MODULE_SUPPORTED_DEVICE("{{Edirol,UA-101}}");
+
+/* I use my UA-1A for testing because I don't have a UA-101 ... */
+#define UA1A_HACK
+
+/*
+ * Should not be lower than the minimum scheduling delay of the host
+ * controller.  Some Intel controllers need more than one frame; as long as
+ * that driver doesn't tell us about this, use 1.5 frames just to be sure.
+ */
+#define MIN_QUEUE_LENGTH       12
+/* Somewhat random. */
+#define MAX_QUEUE_LENGTH       30
+/*
+ * This magic value optimizes memory usage efficiency for the UA-101's packet
+ * sizes at all sample rates, taking into account the stupid cache pool sizes
+ * that usb_buffer_alloc() uses.
+ */
+#define DEFAULT_QUEUE_LENGTH   21
+
+#define MAX_PACKET_SIZE                672 /* hardware specific */
+#define MAX_MEMORY_BUFFERS     DIV_ROUND_UP(MAX_QUEUE_LENGTH, \
+                                            PAGE_SIZE / MAX_PACKET_SIZE)
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+static unsigned int queue_length = 21;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable card");
+module_param(queue_length, uint, 0644);
+MODULE_PARM_DESC(queue_length, "USB queue length in microframes, "
+                __stringify(MIN_QUEUE_LENGTH)"-"__stringify(MAX_QUEUE_LENGTH));
+
+enum {
+       INTF_PLAYBACK,
+       INTF_CAPTURE,
+       INTF_MIDI,
+
+       INTF_COUNT
+};
+
+/* bits in struct ua101::states */
+enum {
+       USB_CAPTURE_RUNNING,
+       USB_PLAYBACK_RUNNING,
+       ALSA_CAPTURE_OPEN,
+       ALSA_PLAYBACK_OPEN,
+       ALSA_CAPTURE_RUNNING,
+       ALSA_PLAYBACK_RUNNING,
+       CAPTURE_URB_COMPLETED,
+       PLAYBACK_URB_COMPLETED,
+       DISCONNECTED,
+};
+
+struct ua101 {
+       struct usb_device *dev;
+       struct snd_card *card;
+       struct usb_interface *intf[INTF_COUNT];
+       int card_index;
+       struct snd_pcm *pcm;
+       struct list_head midi_list;
+       u64 format_bit;
+       unsigned int rate;
+       unsigned int packets_per_second;
+       spinlock_t lock;
+       struct mutex mutex;
+       unsigned long states;
+
+       /* FIFO to synchronize playback rate to capture rate */
+       unsigned int rate_feedback_start;
+       unsigned int rate_feedback_count;
+       u8 rate_feedback[MAX_QUEUE_LENGTH];
+
+       struct list_head ready_playback_urbs;
+       struct tasklet_struct playback_tasklet;
+       wait_queue_head_t alsa_capture_wait;
+       wait_queue_head_t rate_feedback_wait;
+       wait_queue_head_t alsa_playback_wait;
+       struct ua101_stream {
+               struct snd_pcm_substream *substream;
+               unsigned int usb_pipe;
+               unsigned int channels;
+               unsigned int frame_bytes;
+               unsigned int max_packet_bytes;
+               unsigned int period_pos;
+               unsigned int buffer_pos;
+               unsigned int queue_length;
+               struct ua101_urb {
+                       struct urb urb;
+                       struct usb_iso_packet_descriptor iso_frame_desc[1];
+                       struct list_head ready_list;
+               } *urbs[MAX_QUEUE_LENGTH];
+               struct {
+                       unsigned int size;
+                       void *addr;
+                       dma_addr_t dma;
+               } buffers[MAX_MEMORY_BUFFERS];
+       } capture, playback;
+
+       unsigned int fps[10];
+       unsigned int frame_counter;
+};
+
+static DEFINE_MUTEX(devices_mutex);
+static unsigned int devices_used;
+static struct usb_driver ua101_driver;
+
+static void abort_alsa_playback(struct ua101 *ua);
+static void abort_alsa_capture(struct ua101 *ua);
+
+static const char *usb_error_string(int err)
+{
+       switch (err) {
+       case -ENODEV:
+               return "no device";
+       case -ENOENT:
+               return "endpoint not enabled";
+       case -EPIPE:
+               return "endpoint stalled";
+       case -ENOSPC:
+               return "not enough bandwidth";
+       case -ESHUTDOWN:
+               return "device disabled";
+       case -EHOSTUNREACH:
+               return "device suspended";
+       case -EINVAL:
+       case -EAGAIN:
+       case -EFBIG:
+       case -EMSGSIZE:
+               return "internal error";
+       default:
+               return "unknown error";
+       }
+}
+
+static void abort_usb_capture(struct ua101 *ua)
+{
+       if (test_and_clear_bit(USB_CAPTURE_RUNNING, &ua->states)) {
+               wake_up(&ua->alsa_capture_wait);
+               wake_up(&ua->rate_feedback_wait);
+       }
+}
+
+static void abort_usb_playback(struct ua101 *ua)
+{
+       if (test_and_clear_bit(USB_PLAYBACK_RUNNING, &ua->states))
+               wake_up(&ua->alsa_playback_wait);
+}
+
+static void playback_urb_complete(struct urb *usb_urb)
+{
+       struct ua101_urb *urb = (struct ua101_urb *)usb_urb;
+       struct ua101 *ua = urb->urb.context;
+       unsigned long flags;
+
+       if (unlikely(urb->urb.status == -ENOENT ||      /* unlinked */
+                    urb->urb.status == -ENODEV ||      /* device removed */
+                    urb->urb.status == -ECONNRESET ||  /* unlinked */
+                    urb->urb.status == -ESHUTDOWN)) {  /* device disabled */
+               abort_usb_playback(ua);
+               abort_alsa_playback(ua);
+               return;
+       }
+
+       if (test_bit(USB_PLAYBACK_RUNNING, &ua->states)) {
+               /* append URB to FIFO */
+               spin_lock_irqsave(&ua->lock, flags);
+               list_add_tail(&urb->ready_list, &ua->ready_playback_urbs);
+               if (ua->rate_feedback_count > 0)
+                       tasklet_schedule(&ua->playback_tasklet);
+               ua->playback.substream->runtime->delay -=
+                               urb->urb.iso_frame_desc[0].length /
+                                               ua->playback.frame_bytes;
+               spin_unlock_irqrestore(&ua->lock, flags);
+       }
+}
+
+static void first_playback_urb_complete(struct urb *urb)
+{
+       struct ua101 *ua = urb->context;
+
+       urb->complete = playback_urb_complete;
+       playback_urb_complete(urb);
+
+       set_bit(PLAYBACK_URB_COMPLETED, &ua->states);
+       wake_up(&ua->alsa_playback_wait);
+}
+
+/* copy data from the ALSA ring buffer into the URB buffer */
+static bool copy_playback_data(struct ua101_stream *stream, struct urb *urb,
+                              unsigned int frames)
+{
+       struct snd_pcm_runtime *runtime;
+       unsigned int frame_bytes, frames1;
+       const u8 *source;
+
+       runtime = stream->substream->runtime;
+       frame_bytes = stream->frame_bytes;
+       source = runtime->dma_area + stream->buffer_pos * frame_bytes;
+       if (stream->buffer_pos + frames <= runtime->buffer_size) {
+               memcpy(urb->transfer_buffer, source, frames * frame_bytes);
+       } else {
+               /* wrap around at end of ring buffer */
+               frames1 = runtime->buffer_size - stream->buffer_pos;
+               memcpy(urb->transfer_buffer, source, frames1 * frame_bytes);
+               memcpy(urb->transfer_buffer + frames1 * frame_bytes,
+                      runtime->dma_area, (frames - frames1) * frame_bytes);
+       }
+
+       stream->buffer_pos += frames;
+       if (stream->buffer_pos >= runtime->buffer_size)
+               stream->buffer_pos -= runtime->buffer_size;
+       stream->period_pos += frames;
+       if (stream->period_pos >= runtime->period_size) {
+               stream->period_pos -= runtime->period_size;
+               return true;
+       }
+       return false;
+}
+
+static inline void add_with_wraparound(struct ua101 *ua,
+                                      unsigned int *value, unsigned int add)
+{
+       *value += add;
+       if (*value >= ua->playback.queue_length)
+               *value -= ua->playback.queue_length;
+}
+
+static void playback_tasklet(unsigned long data)
+{
+       struct ua101 *ua = (void *)data;
+       unsigned long flags;
+       unsigned int frames;
+       struct ua101_urb *urb;
+       bool do_period_elapsed = false;
+       int err;
+
+       if (unlikely(!test_bit(USB_PLAYBACK_RUNNING, &ua->states)))
+               return;
+
+       /*
+        * Synchronizing the playback rate to the capture rate is done by using
+        * the same sequence of packet sizes for both streams.
+        * Submitting a playback URB therefore requires both a ready URB and
+        * the size of the corresponding capture packet, i.e., both playback
+        * and capture URBs must have been completed.  Since the USB core does
+        * not guarantee that playback and capture complete callbacks are
+        * called alternately, we use two FIFOs for packet sizes and read URBs;
+        * submitting playback URBs is possible as long as both FIFOs are
+        * nonempty.
+        */
+       spin_lock_irqsave(&ua->lock, flags);
+       while (ua->rate_feedback_count > 0 &&
+              !list_empty(&ua->ready_playback_urbs)) {
+               /* take packet size out of FIFO */
+               frames = ua->rate_feedback[ua->rate_feedback_start];
+               add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+               ua->rate_feedback_count--;
+
+               /* take URB out of FIFO */
+               urb = list_first_entry(&ua->ready_playback_urbs,
+                                      struct ua101_urb, ready_list);
+               list_del(&urb->ready_list);
+
+               /* fill packet with data or silence */
+               urb->urb.iso_frame_desc[0].length =
+                       frames * ua->playback.frame_bytes;
+               if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
+                       do_period_elapsed |= copy_playback_data(&ua->playback,
+                                                               &urb->urb,
+                                                               frames);
+               else
+                       memset(urb->urb.transfer_buffer, 0,
+                              urb->urb.iso_frame_desc[0].length);
+
+               /* and off you go ... */
+               err = usb_submit_urb(&urb->urb, GFP_ATOMIC);
+               if (unlikely(err < 0)) {
+                       spin_unlock_irqrestore(&ua->lock, flags);
+                       abort_usb_playback(ua);
+                       abort_alsa_playback(ua);
+                       dev_err(&ua->dev->dev, "USB request error %d: %s\n",
+                               err, usb_error_string(err));
+                       return;
+               }
+               ua->playback.substream->runtime->delay += frames;
+       }
+       spin_unlock_irqrestore(&ua->lock, flags);
+       if (do_period_elapsed)
+               snd_pcm_period_elapsed(ua->playback.substream);
+}
+
+/* copy data from the URB buffer into the ALSA ring buffer */
+static bool copy_capture_data(struct ua101_stream *stream, struct urb *urb,
+                             unsigned int frames)
+{
+       struct snd_pcm_runtime *runtime;
+       unsigned int frame_bytes, frames1;
+       u8 *dest;
+
+       runtime = stream->substream->runtime;
+       frame_bytes = stream->frame_bytes;
+       dest = runtime->dma_area + stream->buffer_pos * frame_bytes;
+       if (stream->buffer_pos + frames <= runtime->buffer_size) {
+               memcpy(dest, urb->transfer_buffer, frames * frame_bytes);
+       } else {
+               /* wrap around at end of ring buffer */
+               frames1 = runtime->buffer_size - stream->buffer_pos;
+               memcpy(dest, urb->transfer_buffer, frames1 * frame_bytes);
+               memcpy(runtime->dma_area,
+                      urb->transfer_buffer + frames1 * frame_bytes,
+                      (frames - frames1) * frame_bytes);
+       }
+
+       stream->buffer_pos += frames;
+       if (stream->buffer_pos >= runtime->buffer_size)
+               stream->buffer_pos -= runtime->buffer_size;
+       stream->period_pos += frames;
+       if (stream->period_pos >= runtime->period_size) {
+               stream->period_pos -= runtime->period_size;
+               return true;
+       }
+       return false;
+}
+
+static void capture_urb_complete(struct urb *urb)
+{
+       struct ua101 *ua = urb->context;
+       struct ua101_stream *stream = &ua->capture;
+       unsigned long flags;
+       unsigned int frames, write_ptr;
+       bool do_period_elapsed;
+       int err;
+
+       if (unlikely(urb->status == -ENOENT ||          /* unlinked */
+                    urb->status == -ENODEV ||          /* device removed */
+                    urb->status == -ECONNRESET ||      /* unlinked */
+                    urb->status == -ESHUTDOWN))        /* device disabled */
+               goto stream_stopped;
+
+       if (urb->status >= 0 && urb->iso_frame_desc[0].status >= 0)
+               frames = urb->iso_frame_desc[0].actual_length /
+                       stream->frame_bytes;
+       else
+               frames = 0;
+
+       spin_lock_irqsave(&ua->lock, flags);
+
+       if (frames > 0 && test_bit(ALSA_CAPTURE_RUNNING, &ua->states))
+               do_period_elapsed = copy_capture_data(stream, urb, frames);
+       else
+               do_period_elapsed = false;
+
+       if (test_bit(USB_CAPTURE_RUNNING, &ua->states)) {
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (unlikely(err < 0)) {
+                       spin_unlock_irqrestore(&ua->lock, flags);
+                       dev_err(&ua->dev->dev, "USB request error %d: %s\n",
+                               err, usb_error_string(err));
+                       goto stream_stopped;
+               }
+
+               /* append packet size to FIFO */
+               write_ptr = ua->rate_feedback_start;
+               add_with_wraparound(ua, &write_ptr, ua->rate_feedback_count);
+               ua->rate_feedback[write_ptr] = frames;
+               if (ua->rate_feedback_count < ua->playback.queue_length) {
+                       ua->rate_feedback_count++;
+                       if (ua->rate_feedback_count ==
+                                               ua->playback.queue_length)
+                               wake_up(&ua->rate_feedback_wait);
+               } else {
+                       /*
+                        * Ring buffer overflow; this happens when the playback
+                        * stream is not running.  Throw away the oldest entry,
+                        * so that the playback stream, when it starts, sees
+                        * the most recent packet sizes.
+                        */
+                       add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+               }
+               if (test_bit(USB_PLAYBACK_RUNNING, &ua->states) &&
+                   !list_empty(&ua->ready_playback_urbs))
+                       tasklet_schedule(&ua->playback_tasklet);
+       }
+
+       spin_unlock_irqrestore(&ua->lock, flags);
+
+       if (do_period_elapsed)
+               snd_pcm_period_elapsed(stream->substream);
+
+       /* for debugging: measure the sample rate relative to the USB clock */
+       ua->fps[ua->frame_counter++ / ua->packets_per_second] += frames;
+       if (ua->frame_counter >= ARRAY_SIZE(ua->fps) * ua->packets_per_second) {
+               printk(KERN_DEBUG "capture rate:");
+               for (frames = 0; frames < ARRAY_SIZE(ua->fps); ++frames)
+                       printk(KERN_CONT " %u", ua->fps[frames]);
+               printk(KERN_CONT "\n");
+               memset(ua->fps, 0, sizeof(ua->fps));
+               ua->frame_counter = 0;
+       }
+       return;
+
+stream_stopped:
+       abort_usb_playback(ua);
+       abort_usb_capture(ua);
+       abort_alsa_playback(ua);
+       abort_alsa_capture(ua);
+}
+
+static void first_capture_urb_complete(struct urb *urb)
+{
+       struct ua101 *ua = urb->context;
+
+       urb->complete = capture_urb_complete;
+       capture_urb_complete(urb);
+
+       set_bit(CAPTURE_URB_COMPLETED, &ua->states);
+       wake_up(&ua->alsa_capture_wait);
+}
+
+static int submit_stream_urbs(struct ua101 *ua, struct ua101_stream *stream)
+{
+       unsigned int i;
+
+       for (i = 0; i < stream->queue_length; ++i) {
+               int err = usb_submit_urb(&stream->urbs[i]->urb, GFP_KERNEL);
+               if (err < 0) {
+                       dev_err(&ua->dev->dev, "USB request error %d: %s\n",
+                               err, usb_error_string(err));
+                       return err;
+               }
+       }
+       return 0;
+}
+
+static void kill_stream_urbs(struct ua101_stream *stream)
+{
+       unsigned int i;
+
+       for (i = 0; i < stream->queue_length; ++i)
+               usb_kill_urb(&stream->urbs[i]->urb);
+}
+
+static int enable_iso_interface(struct ua101 *ua, unsigned int intf_index)
+{
+       struct usb_host_interface *alts;
+
+       alts = ua->intf[intf_index]->cur_altsetting;
+       if (alts->desc.bAlternateSetting != 1) {
+               int err = usb_set_interface(ua->dev,
+                                           alts->desc.bInterfaceNumber, 1);
+               if (err < 0) {
+                       dev_err(&ua->dev->dev,
+                               "cannot initialize interface; error %d: %s\n",
+                               err, usb_error_string(err));
+                       return err;
+               }
+       }
+       return 0;
+}
+
+static void disable_iso_interface(struct ua101 *ua, unsigned int intf_index)
+{
+       struct usb_host_interface *alts;
+
+       alts = ua->intf[intf_index]->cur_altsetting;
+       if (alts->desc.bAlternateSetting != 0) {
+               int err = usb_set_interface(ua->dev,
+                                           alts->desc.bInterfaceNumber, 0);
+               if (err < 0 && !test_bit(DISCONNECTED, &ua->states))
+                       dev_warn(&ua->dev->dev,
+                                "interface reset failed; error %d: %s\n",
+                                err, usb_error_string(err));
+       }
+}
+
+static void stop_usb_capture(struct ua101 *ua)
+{
+       clear_bit(USB_CAPTURE_RUNNING, &ua->states);
+
+       kill_stream_urbs(&ua->capture);
+
+       disable_iso_interface(ua, INTF_CAPTURE);
+}
+
+static int start_usb_capture(struct ua101 *ua)
+{
+       int err;
+
+       if (test_bit(DISCONNECTED, &ua->states))
+               return -ENODEV;
+
+       if (test_bit(USB_CAPTURE_RUNNING, &ua->states))
+               return 0;
+
+       kill_stream_urbs(&ua->capture);
+
+       err = enable_iso_interface(ua, INTF_CAPTURE);
+       if (err < 0)
+               return err;
+
+       clear_bit(CAPTURE_URB_COMPLETED, &ua->states);
+       ua->capture.urbs[0]->urb.complete = first_capture_urb_complete;
+       ua->rate_feedback_start = 0;
+       ua->rate_feedback_count = 0;
+
+       set_bit(USB_CAPTURE_RUNNING, &ua->states);
+       err = submit_stream_urbs(ua, &ua->capture);
+       if (err < 0)
+               stop_usb_capture(ua);
+       return err;
+}
+
+static void stop_usb_playback(struct ua101 *ua)
+{
+       clear_bit(USB_PLAYBACK_RUNNING, &ua->states);
+
+       kill_stream_urbs(&ua->playback);
+
+       tasklet_kill(&ua->playback_tasklet);
+
+       disable_iso_interface(ua, INTF_PLAYBACK);
+}
+
+static int start_usb_playback(struct ua101 *ua)
+{
+       unsigned int i, frames;
+       struct urb *urb;
+       int err = 0;
+
+       if (test_bit(DISCONNECTED, &ua->states))
+               return -ENODEV;
+
+       if (test_bit(USB_PLAYBACK_RUNNING, &ua->states))
+               return 0;
+
+       kill_stream_urbs(&ua->playback);
+       tasklet_kill(&ua->playback_tasklet);
+
+       err = enable_iso_interface(ua, INTF_PLAYBACK);
+       if (err < 0)
+               return err;
+
+       clear_bit(PLAYBACK_URB_COMPLETED, &ua->states);
+       ua->playback.urbs[0]->urb.complete =
+               first_playback_urb_complete;
+       spin_lock_irq(&ua->lock);
+       INIT_LIST_HEAD(&ua->ready_playback_urbs);
+       spin_unlock_irq(&ua->lock);
+
+       /*
+        * We submit the initial URBs all at once, so we have to wait for the
+        * packet size FIFO to be full.
+        */
+       wait_event(ua->rate_feedback_wait,
+                  ua->rate_feedback_count >= ua->playback.queue_length ||
+                  !test_bit(USB_CAPTURE_RUNNING, &ua->states) ||
+                  test_bit(DISCONNECTED, &ua->states));
+       if (test_bit(DISCONNECTED, &ua->states)) {
+               stop_usb_playback(ua);
+               return -ENODEV;
+       }
+       if (!test_bit(USB_CAPTURE_RUNNING, &ua->states)) {
+               stop_usb_playback(ua);
+               return -EIO;
+       }
+
+       for (i = 0; i < ua->playback.queue_length; ++i) {
+               /* all initial URBs contain silence */
+               spin_lock_irq(&ua->lock);
+               frames = ua->rate_feedback[ua->rate_feedback_start];
+               add_with_wraparound(ua, &ua->rate_feedback_start, 1);
+               ua->rate_feedback_count--;
+               spin_unlock_irq(&ua->lock);
+               urb = &ua->playback.urbs[i]->urb;
+               urb->iso_frame_desc[0].length =
+                       frames * ua->playback.frame_bytes;
+               memset(urb->transfer_buffer, 0,
+                      urb->iso_frame_desc[0].length);
+       }
+
+       set_bit(USB_PLAYBACK_RUNNING, &ua->states);
+       err = submit_stream_urbs(ua, &ua->playback);
+       if (err < 0)
+               stop_usb_playback(ua);
+       return err;
+}
+
+static void abort_alsa_capture(struct ua101 *ua)
+{
+       if (test_bit(ALSA_CAPTURE_RUNNING, &ua->states))
+               snd_pcm_stop(ua->capture.substream, SNDRV_PCM_STATE_XRUN);
+}
+
+static void abort_alsa_playback(struct ua101 *ua)
+{
+       if (test_bit(ALSA_PLAYBACK_RUNNING, &ua->states))
+               snd_pcm_stop(ua->playback.substream, SNDRV_PCM_STATE_XRUN);
+}
+
+static int set_stream_hw(struct ua101 *ua, struct snd_pcm_substream *substream,
+                        unsigned int channels)
+{
+       int err;
+
+       substream->runtime->hw.info =
+               SNDRV_PCM_INFO_MMAP |
+               SNDRV_PCM_INFO_MMAP_VALID |
+               SNDRV_PCM_INFO_BATCH |
+               SNDRV_PCM_INFO_INTERLEAVED |
+               SNDRV_PCM_INFO_BLOCK_TRANSFER |
+               SNDRV_PCM_INFO_FIFO_IN_FRAMES;
+       substream->runtime->hw.formats = ua->format_bit;
+       substream->runtime->hw.rates = snd_pcm_rate_to_rate_bit(ua->rate);
+       substream->runtime->hw.rate_min = ua->rate;
+       substream->runtime->hw.rate_max = ua->rate;
+       substream->runtime->hw.channels_min = channels;
+       substream->runtime->hw.channels_max = channels;
+       substream->runtime->hw.buffer_bytes_max = 45000 * 1024;
+       substream->runtime->hw.period_bytes_min = 1;
+       substream->runtime->hw.period_bytes_max = UINT_MAX;
+       substream->runtime->hw.periods_min = 2;
+       substream->runtime->hw.periods_max = UINT_MAX;
+       err = snd_pcm_hw_constraint_minmax(substream->runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                          1500000 / ua->packets_per_second,
+                                          8192000);
+       if (err < 0)
+               return err;
+       err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24);
+       return err;
+}
+
+static int capture_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct ua101 *ua = substream->private_data;
+       int err;
+
+       ua->capture.substream = substream;
+       err = set_stream_hw(ua, substream, ua->capture.channels);
+       if (err < 0)
+               return err;
+       substream->runtime->hw.fifo_size =
+               DIV_ROUND_CLOSEST(ua->rate, ua->packets_per_second);
+       substream->runtime->delay = substream->runtime->hw.fifo_size;
+
+       mutex_lock(&ua->mutex);
+       err = start_usb_capture(ua);
+       if (err >= 0)
+               set_bit(ALSA_CAPTURE_OPEN, &ua->states);
+       mutex_unlock(&ua->mutex);
+       return err;
+}
+
+static int playback_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct ua101 *ua = substream->private_data;
+       int err;
+
+       ua->playback.substream = substream;
+       err = set_stream_hw(ua, substream, ua->playback.channels);
+       if (err < 0)
+               return err;
+       substream->runtime->hw.fifo_size =
+               DIV_ROUND_CLOSEST(ua->rate * ua->playback.queue_length,
+                                 ua->packets_per_second);
+
+       mutex_lock(&ua->mutex);
+       err = start_usb_capture(ua);
+       if (err < 0)
+               goto error;
+       err = start_usb_playback(ua);
+       if (err < 0) {
+               if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states))
+                       stop_usb_capture(ua);
+               goto error;
+       }
+       set_bit(ALSA_PLAYBACK_OPEN, &ua->states);
+error:
+       mutex_unlock(&ua->mutex);
+       return err;
+}
+
+static int capture_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct ua101 *ua = substream->private_data;
+
+       mutex_lock(&ua->mutex);
+       clear_bit(ALSA_CAPTURE_OPEN, &ua->states);
+       if (!test_bit(ALSA_PLAYBACK_OPEN, &ua->states))
+               stop_usb_capture(ua);
+       mutex_unlock(&ua->mutex);
+       return 0;
+}
+
+static int playback_pcm_close(struct snd_pcm_substream *substream)
+{
+       struct ua101 *ua = substream->private_data;
+
+       mutex_lock(&ua->mutex);
+       stop_usb_playback(ua);
+       clear_bit(ALSA_PLAYBACK_OPEN, &ua->states);
+       if (!test_bit(ALSA_CAPTURE_OPEN, &ua->states))
+               stop_usb_capture(ua);
+       mutex_unlock(&ua->mutex);
+       return 0;
+}
+
+static int capture_pcm_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct ua101 *ua = substream->private_data;
+       int err;
+
+       mutex_lock(&ua->mutex);
+       err = start_usb_capture(ua);
+       mutex_unlock(&ua->mutex);
+       if (err < 0)
+               return err;
+
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
+}
+
+static int playback_pcm_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct ua101 *ua = substream->private_data;
+       int err;
+
+       mutex_lock(&ua->mutex);
+       err = start_usb_capture(ua);
+       if (err >= 0)
+               err = start_usb_playback(ua);
+       mutex_unlock(&ua->mutex);
+       if (err < 0)
+               return err;
+
+       return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                               params_buffer_bytes(hw_params));
+}
+
+static int ua101_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int capture_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct ua101 *ua = substream->private_data;
+       int err;
+
+       mutex_lock(&ua->mutex);
+       err = start_usb_capture(ua);
+       mutex_unlock(&ua->mutex);
+       if (err < 0)
+               return err;
+
+       /*
+        * The EHCI driver schedules the first packet of an iso stream at 10 ms
+        * in the future, i.e., no data is actually captured for that long.
+        * Take the wait here so that the stream is known to be actually
+        * running when the start trigger has been called.
+        */
+       wait_event(ua->alsa_capture_wait,
+                  test_bit(CAPTURE_URB_COMPLETED, &ua->states) ||
+                  !test_bit(USB_CAPTURE_RUNNING, &ua->states));
+       if (test_bit(DISCONNECTED, &ua->states))
+               return -ENODEV;
+       if (!test_bit(USB_CAPTURE_RUNNING, &ua->states))
+               return -EIO;
+
+       ua->capture.period_pos = 0;
+       ua->capture.buffer_pos = 0;
+       return 0;
+}
+
+static int playback_pcm_prepare(struct snd_pcm_substream *substream)
+{
+       struct ua101 *ua = substream->private_data;
+       int err;
+
+       mutex_lock(&ua->mutex);
+       err = start_usb_capture(ua);
+       if (err >= 0)
+               err = start_usb_playback(ua);
+       mutex_unlock(&ua->mutex);
+       if (err < 0)
+               return err;
+
+       /* see the comment in capture_pcm_prepare() */
+       wait_event(ua->alsa_playback_wait,
+                  test_bit(PLAYBACK_URB_COMPLETED, &ua->states) ||
+                  !test_bit(USB_PLAYBACK_RUNNING, &ua->states));
+       if (test_bit(DISCONNECTED, &ua->states))
+               return -ENODEV;
+       if (!test_bit(USB_PLAYBACK_RUNNING, &ua->states))
+               return -EIO;
+
+       substream->runtime->delay = 0;
+       ua->playback.period_pos = 0;
+       ua->playback.buffer_pos = 0;
+       return 0;
+}
+
+static int capture_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct ua101 *ua = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               if (!test_bit(USB_CAPTURE_RUNNING, &ua->states))
+                       return -EIO;
+               set_bit(ALSA_CAPTURE_RUNNING, &ua->states);
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               clear_bit(ALSA_CAPTURE_RUNNING, &ua->states);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int playback_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct ua101 *ua = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               if (!test_bit(USB_PLAYBACK_RUNNING, &ua->states))
+                       return -EIO;
+               set_bit(ALSA_PLAYBACK_RUNNING, &ua->states);
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               clear_bit(ALSA_PLAYBACK_RUNNING, &ua->states);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+static inline snd_pcm_uframes_t ua101_pcm_pointer(struct ua101 *ua,
+                                                 struct ua101_stream *stream)
+{
+       unsigned long flags;
+       unsigned int pos;
+
+       spin_lock_irqsave(&ua->lock, flags);
+       pos = stream->buffer_pos;
+       spin_unlock_irqrestore(&ua->lock, flags);
+       return pos;
+}
+
+static snd_pcm_uframes_t capture_pcm_pointer(struct snd_pcm_substream *subs)
+{
+       struct ua101 *ua = subs->private_data;
+
+       return ua101_pcm_pointer(ua, &ua->capture);
+}
+
+static snd_pcm_uframes_t playback_pcm_pointer(struct snd_pcm_substream *subs)
+{
+       struct ua101 *ua = subs->private_data;
+
+       return ua101_pcm_pointer(ua, &ua->playback);
+}
+
+static struct snd_pcm_ops capture_pcm_ops = {
+       .open = capture_pcm_open,
+       .close = capture_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = capture_pcm_hw_params,
+       .hw_free = ua101_pcm_hw_free,
+       .prepare = capture_pcm_prepare,
+       .trigger = capture_pcm_trigger,
+       .pointer = capture_pcm_pointer,
+       .page = snd_pcm_lib_get_vmalloc_page,
+       .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+static struct snd_pcm_ops playback_pcm_ops = {
+       .open = playback_pcm_open,
+       .close = playback_pcm_close,
+       .ioctl = snd_pcm_lib_ioctl,
+       .hw_params = playback_pcm_hw_params,
+       .hw_free = ua101_pcm_hw_free,
+       .prepare = playback_pcm_prepare,
+       .trigger = playback_pcm_trigger,
+       .pointer = playback_pcm_pointer,
+       .page = snd_pcm_lib_get_vmalloc_page,
+       .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+static const struct uac_format_type_i_discrete_descriptor *
+find_format_descriptor(struct usb_interface *interface)
+{
+       struct usb_host_interface *alt;
+       u8 *extra;
+       int extralen;
+
+       if (interface->num_altsetting != 2) {
+               dev_err(&interface->dev, "invalid num_altsetting\n");
+               return NULL;
+       }
+
+       alt = &interface->altsetting[0];
+       if (alt->desc.bNumEndpoints != 0) {
+               dev_err(&interface->dev, "invalid bNumEndpoints\n");
+               return NULL;
+       }
+
+       alt = &interface->altsetting[1];
+       if (alt->desc.bNumEndpoints != 1) {
+               dev_err(&interface->dev, "invalid bNumEndpoints\n");
+               return NULL;
+       }
+
+       extra = alt->extra;
+       extralen = alt->extralen;
+       while (extralen >= sizeof(struct usb_descriptor_header)) {
+               struct uac_format_type_i_discrete_descriptor *desc;
+
+               desc = (struct uac_format_type_i_discrete_descriptor *)extra;
+               if (desc->bLength > extralen) {
+                       dev_err(&interface->dev, "descriptor overflow\n");
+                       return NULL;
+               }
+               if (desc->bLength == UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(1) &&
+                   desc->bDescriptorType == USB_DT_CS_INTERFACE &&
+                   desc->bDescriptorSubtype == UAC_FORMAT_TYPE) {
+                       if (desc->bFormatType != UAC_FORMAT_TYPE_I_PCM ||
+                           desc->bSamFreqType != 1) {
+                               dev_err(&interface->dev,
+                                       "invalid format type\n");
+                               return NULL;
+                       }
+                       return desc;
+               }
+               extralen -= desc->bLength;
+               extra += desc->bLength;
+       }
+       dev_err(&interface->dev, "sample format descriptor not found\n");
+       return NULL;
+}
+
+static int detect_usb_format(struct ua101 *ua)
+{
+       const struct uac_format_type_i_discrete_descriptor *fmt_capture;
+       const struct uac_format_type_i_discrete_descriptor *fmt_playback;
+       const struct usb_endpoint_descriptor *epd;
+       unsigned int rate2;
+
+       fmt_capture = find_format_descriptor(ua->intf[INTF_CAPTURE]);
+       fmt_playback = find_format_descriptor(ua->intf[INTF_PLAYBACK]);
+       if (!fmt_capture || !fmt_playback)
+               return -ENXIO;
+
+       switch (fmt_capture->bSubframeSize) {
+       case 3:
+               ua->format_bit = SNDRV_PCM_FMTBIT_S24_3LE;
+               break;
+       case 4:
+               ua->format_bit = SNDRV_PCM_FMTBIT_S32_LE;
+               break;
+       default:
+               dev_err(&ua->dev->dev, "sample width is not 24 or 32 bits\n");
+               return -ENXIO;
+       }
+       if (fmt_capture->bSubframeSize != fmt_playback->bSubframeSize) {
+               dev_err(&ua->dev->dev,
+                       "playback/capture sample widths do not match\n");
+               return -ENXIO;
+       }
+
+       if (fmt_capture->bBitResolution != 24 ||
+           fmt_playback->bBitResolution != 24) {
+               dev_err(&ua->dev->dev, "sample width is not 24 bits\n");
+               return -ENXIO;
+       }
+
+       ua->rate = combine_triple(fmt_capture->tSamFreq[0]);
+       rate2 = combine_triple(fmt_playback->tSamFreq[0]);
+       if (ua->rate != rate2) {
+               dev_err(&ua->dev->dev,
+                       "playback/capture rates do not match: %u/%u\n",
+                       rate2, ua->rate);
+               return -ENXIO;
+       }
+
+       switch (ua->dev->speed) {
+       case USB_SPEED_FULL:
+               ua->packets_per_second = 1000;
+               break;
+       case USB_SPEED_HIGH:
+               ua->packets_per_second = 8000;
+               break;
+       default:
+               dev_err(&ua->dev->dev, "unknown device speed\n");
+               return -ENXIO;
+       }
+
+       ua->capture.channels = fmt_capture->bNrChannels;
+       ua->playback.channels = fmt_playback->bNrChannels;
+       ua->capture.frame_bytes =
+               fmt_capture->bSubframeSize * ua->capture.channels;
+       ua->playback.frame_bytes =
+               fmt_playback->bSubframeSize * ua->playback.channels;
+
+       epd = &ua->intf[INTF_CAPTURE]->altsetting[1].endpoint[0].desc;
+       if (!usb_endpoint_is_isoc_in(epd)) {
+               dev_err(&ua->dev->dev, "invalid capture endpoint\n");
+               return -ENXIO;
+       }
+       ua->capture.usb_pipe = usb_rcvisocpipe(ua->dev, usb_endpoint_num(epd));
+       ua->capture.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize);
+
+       epd = &ua->intf[INTF_PLAYBACK]->altsetting[1].endpoint[0].desc;
+       if (!usb_endpoint_is_isoc_out(epd)) {
+               dev_err(&ua->dev->dev, "invalid playback endpoint\n");
+               return -ENXIO;
+       }
+       ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, usb_endpoint_num(epd));
+       ua->playback.max_packet_bytes = le16_to_cpu(epd->wMaxPacketSize);
+       return 0;
+}
+
+static int alloc_stream_buffers(struct ua101 *ua, struct ua101_stream *stream)
+{
+       unsigned int remaining_packets, packets, packets_per_page, i;
+       size_t size;
+
+       stream->queue_length = queue_length;
+       stream->queue_length = max(stream->queue_length,
+                                  (unsigned int)MIN_QUEUE_LENGTH);
+       stream->queue_length = min(stream->queue_length,
+                                  (unsigned int)MAX_QUEUE_LENGTH);
+
+       /*
+        * The cache pool sizes used by usb_buffer_alloc() (128, 512, 2048) are
+        * quite bad when used with the packet sizes of this device (e.g. 280,
+        * 520, 624).  Therefore, we allocate and subdivide entire pages, using
+        * a smaller buffer only for the last chunk.
+        */
+       remaining_packets = stream->queue_length;
+       packets_per_page = PAGE_SIZE / stream->max_packet_bytes;
+       for (i = 0; i < ARRAY_SIZE(stream->buffers); ++i) {
+               packets = min(remaining_packets, packets_per_page);
+               size = packets * stream->max_packet_bytes;
+               stream->buffers[i].addr =
+                       usb_buffer_alloc(ua->dev, size, GFP_KERNEL,
+                                        &stream->buffers[i].dma);
+               if (!stream->buffers[i].addr)
+                       return -ENOMEM;
+               stream->buffers[i].size = size;
+               remaining_packets -= packets;
+               if (!remaining_packets)
+                       break;
+       }
+       if (remaining_packets) {
+               dev_err(&ua->dev->dev, "too many packets\n");
+               return -ENXIO;
+       }
+       return 0;
+}
+
+static void free_stream_buffers(struct ua101 *ua, struct ua101_stream *stream)
+{
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(stream->buffers); ++i)
+               usb_buffer_free(ua->dev,
+                               stream->buffers[i].size,
+                               stream->buffers[i].addr,
+                               stream->buffers[i].dma);
+}
+
+static int alloc_stream_urbs(struct ua101 *ua, struct ua101_stream *stream,
+                            void (*urb_complete)(struct urb *))
+{
+       unsigned max_packet_size = stream->max_packet_bytes;
+       struct ua101_urb *urb;
+       unsigned int b, u = 0;
+
+       for (b = 0; b < ARRAY_SIZE(stream->buffers); ++b) {
+               unsigned int size = stream->buffers[b].size;
+               u8 *addr = stream->buffers[b].addr;
+               dma_addr_t dma = stream->buffers[b].dma;
+
+               while (size >= max_packet_size) {
+                       if (u >= stream->queue_length)
+                               goto bufsize_error;
+                       urb = kmalloc(sizeof(*urb), GFP_KERNEL);
+                       if (!urb)
+                               return -ENOMEM;
+                       usb_init_urb(&urb->urb);
+                       urb->urb.dev = ua->dev;
+                       urb->urb.pipe = stream->usb_pipe;
+                       urb->urb.transfer_flags = URB_ISO_ASAP |
+                                       URB_NO_TRANSFER_DMA_MAP;
+                       urb->urb.transfer_buffer = addr;
+                       urb->urb.transfer_dma = dma;
+                       urb->urb.transfer_buffer_length = max_packet_size;
+                       urb->urb.number_of_packets = 1;
+                       urb->urb.interval = 1;
+                       urb->urb.context = ua;
+                       urb->urb.complete = urb_complete;
+                       urb->urb.iso_frame_desc[0].offset = 0;
+                       urb->urb.iso_frame_desc[0].length = max_packet_size;
+                       stream->urbs[u++] = urb;
+                       size -= max_packet_size;
+                       addr += max_packet_size;
+                       dma += max_packet_size;
+               }
+       }
+       if (u == stream->queue_length)
+               return 0;
+bufsize_error:
+       dev_err(&ua->dev->dev, "internal buffer size error\n");
+       return -ENXIO;
+}
+
+static void free_stream_urbs(struct ua101_stream *stream)
+{
+       unsigned int i;
+
+       for (i = 0; i < stream->queue_length; ++i)
+               kfree(stream->urbs[i]);
+}
+
+static void free_usb_related_resources(struct ua101 *ua,
+                                      struct usb_interface *interface)
+{
+       unsigned int i;
+
+       free_stream_urbs(&ua->capture);
+       free_stream_urbs(&ua->playback);
+       free_stream_buffers(ua, &ua->capture);
+       free_stream_buffers(ua, &ua->playback);
+
+       for (i = 0; i < ARRAY_SIZE(ua->intf); ++i)
+               if (ua->intf[i]) {
+                       usb_set_intfdata(ua->intf[i], NULL);
+                       if (ua->intf[i] != interface)
+                               usb_driver_release_interface(&ua101_driver,
+                                                            ua->intf[i]);
+               }
+}
+
+static void ua101_card_free(struct snd_card *card)
+{
+       struct ua101 *ua = card->private_data;
+
+       mutex_destroy(&ua->mutex);
+}
+
+static int ua101_probe(struct usb_interface *interface,
+                      const struct usb_device_id *usb_id)
+{
+       static const struct snd_usb_midi_endpoint_info midi_ep = {
+               .out_cables = 0x0001,
+               .in_cables = 0x0001
+       };
+       static const struct snd_usb_audio_quirk midi_quirk = {
+               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+               .data = &midi_ep
+       };
+       struct snd_card *card;
+       struct ua101 *ua;
+       unsigned int card_index, i;
+       char usb_path[32];
+       int err;
+
+       if (interface->altsetting->desc.bInterfaceNumber != 0)
+               return -ENODEV;
+
+       mutex_lock(&devices_mutex);
+
+       for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
+               if (enable[card_index] && !(devices_used & (1 << card_index)))
+                       break;
+       if (card_index >= SNDRV_CARDS) {
+               mutex_unlock(&devices_mutex);
+               return -ENOENT;
+       }
+       err = snd_card_create(index[card_index], id[card_index], THIS_MODULE,
+                             sizeof(*ua), &card);
+       if (err < 0) {
+               mutex_unlock(&devices_mutex);
+               return err;
+       }
+       card->private_free = ua101_card_free;
+       ua = card->private_data;
+       ua->dev = interface_to_usbdev(interface);
+       ua->card = card;
+       ua->card_index = card_index;
+       INIT_LIST_HEAD(&ua->midi_list);
+       spin_lock_init(&ua->lock);
+       mutex_init(&ua->mutex);
+       INIT_LIST_HEAD(&ua->ready_playback_urbs);
+       tasklet_init(&ua->playback_tasklet,
+                    playback_tasklet, (unsigned long)ua);
+       init_waitqueue_head(&ua->alsa_capture_wait);
+       init_waitqueue_head(&ua->rate_feedback_wait);
+       init_waitqueue_head(&ua->alsa_playback_wait);
+
+#ifdef UA1A_HACK
+       if (ua->dev->descriptor.idProduct == cpu_to_le16(0x0018)) {
+               ua->intf[2] = interface;
+               ua->intf[0] = usb_ifnum_to_if(ua->dev, 1);
+               ua->intf[1] = usb_ifnum_to_if(ua->dev, 2);
+               usb_driver_claim_interface(&ua101_driver, ua->intf[0], ua);
+               usb_driver_claim_interface(&ua101_driver, ua->intf[1], ua);
+       } else {
+#endif
+       ua->intf[0] = interface;
+       for (i = 1; i < ARRAY_SIZE(ua->intf); ++i) {
+               ua->intf[i] = usb_ifnum_to_if(ua->dev, i);
+               if (!ua->intf[i]) {
+                       dev_err(&ua->dev->dev, "interface %u not found\n", i);
+                       err = -ENXIO;
+                       goto probe_error;
+               }
+               err = usb_driver_claim_interface(&ua101_driver,
+                                                ua->intf[i], ua);
+               if (err < 0) {
+                       ua->intf[i] = NULL;
+                       err = -EBUSY;
+                       goto probe_error;
+               }
+       }
+#ifdef UA1A_HACK
+       }
+#endif
+
+       snd_card_set_dev(card, &interface->dev);
+
+#ifdef UA1A_HACK
+       if (ua->dev->descriptor.idProduct == cpu_to_le16(0x0018)) {
+               ua->format_bit = SNDRV_PCM_FMTBIT_S16_LE;
+               ua->rate = 44100;
+               ua->packets_per_second = 1000;
+               ua->capture.channels = 2;
+               ua->playback.channels = 2;
+               ua->capture.frame_bytes = 4;
+               ua->playback.frame_bytes = 4;
+               ua->capture.usb_pipe = usb_rcvisocpipe(ua->dev, 2);
+               ua->playback.usb_pipe = usb_sndisocpipe(ua->dev, 1);
+               ua->capture.max_packet_bytes = 192;
+               ua->playback.max_packet_bytes = 192;
+       } else {
+#endif
+       err = detect_usb_format(ua);
+       if (err < 0)
+               goto probe_error;
+#ifdef UA1A_HACK
+       }
+#endif
+
+       strcpy(card->driver, "UA-101");
+       strcpy(card->shortname, "UA-101");
+       usb_make_path(ua->dev, usb_path, sizeof(usb_path));
+       snprintf(ua->card->longname, sizeof(ua->card->longname),
+                "EDIROL UA-101 (serial %s), %u Hz at %s, %s speed",
+                ua->dev->serial ? ua->dev->serial : "?", ua->rate, usb_path,
+                ua->dev->speed == USB_SPEED_HIGH ? "high" : "full");
+
+       err = alloc_stream_buffers(ua, &ua->capture);
+       if (err < 0)
+               goto probe_error;
+       err = alloc_stream_buffers(ua, &ua->playback);
+       if (err < 0)
+               goto probe_error;
+
+       err = alloc_stream_urbs(ua, &ua->capture, capture_urb_complete);
+       if (err < 0)
+               goto probe_error;
+       err = alloc_stream_urbs(ua, &ua->playback, playback_urb_complete);
+       if (err < 0)
+               goto probe_error;
+
+       err = snd_pcm_new(card, "UA-101", 0, 1, 1, &ua->pcm);
+       if (err < 0)
+               goto probe_error;
+       ua->pcm->private_data = ua;
+       strcpy(ua->pcm->name, "UA-101");
+       snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_pcm_ops);
+       snd_pcm_set_ops(ua->pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_pcm_ops);
+
+#ifdef UA1A_HACK
+       if (ua->dev->descriptor.idProduct != cpu_to_le16(0x0018)) {
+#endif
+       err = snd_usbmidi_create(card, ua->intf[INTF_MIDI],
+                                &ua->midi_list, &midi_quirk);
+       if (err < 0)
+               goto probe_error;
+#ifdef UA1A_HACK
+       }
+#endif
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto probe_error;
+
+       usb_set_intfdata(interface, ua);
+       devices_used |= 1 << card_index;
+
+       mutex_unlock(&devices_mutex);
+       return 0;
+
+probe_error:
+       free_usb_related_resources(ua, interface);
+       snd_card_free(card);
+       mutex_unlock(&devices_mutex);
+       return err;
+}
+
+static void ua101_disconnect(struct usb_interface *interface)
+{
+       struct ua101 *ua = usb_get_intfdata(interface);
+       struct list_head *midi;
+
+       if (!ua)
+               return;
+
+       mutex_lock(&devices_mutex);
+
+       set_bit(DISCONNECTED, &ua->states);
+       wake_up(&ua->rate_feedback_wait);
+
+       /* make sure that userspace cannot create new requests */
+       snd_card_disconnect(ua->card);
+
+       /* make sure that there are no pending USB requests */
+       __list_for_each(midi, &ua->midi_list)
+               snd_usbmidi_disconnect(midi);
+       abort_alsa_playback(ua);
+       abort_alsa_capture(ua);
+       mutex_lock(&ua->mutex);
+       stop_usb_playback(ua);
+       stop_usb_capture(ua);
+       mutex_unlock(&ua->mutex);
+
+       free_usb_related_resources(ua, interface);
+
+       devices_used &= ~(1 << ua->card_index);
+
+       snd_card_free_when_closed(ua->card);
+
+       mutex_unlock(&devices_mutex);
+}
+
+static struct usb_device_id ua101_ids[] = {
+#ifdef UA1A_HACK
+       { USB_DEVICE(0x0582, 0x0018) },
+#endif
+       { USB_DEVICE(0x0582, 0x007d) },
+       { USB_DEVICE(0x0582, 0x008d) },
+       { }
+};
+MODULE_DEVICE_TABLE(usb, ua101_ids);
+
+static struct usb_driver ua101_driver = {
+       .name = "snd-ua101",
+       .id_table = ua101_ids,
+       .probe = ua101_probe,
+       .disconnect = ua101_disconnect,
+#if 0
+       .suspend = ua101_suspend,
+       .resume = ua101_resume,
+#endif
+};
+
+static int __init alsa_card_ua101_init(void)
+{
+       return usb_register(&ua101_driver);
+}
+
+static void __exit alsa_card_ua101_exit(void)
+{
+       usb_deregister(&ua101_driver);
+       mutex_destroy(&devices_mutex);
+}
+
+module_init(alsa_card_ua101_init);
+module_exit(alsa_card_ua101_exit);
index 9edef46..c6b9c8c 100644 (file)
@@ -44,7 +44,6 @@
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/usb.h>
-#include <linux/vmalloc.h>
 #include <linux/moduleparam.h>
 #include <linux/mutex.h>
 #include <sound/core.h>
@@ -170,11 +169,12 @@ struct snd_usb_substream {
        unsigned int curpacksize;       /* current packet size in bytes (for capture) */
        unsigned int curframesize;      /* current packet size in frames (for capture) */
        unsigned int fill_max: 1;       /* fill max packet size always */
+       unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
 
        unsigned int running: 1;        /* running status */
 
-       unsigned int hwptr_done;                        /* processed frame position in the buffer */
+       unsigned int hwptr_done;        /* processed byte position in the buffer */
        unsigned int transfer_done;             /* processed frames since last period update */
        unsigned long active_mask;      /* bitmask of active urbs */
        unsigned long unlink_mask;      /* bitmask of unlinked urbs */
@@ -343,7 +343,7 @@ static int retire_capture_urb(struct snd_usb_substream *subs,
        unsigned long flags;
        unsigned char *cp;
        int i;
-       unsigned int stride, len, oldptr;
+       unsigned int stride, frames, bytes, oldptr;
        int period_elapsed = 0;
 
        stride = runtime->frame_bits >> 3;
@@ -354,29 +354,39 @@ static int retire_capture_urb(struct snd_usb_substream *subs,
                        snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
                        // continue;
                }
-               len = urb->iso_frame_desc[i].actual_length / stride;
-               if (! len)
-                       continue;
+               bytes = urb->iso_frame_desc[i].actual_length;
+               frames = bytes / stride;
+               if (!subs->txfr_quirk)
+                       bytes = frames * stride;
+               if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       int oldbytes = bytes;
+#endif
+                       bytes = frames * stride;
+                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+                                                       oldbytes, bytes);
+               }
                /* update the current pointer */
                spin_lock_irqsave(&subs->lock, flags);
                oldptr = subs->hwptr_done;
-               subs->hwptr_done += len;
-               if (subs->hwptr_done >= runtime->buffer_size)
-                       subs->hwptr_done -= runtime->buffer_size;
-               subs->transfer_done += len;
+               subs->hwptr_done += bytes;
+               if (subs->hwptr_done >= runtime->buffer_size * stride)
+                       subs->hwptr_done -= runtime->buffer_size * stride;
+               frames = (bytes + (oldptr % stride)) / stride;
+               subs->transfer_done += frames;
                if (subs->transfer_done >= runtime->period_size) {
                        subs->transfer_done -= runtime->period_size;
                        period_elapsed = 1;
                }
                spin_unlock_irqrestore(&subs->lock, flags);
                /* copy a data chunk */
-               if (oldptr + len > runtime->buffer_size) {
-                       unsigned int cnt = runtime->buffer_size - oldptr;
-                       unsigned int blen = cnt * stride;
-                       memcpy(runtime->dma_area + oldptr * stride, cp, blen);
-                       memcpy(runtime->dma_area, cp + blen, len * stride - blen);
+               if (oldptr + bytes > runtime->buffer_size * stride) {
+                       unsigned int bytes1 =
+                                       runtime->buffer_size * stride - oldptr;
+                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
+                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
                } else {
-                       memcpy(runtime->dma_area + oldptr * stride, cp, len * stride);
+                       memcpy(runtime->dma_area + oldptr, cp, bytes);
                }
        }
        if (period_elapsed)
@@ -563,24 +573,24 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
                                struct snd_pcm_runtime *runtime,
                                struct urb *urb)
 {
-       int i, stride, offs;
-       unsigned int counts;
+       int i, stride;
+       unsigned int counts, frames, bytes;
        unsigned long flags;
        int period_elapsed = 0;
        struct snd_urb_ctx *ctx = urb->context;
 
        stride = runtime->frame_bits >> 3;
 
-       offs = 0;
+       frames = 0;
        urb->dev = ctx->subs->dev; /* we need to set this at each time */
        urb->number_of_packets = 0;
        spin_lock_irqsave(&subs->lock, flags);
        for (i = 0; i < ctx->packets; i++) {
                counts = snd_usb_audio_next_packet_size(subs);
                /* set up descriptor */
-               urb->iso_frame_desc[i].offset = offs * stride;
+               urb->iso_frame_desc[i].offset = frames * stride;
                urb->iso_frame_desc[i].length = counts * stride;
-               offs += counts;
+               frames += counts;
                urb->number_of_packets++;
                subs->transfer_done += counts;
                if (subs->transfer_done >= runtime->period_size) {
@@ -590,7 +600,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
                                if (subs->transfer_done > 0) {
                                        /* FIXME: fill-max mode is not
                                         * supported yet */
-                                       offs -= subs->transfer_done;
+                                       frames -= subs->transfer_done;
                                        counts -= subs->transfer_done;
                                        urb->iso_frame_desc[i].length =
                                                counts * stride;
@@ -600,7 +610,7 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
                                if (i < ctx->packets) {
                                        /* add a transfer delimiter */
                                        urb->iso_frame_desc[i].offset =
-                                               offs * stride;
+                                               frames * stride;
                                        urb->iso_frame_desc[i].length = 0;
                                        urb->number_of_packets++;
                                }
@@ -610,26 +620,25 @@ static int prepare_playback_urb(struct snd_usb_substream *subs,
                if (period_elapsed) /* finish at the period boundary */
                        break;
        }
-       if (subs->hwptr_done + offs > runtime->buffer_size) {
+       bytes = frames * stride;
+       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
                /* err, the transferred area goes over buffer boundary. */
-               unsigned int len = runtime->buffer_size - subs->hwptr_done;
+               unsigned int bytes1 =
+                       runtime->buffer_size * stride - subs->hwptr_done;
                memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done * stride,
-                      len * stride);
-               memcpy(urb->transfer_buffer + len * stride,
-                      runtime->dma_area,
-                      (offs - len) * stride);
+                      runtime->dma_area + subs->hwptr_done, bytes1);
+               memcpy(urb->transfer_buffer + bytes1,
+                      runtime->dma_area, bytes - bytes1);
        } else {
                memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done * stride,
-                      offs * stride);
+                      runtime->dma_area + subs->hwptr_done, bytes);
        }
-       subs->hwptr_done += offs;
-       if (subs->hwptr_done >= runtime->buffer_size)
-               subs->hwptr_done -= runtime->buffer_size;
-       runtime->delay += offs;
+       subs->hwptr_done += bytes;
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+       runtime->delay += frames;
        spin_unlock_irqrestore(&subs->lock, flags);
-       urb->transfer_buffer_length = offs * stride;
+       urb->transfer_buffer_length = bytes;
        if (period_elapsed)
                snd_pcm_period_elapsed(subs->pcm_substream);
        return 0;
@@ -735,41 +744,6 @@ static void snd_complete_sync_urb(struct urb *urb)
 }
 
 
-/* get the physical page pointer at the given offset */
-static struct page *snd_pcm_get_vmalloc_page(struct snd_pcm_substream *subs,
-                                            unsigned long offset)
-{
-       void *pageptr = subs->runtime->dma_area + offset;
-       return vmalloc_to_page(pageptr);
-}
-
-/* allocate virtual buffer; may be called more than once */
-static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, size_t size)
-{
-       struct snd_pcm_runtime *runtime = subs->runtime;
-       if (runtime->dma_area) {
-               if (runtime->dma_bytes >= size)
-                       return 0; /* already large enough */
-               vfree(runtime->dma_area);
-       }
-       runtime->dma_area = vmalloc_user(size);
-       if (!runtime->dma_area)
-               return -ENOMEM;
-       runtime->dma_bytes = size;
-       return 0;
-}
-
-/* free virtual buffer; may be called more than once */
-static int snd_pcm_free_vmalloc_buffer(struct snd_pcm_substream *subs)
-{
-       struct snd_pcm_runtime *runtime = subs->runtime;
-
-       vfree(runtime->dma_area);
-       runtime->dma_area = NULL;
-       return 0;
-}
-
-
 /*
  * unlink active urbs.
  */
@@ -937,18 +911,18 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
 
 
 /*
- * return the current pcm pointer.  just return the hwptr_done value.
+ * return the current pcm pointer.  just based on the hwptr_done value.
  */
 static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_usb_substream *subs;
-       snd_pcm_uframes_t hwptr_done;
+       unsigned int hwptr_done;
        
        subs = (struct snd_usb_substream *)substream->runtime->private_data;
        spin_lock(&subs->lock);
        hwptr_done = subs->hwptr_done;
        spin_unlock(&subs->lock);
-       return hwptr_done;
+       return hwptr_done / (substream->runtime->frame_bits >> 3);
 }
 
 
@@ -1306,6 +1280,47 @@ static int init_usb_sample_rate(struct usb_device *dev, int iface,
        return 0;
 }
 
+/*
+ * For E-Mu 0404USB/0202USB/TrackerPre sample rate should be set for device,
+ * not for interface.
+ */
+static void set_format_emu_quirk(struct snd_usb_substream *subs,
+                                struct audioformat *fmt)
+{
+       unsigned char emu_samplerate_id = 0;
+
+       /* When capture is active
+        * sample rate shouldn't be changed
+        * by playback substream
+        */
+       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (subs->stream->substream[SNDRV_PCM_STREAM_CAPTURE].interface != -1)
+                       return;
+       }
+
+       switch (fmt->rate_min) {
+       case 48000:
+               emu_samplerate_id = EMU_QUIRK_SR_48000HZ;
+               break;
+       case 88200:
+               emu_samplerate_id = EMU_QUIRK_SR_88200HZ;
+               break;
+       case 96000:
+               emu_samplerate_id = EMU_QUIRK_SR_96000HZ;
+               break;
+       case 176400:
+               emu_samplerate_id = EMU_QUIRK_SR_176400HZ;
+               break;
+       case 192000:
+               emu_samplerate_id = EMU_QUIRK_SR_192000HZ;
+               break;
+       default:
+               emu_samplerate_id = EMU_QUIRK_SR_44100HZ;
+               break;
+       }
+       snd_emuusb_set_samplerate(subs->stream->chip, emu_samplerate_id);
+}
+
 /*
  * find a matching format and set up the interface
  */
@@ -1419,6 +1434,14 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
 
        subs->cur_audiofmt = fmt;
 
+       switch (subs->stream->chip->usb_id) {
+       case USB_ID(0x041e, 0x3f02): /* E-Mu 0202 USB */
+       case USB_ID(0x041e, 0x3f04): /* E-Mu 0404 USB */
+       case USB_ID(0x041e, 0x3f0a): /* E-Mu Tracker Pre */
+               set_format_emu_quirk(subs, fmt);
+               break;
+       }
+
 #if 0
        printk(KERN_DEBUG
               "setting done: format = %d, rate = %d..%d, channels = %d\n",
@@ -1449,8 +1472,8 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
        unsigned int channels, rate, format;
        int ret, changed;
 
-       ret = snd_pcm_alloc_vmalloc_buffer(substream,
-                                          params_buffer_bytes(hw_params));
+       ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
        if (ret < 0)
                return ret;
 
@@ -1507,7 +1530,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
        subs->period_bytes = 0;
        if (!subs->stream->chip->shutdown)
                release_substream_urbs(subs, 0);
-       return snd_pcm_free_vmalloc_buffer(substream);
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
 
 /*
@@ -1973,7 +1996,8 @@ static struct snd_pcm_ops snd_usb_playback_ops = {
        .prepare =      snd_usb_pcm_prepare,
        .trigger =      snd_usb_pcm_playback_trigger,
        .pointer =      snd_usb_pcm_pointer,
-       .page =         snd_pcm_get_vmalloc_page,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
 };
 
 static struct snd_pcm_ops snd_usb_capture_ops = {
@@ -1985,7 +2009,8 @@ static struct snd_pcm_ops snd_usb_capture_ops = {
        .prepare =      snd_usb_pcm_prepare,
        .trigger =      snd_usb_pcm_capture_trigger,
        .pointer =      snd_usb_pcm_pointer,
-       .page =         snd_pcm_get_vmalloc_page,
+       .page =         snd_pcm_lib_get_vmalloc_page,
+       .mmap =         snd_pcm_lib_mmap_vmalloc,
 };
 
 
@@ -2227,6 +2252,7 @@ static void init_substream(struct snd_usb_stream *as, int stream, struct audiofo
        subs->stream = as;
        subs->direction = stream;
        subs->dev = as->chip->dev;
+       subs->txfr_quirk = as->chip->txfr_quirk;
        if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL) {
                subs->ops = audio_urb_ops[stream];
        } else {
@@ -3142,59 +3168,6 @@ static int create_ua1000_quirk(struct snd_usb_audio *chip,
        return 0;
 }
 
-/*
- * Create a stream for an Edirol UA-101 interface.
- * Copy, paste and modify from Edirol UA-1000
- */
-static int create_ua101_quirk(struct snd_usb_audio *chip,
-                              struct usb_interface *iface,
-                              const struct snd_usb_audio_quirk *quirk)
-{
-       static const struct audioformat ua101_format = {
-               .format = SNDRV_PCM_FORMAT_S32_LE,
-               .fmt_type = USB_FORMAT_TYPE_I,
-               .altsetting = 1,
-               .altset_idx = 1,
-               .attributes = 0,
-               .rates = SNDRV_PCM_RATE_CONTINUOUS,
-       };
-       struct usb_host_interface *alts;
-       struct usb_interface_descriptor *altsd;
-       struct audioformat *fp;
-       int stream, err;
-
-       if (iface->num_altsetting != 2)
-               return -ENXIO;
-       alts = &iface->altsetting[1];
-       altsd = get_iface_desc(alts);
-       if (alts->extralen != 18 || alts->extra[1] != USB_DT_CS_INTERFACE ||
-           altsd->bNumEndpoints != 1)
-               return -ENXIO;
-
-       fp = kmemdup(&ua101_format, sizeof(*fp), GFP_KERNEL);
-       if (!fp)
-               return -ENOMEM;
-
-       fp->channels = alts->extra[11];
-       fp->iface = altsd->bInterfaceNumber;
-       fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
-       fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
-       fp->datainterval = parse_datainterval(chip, alts);
-       fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
-       fp->rate_max = fp->rate_min = combine_triple(&alts->extra[15]);
-
-       stream = (fp->endpoint & USB_DIR_IN)
-               ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
-       err = add_audio_endpoint(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               return err;
-       }
-       /* FIXME: playback must be synchronized to capture */
-       usb_set_interface(chip->dev, fp->iface, 0);
-       return 0;
-}
-
 static int snd_usb_create_quirk(struct snd_usb_audio *chip,
                                struct usb_interface *iface,
                                const struct snd_usb_audio_quirk *quirk);
@@ -3232,6 +3205,18 @@ static int ignore_interface_quirk(struct snd_usb_audio *chip,
        return 0;
 }
 
+/*
+ * Allow alignment on audio sub-slot (channel samples) rather than
+ * on audio slots (audio frames)
+ */
+static int create_align_transfer_quirk(struct snd_usb_audio *chip,
+                                 struct usb_interface *iface,
+                                 const struct snd_usb_audio_quirk *quirk)
+{
+       chip->txfr_quirk = 1;
+       return 1;       /* Continue with creating streams and mixer */
+}
+
 
 /*
  * boot quirks
@@ -3326,6 +3311,32 @@ static int snd_usb_cm6206_boot_quirk(struct usb_device *dev)
        return err;
 }
 
+/*
+ * This call will put the synth in "USB send" mode, i.e it will send MIDI
+ * messages through USB (this is disabled at startup). The synth will
+ * acknowledge by sending a sysex on endpoint 0x85 and by displaying a USB
+ * sign on its LCD. Values here are chosen based on sniffing USB traffic
+ * under Windows.
+ */
+static int snd_usb_accessmusic_boot_quirk(struct usb_device *dev)
+{
+       int err, actual_length;
+
+       /* "midi send" enable */
+       static const u8 seq[] = { 0x4e, 0x73, 0x52, 0x01 };
+
+       void *buf = kmemdup(seq, ARRAY_SIZE(seq), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+       err = usb_interrupt_msg(dev, usb_sndintpipe(dev, 0x05), buf,
+                       ARRAY_SIZE(seq), &actual_length, 1000);
+       kfree(buf);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 /*
  * Setup quirks
  */
@@ -3406,8 +3417,8 @@ static int snd_usb_create_quirk(struct snd_usb_audio *chip,
                [QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
                [QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
                [QUIRK_AUDIO_EDIROL_UA1000] = create_ua1000_quirk,
-               [QUIRK_AUDIO_EDIROL_UA101] = create_ua101_quirk,
-               [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk
+               [QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
+               [QUIRK_AUDIO_ALIGN_TRANSFER] = create_align_transfer_quirk
        };
 
        if (quirk->type < QUIRK_TYPE_COUNT) {
@@ -3624,6 +3635,12 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                        goto __err_val;
        }
 
+       /* Access Music VirusTI Desktop */
+       if (id == USB_ID(0x133e, 0x0815)) {
+               if (snd_usb_accessmusic_boot_quirk(dev) < 0)
+                       goto __err_val;
+       }
+
        /*
         * found a config.  now register to ALSA
         */
@@ -3661,6 +3678,7 @@ static void *snd_usb_audio_probe(struct usb_device *dev,
                }
        }
 
+       chip->txfr_quirk = 0;
        err = 1; /* continue */
        if (quirk && quirk->ifnum != QUIRK_NO_INTERFACE) {
                /* need some special handlings */
index 40ba811..9d8cea4 100644 (file)
@@ -125,6 +125,7 @@ struct snd_usb_audio {
        struct snd_card *card;
        u32 usb_id;
        int shutdown;
+       unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
        int num_interfaces;
        int num_suspended_intf;
 
@@ -159,8 +160,8 @@ enum quirk_type {
        QUIRK_AUDIO_STANDARD_INTERFACE,
        QUIRK_AUDIO_FIXED_ENDPOINT,
        QUIRK_AUDIO_EDIROL_UA1000,
-       QUIRK_AUDIO_EDIROL_UA101,
        QUIRK_AUDIO_EDIROL_UAXX,
+       QUIRK_AUDIO_ALIGN_TRANSFER,
 
        QUIRK_TYPE_COUNT
 };
@@ -209,6 +210,16 @@ struct snd_usb_midi_endpoint_info {
 /*
  */
 
+/*E-mu USB samplerate control quirk*/
+enum {
+       EMU_QUIRK_SR_44100HZ = 0,
+       EMU_QUIRK_SR_48000HZ,
+       EMU_QUIRK_SR_88200HZ,
+       EMU_QUIRK_SR_96000HZ,
+       EMU_QUIRK_SR_176400HZ,
+       EMU_QUIRK_SR_192000HZ
+};
+
 #define combine_word(s)    ((*(s)) | ((unsigned int)(s)[1] << 8))
 #define combine_triple(s)  (combine_word(s) | ((unsigned int)(s)[2] << 16))
 #define combine_quad(s)    (combine_triple(s) | ((unsigned int)(s)[3] << 24))
@@ -234,6 +245,9 @@ void snd_usbmidi_input_stop(struct list_head* p);
 void snd_usbmidi_input_start(struct list_head* p);
 void snd_usbmidi_disconnect(struct list_head *p);
 
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+                       unsigned char samplerate_id);
+
 /*
  * retrieve usb_interface descriptor from the host interface
  * (conditional for compatibility with the older API)
index aae50df..b2da478 100644 (file)
@@ -1419,6 +1419,12 @@ static struct port_info {
        EXTERNAL_PORT(0x086a, 0x0001, 8, "%s Broadcast"),
        EXTERNAL_PORT(0x086a, 0x0002, 8, "%s Broadcast"),
        EXTERNAL_PORT(0x086a, 0x0003, 4, "%s Broadcast"),
+       /* Access Music Virus TI */
+       EXTERNAL_PORT(0x133e, 0x0815, 0, "%s MIDI"),
+       PORT_INFO(0x133e, 0x0815, 1, "%s Synth", 0,
+               SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC |
+               SNDRV_SEQ_PORT_TYPE_HARDWARE |
+               SNDRV_SEQ_PORT_TYPE_SYNTHESIZER),
 };
 
 static struct port_info *find_port_info(struct snd_usb_midi* umidi, int number)
index c998220..35b4830 100644 (file)
@@ -69,13 +69,16 @@ static const struct rc_config {
        { USB_ID(0x041e, 0x3048), 2, 2, 6, 6,  2,  0x6e91 }, /* Toshiba SB0500 */
 };
 
+#define MAX_ID_ELEMS   256
+
 struct usb_mixer_interface {
        struct snd_usb_audio *chip;
        unsigned int ctrlif;
        struct list_head list;
        unsigned int ignore_ctl_error;
        struct urb *urb;
-       struct usb_mixer_elem_info **id_elems; /* array[256], indexed by unit id */
+       /* array[MAX_ID_ELEMS], indexed by unit id */
+       struct usb_mixer_elem_info **id_elems;
 
        /* Sound Blaster remote control stuff */
        const struct rc_config *rc_cfg;
@@ -105,7 +108,7 @@ struct mixer_build {
        struct usb_mixer_interface *mixer;
        unsigned char *buffer;
        unsigned int buflen;
-       DECLARE_BITMAP(unitbitmap, 256);
+       DECLARE_BITMAP(unitbitmap, MAX_ID_ELEMS);
        struct usb_audio_term oterm;
        const struct usbmix_name_map *map;
        const struct usbmix_selector_map *selector_map;
@@ -123,6 +126,7 @@ struct usb_mixer_elem_info {
        int channels;
        int val_type;
        int min, max, res;
+       int dBmin, dBmax;
        int cached;
        int cache_val[MAX_CHANNELS];
        u8 initialized;
@@ -186,6 +190,21 @@ enum {
        USB_PROC_DCR_RELEASE = 6,
 };
 
+/*E-mu 0202(0404) eXtension Unit(XU) control*/
+enum {
+       USB_XU_CLOCK_RATE               = 0xe301,
+       USB_XU_CLOCK_SOURCE             = 0xe302,
+       USB_XU_DIGITAL_IO_STATUS        = 0xe303,
+       USB_XU_DEVICE_OPTIONS           = 0xe304,
+       USB_XU_DIRECT_MONITORING        = 0xe305,
+       USB_XU_METERING                 = 0xe306
+};
+enum {
+       USB_XU_CLOCK_SOURCE_SELECTOR = 0x02,    /* clock source*/
+       USB_XU_CLOCK_RATE_SELECTOR = 0x03,      /* clock rate */
+       USB_XU_DIGITAL_FORMAT_SELECTOR = 0x01,  /* the spdif format */
+       USB_XU_SOFT_LIMIT_SELECTOR = 0x03       /* soft limiter */
+};
 
 /*
  * manual mapping of mixer names
@@ -194,42 +213,50 @@ enum {
  */
 #include "usbmixer_maps.c"
 
-/* get the mapped name if the unit matches */
-static int check_mapped_name(struct mixer_build *state, int unitid, int control, char *buf, int buflen)
+static const struct usbmix_name_map *
+find_map(struct mixer_build *state, int unitid, int control)
 {
-       const struct usbmix_name_map *p;
+       const struct usbmix_name_map *p = state->map;
 
-       if (! state->map)
-               return 0;
+       if (!p)
+               return NULL;
 
        for (p = state->map; p->id; p++) {
-               if (p->id == unitid && p->name &&
-                   (! control || ! p->control || control == p->control)) {
-                       buflen--;
-                       return strlcpy(buf, p->name, buflen);
-               }
+               if (p->id == unitid &&
+                   (!control || !p->control || control == p->control))
+                       return p;
        }
-       return 0;
+       return NULL;
 }
 
-/* check whether the control should be ignored */
-static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
+/* get the mapped name if the unit matches */
+static int
+check_mapped_name(const struct usbmix_name_map *p, char *buf, int buflen)
 {
-       const struct usbmix_name_map *p;
+       if (!p || !p->name)
+               return 0;
+
+       buflen--;
+       return strlcpy(buf, p->name, buflen);
+}
 
-       if (! state->map)
+/* check whether the control should be ignored */
+static inline int
+check_ignored_ctl(const struct usbmix_name_map *p)
+{
+       if (!p || p->name || p->dB)
                return 0;
-       for (p = state->map; p->id; p++) {
-               if (p->id == unitid && ! p->name &&
-                   (! control || ! p->control || control == p->control)) {
-                       /*
-                       printk(KERN_DEBUG "ignored control %d:%d\n",
-                              unitid, control);
-                       */
-                       return 1;
-               }
+       return 1;
+}
+
+/* dB mapping */
+static inline void check_mapped_dB(const struct usbmix_name_map *p,
+                                  struct usb_mixer_elem_info *cval)
+{
+       if (p && p->dB) {
+               cval->dBmin = p->dB->min;
+               cval->dBmax = p->dB->max;
        }
-       return 0;
 }
 
 /* get the mapped selector source name */
@@ -466,20 +493,8 @@ static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
 
        if (size < sizeof(scale))
                return -ENOMEM;
-       /* USB descriptions contain the dB scale in 1/256 dB unit
-        * while ALSA TLV contains in 1/100 dB unit
-        */
-       scale[2] = (convert_signed_value(cval, cval->min) * 100) / 256;
-       scale[3] = (convert_signed_value(cval, cval->max) * 100) / 256;
-       if (scale[3] <= scale[2]) {
-               /* something is wrong; assume it's either from/to 0dB */
-               if (scale[2] < 0)
-                       scale[3] = 0;
-               else if (scale[2] > 0)
-                       scale[2] = 0;
-               else /* totally crap, return an error */
-                       return -EINVAL;
-       }
+       scale[2] = cval->dBmin;
+       scale[3] = cval->dBmax;
        if (copy_to_user(_tlv, scale, sizeof(scale)))
                return -EFAULT;
        return 0;
@@ -720,6 +735,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
        cval->min = default_min;
        cval->max = cval->min + 1;
        cval->res = 1;
+       cval->dBmin = cval->dBmax = 0;
 
        if (cval->val_type == USB_MIXER_BOOLEAN ||
            cval->val_type == USB_MIXER_INV_BOOLEAN) {
@@ -787,6 +803,24 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
 
                cval->initialized = 1;
        }
+
+       /* USB descriptions contain the dB scale in 1/256 dB unit
+        * while ALSA TLV contains in 1/100 dB unit
+        */
+       cval->dBmin = (convert_signed_value(cval, cval->min) * 100) / 256;
+       cval->dBmax = (convert_signed_value(cval, cval->max) * 100) / 256;
+       if (cval->dBmin > cval->dBmax) {
+               /* something is wrong; assume it's either from/to 0dB */
+               if (cval->dBmin < 0)
+                       cval->dBmax = 0;
+               else if (cval->dBmin > 0)
+                       cval->dBmin = 0;
+               if (cval->dBmin > cval->dBmax) {
+                       /* totally crap, return an error */
+                       return -EINVAL;
+               }
+       }
+
        return 0;
 }
 
@@ -912,6 +946,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
        int nameid = desc[desc[0] - 1];
        struct snd_kcontrol *kctl;
        struct usb_mixer_elem_info *cval;
+       const struct usbmix_name_map *map;
 
        control++; /* change from zero-based to 1-based value */
 
@@ -920,7 +955,8 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
                return;
        }
 
-       if (check_ignored_ctl(state, unitid, control))
+       map = find_map(state, unitid, control);
+       if (check_ignored_ctl(map))
                return;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -954,10 +990,11 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
        }
        kctl->private_free = usb_mixer_elem_free;
 
-       len = check_mapped_name(state, unitid, control, kctl->id.name, sizeof(kctl->id.name));
+       len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        mapped_name = len != 0;
        if (! len && nameid)
-               len = snd_usb_copy_string_desc(state, nameid, kctl->id.name, sizeof(kctl->id.name));
+               len = snd_usb_copy_string_desc(state, nameid,
+                               kctl->id.name, sizeof(kctl->id.name));
 
        switch (control) {
        case USB_FEATURE_MUTE:
@@ -995,6 +1032,7 @@ static void build_feature_ctl(struct mixer_build *state, unsigned char *desc,
                        kctl->vd[0].access |= 
                                SNDRV_CTL_ELEM_ACCESS_TLV_READ |
                                SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+                       check_mapped_dB(map, cval);
                }
                break;
 
@@ -1122,8 +1160,10 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
        unsigned int num_outs = desc[5 + input_pins];
        unsigned int i, len;
        struct snd_kcontrol *kctl;
+       const struct usbmix_name_map *map;
 
-       if (check_ignored_ctl(state, unitid, 0))
+       map = find_map(state, unitid, 0);
+       if (check_ignored_ctl(map))
                return;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -1152,7 +1192,7 @@ static void build_mixer_unit_ctl(struct mixer_build *state, unsigned char *desc,
        }
        kctl->private_free = usb_mixer_elem_free;
 
-       len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
+       len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        if (! len)
                len = get_term_name(state, iterm, kctl->id.name, sizeof(kctl->id.name), 0);
        if (! len)
@@ -1330,7 +1370,32 @@ static struct procunit_info procunits[] = {
        { USB_PROC_DCR, "DCR", dcr_proc_info },
        { 0 },
 };
-
+/*
+ * predefined data for extension units
+ */
+static struct procunit_value_info clock_rate_xu_info[] = {
+       { USB_XU_CLOCK_RATE_SELECTOR, "Selector", USB_MIXER_U8, 0 },
+       { 0 }
+};
+static struct procunit_value_info clock_source_xu_info[] = {
+       { USB_XU_CLOCK_SOURCE_SELECTOR, "External", USB_MIXER_BOOLEAN },
+       { 0 }
+};
+static struct procunit_value_info spdif_format_xu_info[] = {
+       { USB_XU_DIGITAL_FORMAT_SELECTOR, "SPDIF/AC3", USB_MIXER_BOOLEAN },
+       { 0 }
+};
+static struct procunit_value_info soft_limit_xu_info[] = {
+       { USB_XU_SOFT_LIMIT_SELECTOR, " ", USB_MIXER_BOOLEAN },
+       { 0 }
+};
+static struct procunit_info extunits[] = {
+       { USB_XU_CLOCK_RATE, "Clock rate", clock_rate_xu_info },
+       { USB_XU_CLOCK_SOURCE, "DigitalIn CLK source", clock_source_xu_info },
+       { USB_XU_DIGITAL_IO_STATUS, "DigitalOut format:", spdif_format_xu_info },
+       { USB_XU_DEVICE_OPTIONS, "AnalogueIn Soft Limit", soft_limit_xu_info },
+       { 0 }
+};
 /*
  * build a processing/extension unit
  */
@@ -1342,6 +1407,7 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
        int i, err, nameid, type, len;
        struct procunit_info *info;
        struct procunit_value_info *valinfo;
+       const struct usbmix_name_map *map;
        static struct procunit_value_info default_value_info[] = {
                { 0x01, "Switch", USB_MIXER_BOOLEAN },
                { 0 }
@@ -1371,7 +1437,8 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                /* FIXME: bitmap might be longer than 8bit */
                if (! (dsc[12 + num_ins] & (1 << (valinfo->control - 1))))
                        continue;
-               if (check_ignored_ctl(state, unitid, valinfo->control))
+               map = find_map(state, unitid, valinfo->control);
+               if (check_ignored_ctl(map))
                        continue;
                cval = kzalloc(sizeof(*cval), GFP_KERNEL);
                if (! cval) {
@@ -1391,8 +1458,18 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                        cval->max = dsc[15];
                        cval->res = 1;
                        cval->initialized = 1;
-               } else
-                       get_min_max(cval, valinfo->min_value);
+               } else {
+                       if (type == USB_XU_CLOCK_RATE) {
+                               /* E-Mu USB 0404/0202/TrackerPre
+                                * samplerate control quirk
+                                */
+                               cval->min = 0;
+                               cval->max = 5;
+                               cval->res = 1;
+                               cval->initialized = 1;
+                       } else
+                               get_min_max(cval, valinfo->min_value);
+               }
 
                kctl = snd_ctl_new1(&mixer_procunit_ctl, cval);
                if (! kctl) {
@@ -1402,8 +1479,9 @@ static int build_audio_procunit(struct mixer_build *state, int unitid, unsigned
                }
                kctl->private_free = usb_mixer_elem_free;
 
-               if (check_mapped_name(state, unitid, cval->control, kctl->id.name, sizeof(kctl->id.name)))
-                       ;
+               if (check_mapped_name(map, kctl->id.name,
+                                               sizeof(kctl->id.name)))
+                       /* nothing */ ;
                else if (info->name)
                        strlcpy(kctl->id.name, info->name, sizeof(kctl->id.name));
                else {
@@ -1433,7 +1511,7 @@ static int parse_audio_processing_unit(struct mixer_build *state, int unitid, un
 
 static int parse_audio_extension_unit(struct mixer_build *state, int unitid, unsigned char *desc)
 {
-       return build_audio_procunit(state, unitid, desc, NULL, "Extension Unit");
+       return build_audio_procunit(state, unitid, desc, extunits, "Extension Unit");
 }
 
 
@@ -1542,6 +1620,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        int err;
        struct usb_mixer_elem_info *cval;
        struct snd_kcontrol *kctl;
+       const struct usbmix_name_map *map;
        char **namelist;
 
        if (! num_ins || desc[0] < 5 + num_ins) {
@@ -1557,7 +1636,8 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        if (num_ins == 1) /* only one ? nonsense! */
                return 0;
 
-       if (check_ignored_ctl(state, unitid, 0))
+       map = find_map(state, unitid, 0);
+       if (check_ignored_ctl(map))
                return 0;
 
        cval = kzalloc(sizeof(*cval), GFP_KERNEL);
@@ -1612,7 +1692,7 @@ static int parse_audio_selector_unit(struct mixer_build *state, int unitid, unsi
        kctl->private_free = usb_mixer_selector_elem_free;
 
        nameid = desc[desc[0] - 1];
-       len = check_mapped_name(state, unitid, 0, kctl->id.name, sizeof(kctl->id.name));
+       len = check_mapped_name(map, kctl->id.name, sizeof(kctl->id.name));
        if (len)
                ;
        else if (nameid)
@@ -1748,6 +1828,46 @@ static void snd_usb_mixer_notify_id(struct usb_mixer_interface *mixer,
                               info->elem_id);
 }
 
+static void snd_usb_mixer_dump_cval(struct snd_info_buffer *buffer,
+                                   int unitid,
+                                   struct usb_mixer_elem_info *cval)
+{
+       static char *val_types[] = {"BOOLEAN", "INV_BOOLEAN",
+                                   "S8", "U8", "S16", "U16"};
+       snd_iprintf(buffer, "  Unit: %i\n", unitid);
+       if (cval->elem_id)
+               snd_iprintf(buffer, "    Control: name=\"%s\", index=%i\n",
+                               cval->elem_id->name, cval->elem_id->index);
+       snd_iprintf(buffer, "    Info: id=%i, control=%i, cmask=0x%x, "
+                           "channels=%i, type=\"%s\"\n", cval->id,
+                           cval->control, cval->cmask, cval->channels,
+                           val_types[cval->val_type]);
+       snd_iprintf(buffer, "    Volume: min=%i, max=%i, dBmin=%i, dBmax=%i\n",
+                           cval->min, cval->max, cval->dBmin, cval->dBmax);
+}
+
+static void snd_usb_mixer_proc_read(struct snd_info_entry *entry,
+                                   struct snd_info_buffer *buffer)
+{
+       struct snd_usb_audio *chip = entry->private_data;
+       struct usb_mixer_interface *mixer;
+       struct usb_mixer_elem_info *cval;
+       int unitid;
+
+       list_for_each_entry(mixer, &chip->mixer_list, list) {
+               snd_iprintf(buffer,
+                       "USB Mixer: usb_id=0x%08x, ctrlif=%i, ctlerr=%i\n",
+                               chip->usb_id, mixer->ctrlif,
+                               mixer->ignore_ctl_error);
+               snd_iprintf(buffer, "Card: %s\n", chip->card->longname);
+               for (unitid = 0; unitid < MAX_ID_ELEMS; unitid++) {
+                       for (cval = mixer->id_elems[unitid]; cval;
+                                               cval = cval->next_id_elem)
+                               snd_usb_mixer_dump_cval(buffer, unitid, cval);
+               }
+       }
+}
+
 static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
                                        int unitid)
 {
@@ -2109,6 +2229,24 @@ static int snd_xonar_u1_controls_create(struct usb_mixer_interface *mixer)
        return 0;
 }
 
+void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
+                              unsigned char samplerate_id)
+{
+       struct usb_mixer_interface *mixer;
+       struct usb_mixer_elem_info *cval;
+       int unitid = 12; /* SamleRate ExtensionUnit ID */
+
+       list_for_each_entry(mixer, &chip->mixer_list, list) {
+               cval = mixer->id_elems[unitid];
+               if (cval) {
+                       set_cur_ctl_value(cval, cval->control << 8,
+                                         samplerate_id);
+                       snd_usb_mixer_notify_id(mixer, unitid);
+               }
+               break;
+       }
+}
+
 int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
                         int ignore_error)
 {
@@ -2116,6 +2254,7 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
                .dev_free = snd_usb_mixer_dev_free
        };
        struct usb_mixer_interface *mixer;
+       struct snd_info_entry *entry;
        int err;
 
        strcpy(chip->card->mixername, "USB Mixer");
@@ -2126,7 +2265,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        mixer->chip = chip;
        mixer->ctrlif = ctrlif;
        mixer->ignore_ctl_error = ignore_error;
-       mixer->id_elems = kcalloc(256, sizeof(*mixer->id_elems), GFP_KERNEL);
+       mixer->id_elems = kcalloc(MAX_ID_ELEMS, sizeof(*mixer->id_elems),
+                                 GFP_KERNEL);
        if (!mixer->id_elems) {
                kfree(mixer);
                return -ENOMEM;
@@ -2142,8 +2282,6 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        if (mixer->chip->usb_id == USB_ID(0x041e, 0x3020) ||
            mixer->chip->usb_id == USB_ID(0x041e, 0x3040) ||
            mixer->chip->usb_id == USB_ID(0x041e, 0x3048)) {
-               struct snd_info_entry *entry;
-
                if ((err = snd_audigy2nx_controls_create(mixer)) < 0)
                        goto _error;
                if (!snd_card_proc_new(chip->card, "audigy2nx", &entry))
@@ -2161,6 +2299,11 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
        err = snd_device_new(chip->card, SNDRV_DEV_LOWLEVEL, mixer, &dev_ops);
        if (err < 0)
                goto _error;
+
+       if (list_empty(&chip->mixer_list) &&
+           !snd_card_proc_new(chip->card, "usbmixer", &entry))
+               snd_info_set_text_ops(entry, chip, snd_usb_mixer_proc_read);
+
        list_add(&mixer->list, &chip->mixer_list);
        return 0;
 
index 77c3588..79e903a 100644 (file)
  *
  */
 
+struct usbmix_dB_map {
+       u32 min;
+       u32 max;
+};
 
 struct usbmix_name_map {
        int id;
        const char *name;
        int control;
+       struct usbmix_dB_map *dB;
 };
 
 struct usbmix_selector_map {
@@ -72,7 +77,7 @@ static struct usbmix_name_map extigy_map[] = {
        { 8, "Line Playback" }, /* FU */
        /* 9: IT mic */
        { 10, "Mic Playback" }, /* FU */
-       { 11, "Capture Input Source" }, /* SU */
+       { 11, "Capture Source" }, /* SU */
        { 12, "Capture" }, /* FU */
        /* 13: OT pcm capture */
        /* 14: MU (w/o controls) */
@@ -102,6 +107,9 @@ static struct usbmix_name_map extigy_map[] = {
  * e.g. no Master and fake PCM volume
  *                     Pavel Mihaylov <bin@bash.info>
  */
+static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0}; /* just guess */
+static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */
+
 static struct usbmix_name_map mp3plus_map[] = {
        /* 1: IT pcm */
        /* 2: IT mic */
@@ -110,16 +118,19 @@ static struct usbmix_name_map mp3plus_map[] = {
        /* 5: OT digital out */
        /* 6: OT speaker */
        /* 7: OT pcm capture */
-       { 8, "Capture Input Source" }, /* FU, default PCM Capture Source */
+       { 8, "Capture Source" }, /* FU, default PCM Capture Source */
                /* (Mic, Input 1 = Line input, Input 2 = Optical input) */
        { 9, "Master Playback" }, /* FU, default Speaker 1 */
        /* { 10, "Mic Capture", 1 }, */ /* FU, Mic Capture */
-       /* { 10, "Mic Capture", 2 }, */ /* FU, Mic Capture */
+       { 10, /* "Mic Capture", */ NULL, 2, .dB = &mp3plus_dB_2 },
+               /* FU, Mic Capture */
        { 10, "Mic Boost", 7 }, /* FU, default Auto Gain Input */
-       { 11, "Line Capture" }, /* FU, default PCM Capture */
+       { 11, "Line Capture", .dB = &mp3plus_dB_2 },
+               /* FU, default PCM Capture */
        { 12, "Digital In Playback" }, /* FU, default PCM 1 */
-       /* { 13, "Mic Playback" }, */ /* FU, default Mic Playback */
-       { 14, "Line Playback" }, /* FU, default Speaker */
+       { 13, /* "Mic Playback", */ .dB = &mp3plus_dB_1 },
+               /* FU, default Mic Playback */
+       { 14, "Line Playback", .dB = &mp3plus_dB_1 }, /* FU, default Speaker */
        /* 15: MU */
        { 0 } /* terminator */
 };
index a892bda..e691eba 100644 (file)
@@ -1266,37 +1266,6 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                }
        }
 },
-/* Roland UA-101 in High-Speed Mode only */
-{
-       USB_DEVICE(0x0582, 0x007d),
-       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
-               .vendor_name = "Roland",
-               .product_name = "UA-101",
-               .ifnum = QUIRK_ANY_INTERFACE,
-               .type = QUIRK_COMPOSITE,
-               .data = (const struct snd_usb_audio_quirk[]) {
-                       {
-                               .ifnum = 0,
-                               .type = QUIRK_AUDIO_EDIROL_UA101
-                       },
-                       {
-                               .ifnum = 1,
-                               .type = QUIRK_AUDIO_EDIROL_UA101
-                       },
-                       {
-                               .ifnum = 2,
-                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
-                               .data = & (const struct snd_usb_midi_endpoint_info) {
-                                       .out_cables = 0x0001,
-                                       .in_cables  = 0x0001
-                               }
-                       },
-                       {
-                               .ifnum = -1
-                       }
-               }
-       }
-},
 {
        /* has ID 0x0081 when not in "Advanced Driver" mode */
        USB_DEVICE(0x0582, 0x0080),
@@ -2073,6 +2042,33 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        }
 },
 
+/* Access Music devices */
+{
+       /* VirusTI Desktop */
+       USB_DEVICE_VENDOR_SPEC(0x133e, 0x0815),
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_COMPOSITE,
+               .data = &(const struct snd_usb_audio_quirk[]) {
+                       {
+                               .ifnum = 3,
+                               .type = QUIRK_MIDI_FIXED_ENDPOINT,
+                               .data = &(const struct snd_usb_midi_endpoint_info) {
+                                       .out_cables = 0x0003,
+                                       .in_cables  = 0x0003
+                               }
+                       },
+                       {
+                               .ifnum = 4,
+                               .type = QUIRK_IGNORE_INTERFACE
+                       },
+                       {
+                               .ifnum = -1
+                       }
+               }
+       }
+},
+
 /* */
 {
        /* aka. Serato Scratch Live DJ Box */
@@ -2105,6 +2101,120 @@ YAMAHA_DEVICE(0x7010, "UB99"),
        }
 },
 
+/* Hauppauge HVR-950Q and HVR-850 */
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7200),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7201),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7202),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7203),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7204),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7205),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7250),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-950Q",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+{
+       USB_DEVICE_VENDOR_SPEC(0x2040, 0x7230),
+       .match_flags = USB_DEVICE_ID_MATCH_DEVICE |
+                      USB_DEVICE_ID_MATCH_INT_CLASS |
+                      USB_DEVICE_ID_MATCH_INT_SUBCLASS,
+       .bInterfaceClass = USB_CLASS_AUDIO,
+       .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL,
+       .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) {
+               .vendor_name = "Hauppauge",
+               .product_name = "HVR-850",
+               .ifnum = QUIRK_ANY_INTERFACE,
+               .type = QUIRK_AUDIO_ALIGN_TRANSFER,
+       }
+},
+
 {
        /*
         * Some USB MIDI devices don't have an audio control interface,