]> git.karo-electronics.de Git - linux-beck.git/commitdiff
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 8923597bd2bd215a55ba00be57deca7339930bc1..33df82e3a3983a416c7750a273e6fdef734dbf0f 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 a61499c22b0b96044314048791b3152e5249d98e..89e0ac17f44a1c461bd8b1cdc565f99b8eeb4217 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 7c44667e79a6498bb1137d4dcc3e11a3a78a342c..49b03c9e5e551554b71640fb3dd397beb4c28a06 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 c83a4a79f16b2646279fa1e68e7516bd73f2c122..8b611a561985a8350c217d160f167cb4d224d01a 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 cc4e226f35fd5b07a4c0aebbea8ea282aa1309b7..760c969d885d542997010ad8486662fc35731088 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 4e62ee1e4115222ad42c1fb52beea6c03b27da52..95353542256a2c69a9685460b49a853f8c8f5a7b 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 22939142dd236627796a248425702ffa6f413758..7fed23442db89f132b4e383d964875ccdf3f977d 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 268ab7471224a5e42fe4f448df75c7ad4ad7094e..439ce64f9d82edfcfdd867d152dc469969cc1ead 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 23a032c6d4873a53b6c95b06ef08f6523332bae4..3da4f92427d8acc129bfba67d2ca29ea0fe56fb1 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 d9c96353121a567b53f040dc1b87207aac40cac4..82d4e3329b3daa11c956e631bab2fb65a59f5d58 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 6884ae031f6fc4975c52fb4beb026aeaa61f2248..0d428d0896dbd564520f33f28f797912906b2c58 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 a27545b23ee9ff031e8eb98346a492516db42b8b..b546ac2660f9f64b8436e3efe836cfa0f298afee 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 caa7796bc2f513e2bb2a73c1c26a8c866856894b..d6d49d6651f9122a389572db10d4f2ff078bbec2 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 f7e1c9f0d3ed6b969892e2d1161998faebdc509a..87288762403055a3f6f842c70b9e99794956a1cd 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 8ca2be339f3bacb230c584472f5a371f6098073b..48eca9ff9ee7e144d2dac18f17e2c6b2f817f286 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 f745c317d6af17e9acb98928eb1332541299b8ec..160b1bd0cd62262931896573953e990736e4da52 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 252e04ce602f6adf6bf434f0b02edc24c4c8bb76..7f41990ed68b74f557caa6b180cd1b11db3ad2ef 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 6644d0034fbaa357b88f7207b676343881ff4207..35a2f71a6af5202f49b2e3339c68ebb2575abe7e 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 02fe81ca88fde87c29487c8cc09afcdda9a0810c..755a0a5f0e3fbaed9aa1a68a83cc04cf1a24be91 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 b906b9a1a81e622654fa8628f62b9547679afe77..c73d30c4f46289c982ff793f42f6b3a6828052b8 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 5fd52e4d707998769f317b8dbbaf3d7e0839f9f4..20becc89f6f6f45ab35a456591add0b93f89f038 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 c8a8da0d403616f48a3c39d0dc50dcffce8f1e91..a4af53b5c1cfefe30eaf0703ef7eea9dacdfb642 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 faeffceb01b796c86bbd339dfa940e507f024142..af36696817880fe9103641828b2b581060d507fe 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 658d55769c9cd5ff44adf13aee830b455b215f5f..7d84c9f34dc90cf491a001acf1bca64740ac9a3e 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 27a65150225168a3f7264c4d6c1c6f8667cc7bfa..eae6c1c0eff9bc536d87f8fb3eb2b7abb578a7e1 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 318ff0c823e75cfb869d42798502b030269f2faf..6496822c1808b53c172d56df4382fb88e9e20e95 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 5b9d6c18bc45a0479d1374480d64a47151b4801c..9191b32d913002fca98baea6b6945ab559156d54 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 f1d9d16b54864258ce47868556f4b2f5888909f7..6aff217379d910d2a333bec3c4c950a8ffe593d3 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 89466b056be79c8f1515db39d517b3dede0d25c2..24d152ccf80d21ac6b751a85839bcaa8bedaa651 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 c62530943888aaf26a18d3f11dd8afd6a80ff019..fde7c12fe5da9eac43384068c7c761b14cee193c 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 351654cf7b098e72db04403cb92972bfa0bed8e2..1298c68d6bf0f880042c54f0fe29fd777fc628d2 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 d9266bae284977fb4bccbff7bc062d421d65cee0..1caf5e3c1f6ad481064ea70cced23293333de96a 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 8f5098f92c370f81c937f8159827736b34c9742d..4382d0fa6b9a374c05f3eb674ed82e7d4f9cf7e0 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 aaf4da68969c4572c9201e06f8ddc47cbd711f60..5c6e322a48f0dfd0d3f23e78c4148be40455240a 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 3aa35af7ca9170746d4a5e972a7ff01261bddc71..d7653cb7ac60dc24f5d3e07ff1537f18a32e1299 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 3dbacde1a5afd45475ec79cdccf7f95a3d085f2a..d75cf7b0642684a8d2f7a6ebde3ee1296ece0ea4 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 42b4fbbd8e2b196b47c967d4057a634fe004b602..49d572a7b235cd21dfc8a3f5d8069b728a9ec2dc 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 e7e147bf8eb2686cc98a629905f9589a0fb5357d..91d7036b64118572a7c4ffa3ed20724e1ede6c00 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 c0e8c6b295cb98df3fb8ef164f373cb35f81ba58..aa51cc7771dd9f270307cd64dc013fd6d3adbcbd 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 a6527330df584964b5b96b82632f747e22da89f2..2f321e7306cd2c94739740dcb910c4c909d547ad 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 6c702ad4352ad8198af4a062c75c9d93571f10fd..279b78f06d220f2a850671b2d29eeb3876a2c3de 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 4d34bb0d99d327120dde4bf5bea211f98a30105a..67921f93a41e08eefc0a2899537d6a5fe071a4e7 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 69867ace7860be8774f9081432abbb7b74e9c2cb..4679ed83a43b8687402b7efca98ed47a0518ac98 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 4e2b925a94cc0af1cb586609d65aaed7ca36ed5b..37e1b5df5ab8d7b7bbfad9c7dd6afcfd56c537e2 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 15e4138bce17ea4b42b2904bbe29b43095252d38..0a3d3d6e77b4d668bad27a929cda8fa174d1cd1c 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 a312bae08f52d5552b07e6ff05b446a7f768d98d..1ded64e056433e22f8e96d8b0fbeb6dbf2c557d4 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 e2e0359bb0568ca98999f2512baeb83e67a55883..9edc65059e3e4e02fe76ed6526613591c213b089 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 033aec430117ac698c50cfe7514e26b6b792bdbd..767fa7f06cd0857e3ca2cf22ec333fbc683e0215 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 1be96ead42448d08be408f3f05993a9d76a14a82..56fcf00c0e27216a69426f178a917e5dd152d4e5 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 f4f0c8f5dad76ab71cf57eaf835bfda69922c972..3e5ca8fb519ff1ae5d2c03cf7f2beb8700b1745d 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 f9e169d33c03032d21c777805c8fe05880f5583f..ca47a8114c7f9e9ed366772f6d5fc8780bc674c1 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 dd7c41b037b45e0700c55db9e32851a04e34ef55..00b148a10239f4d0f97caba451fd6d0b3b4d46c0 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 dc464321d0f3ebda294e8e45de1db429122f4bc7..207479a641cff3fb115ff75f0cd861ec56227fe4 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 91e7faf69bbb3732dc7eb224635c6728fec31396..afb803708416e4dec1b0e7182f288b34243797f4 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 459c1f62783b627c2988c7d95ce5935c31e698f4..480cb1e905b61af9a5c3c12aaed35da17376ecd5 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 9fd8a57089434d3c16b178664a8980aaa88836c7..7167c0185d5281cb2c2a77bc978ce4c3dbdbdb5f 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 76541748e7bcf8ec43ccd5964d90faf4258f442a..f42e7e1a107457bf99091d5813b438a88fe31f2f 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 8c6db3aa3c1ad59d55ae51d67fbb5b112140c0f2..a65bafe0800f8c727569024e699f395231d4a46a 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 29043301ebb867aaaf32f0b6221905407e0f5a0f..20c7cbc89bb38aff3042e5b5a603de7af286a7f5 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 04cbf3eaf05a1cd8ae683d1ca2d39be50b0f5156..0a6c50bcd758073c0a7ba0de4043c886c71a2351 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 60228731841f84fc2b1bc814242f9e9d06839451..6da6663e9176eb467c794b5772acaf0b3fb271ab 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 4022e43a0053dc36fa04ed958d74f7c86f790e98..f5142796989b074480949b61a39c73c55f4ce7a3 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 57967e580571ffaf6634d5a8d5e265c4c9101d83..3cdc2ee2d1dd900d97c431a3180a405230f70b5a 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 641d7f07392c63edea8bcf8a9bc71957914239cd..8dab82d7d19d1368abdcb26dd25b7ae581a058b2 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 f9490ae36c2e6eeefafcd3d995b06d7db0bbc529..1df974dcb5f4b9b37a7e1727645a3fcbec50d0d9 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 e32a748979210098c6282bfb9879d4d8cf89d504..658db44ef74668b3583b4c11d4e0518be6b02743 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 4df51ef5e09533231100d94aa1bc35404e992df3..64417a7332200a8dda0acf4adf7ec36877d0719e 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 c0e64b8f52a40577834d29da28f42443667a59ff..2364f8a1bc21509e678c6d09eab207f2efa0e0c7 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 3f1e7475faea916203d6c3af085ce5bb6fd1300e..d1615a0579d17f50fe9563e837631ab89161b6ab 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 c36a78dd0b5e373d6202c97cc3dd54833e122583..616b55825a194af53d8bee426987422bd07ba0f1 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 2fef37a2a5b96693abc5988fc420a7827d36b8ae..98f7cfa81b5f3bb0fcbd660c3b1f9527e390bde7 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 0a58a7c1fd7cc69fc02aa042a356b4ad47b00ae4..776175c0bdad10c4bb86b014274dc349aac5db02 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 0b2cd9c8627750f0b2addbc5c8005756d9edb1aa..5e85f14fe5a8c83e78e3d59b5321674f3157136f 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 9ab625e15652512305bf1b3089a319bbe2efa2a0..2e4ab3e34a7454e1053d131d768aadc8a12d4aab 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 2db24d29332bea8eed11bc0e0311df1756666e11..8816b0bd2ba61102ea2b922d88f61efcccfb56c2 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 08392916691e365d53422f660cb5ac54e7c36b58..68f3c8ccc1bf83375bb6873bbe62c3237b415e11 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 2e44316530a298d39e1295df8d1f3f9edc585ef0..b1e3652f2f485669874130be56287e97080133f7 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 f591fc2ed960c4904d4a05a8fe5140fbbec774e9..bb9632c752a953e873bb0d85c32efa80c7021ab1 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 a60c0a0a89b737470da2b6d650d7c2cbb352fc7e..1035125336d6f89a0f36bc63d1bd5c8e67fae735 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 0604c8a85223bc7b0ceacfc6f60b3171193b65aa..beb9a5b69892e496a9717b917263a7fcef230cab 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 eb3819f9654a6630184b06436f9f9d1f5f9e7c9b..60b7cb2753cfd2fd1968bc6f978e95b542f62cfc 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 f357521c79e6ebc5bea7139d2f521f4ec31a1e8c..394c6e76bcbcba5a377d7d7ed38e8957ccf1f2ed 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 506194688995ddab5499c556f5d425c8b908511a..8c3f5c5b53013a879b5848663d7aadab209e2023 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 83750e9fd7b4a3cc0290498ac1a267ee5c5da30f..53ce9460504413a0fabbc705623a0832cb8f188d 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 e09e3ea7781e4c6a87247b1ae8adb2f176db49dc..ed1cc0abc2b82cf2a8523ad4711c3b8cfce90f0b 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 d61b5cbcccad4235314d6d0bd05ee93e82ed282f..8c041647f285e0fe44afb3eb853c96c6dd27dac7 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 f05c8c097aa8196de2f9fe0a62e6e45e2efbd66c..cc2bbfc65327f23284a3000257aa261e497f2189 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 551405114cbc67a33f04f0bf3fa37038ce1b32b7..6ebfa6e7ab9e28bd1301bd75ab0ff4334c7bd40a 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 b05bad944901b6662dbb19bc231260087e1c08b3..3e7e01824b4073febd740144f906a460d77670c9 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 eaa619bd2a0343f2d9a5e7a7c7c8b54585a9413c..6e6a7eb555b8103f455d5c65fd056d94f320917f 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 168af67d938e1415993b29eb166763327ec0048b..4203782d7cb79bec7e7bf47157b32ee04de1efee 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 1d369ff73805658967e87a52809168936d97f4da..df47f738098df265e1d702efb5adaec7cd49c022 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 2b82c5c723e159a1f393724dbca259f12d755050..c7fba53798138b9ec7604fab68d0861c7434db02 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 fb83e1ffa5cb5e99ed3b697bedaf5fe66ff78e91..553b752172593fc377d5da54d5267f47ece5ed3f 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 a11f453a6b6d697614943024696de4b5019d6339..ecaea9fb48ec24328ac634a6441ad3538abc5207 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 83508b3964fb7c91329bff336a3a276f42e0e7ba..e1baad74ea4b4f8a2ae80894c330c0a46026694d 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 f98b47cd6cfb38fc97335c3788a8f7f5cc3afca1..26ceace88c96febb3538e244d2efc3678aa72a7b 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 0a770a28e71f8b66a6c225bf08133dcec5d99390..0c8f05cc56beebdfcec62f2ff0f5d0a6c19b6bdf 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 092c6a7c2ff3f75a23e7dace87351849d03b7c3e..5ea21285ee1f6e700fa320b3710495e47f06aca7 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 40ccb419b6e97a83bdd253a05c529ad8adfec9d2..b36919c0d363ee85e5419ddbfc85afe78f50f193 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 3600e9cc9bc604ce61a1b11871424a40c7b5c5a3..8d8e0b5aa2486e7a430172fc5c9020bd964347e9 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 5778ae882b839fb918d597f29193aa9f75c054ee..7cee364976ff3e7488cb0443a7c64b853cfbe781 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 c9afc04adac8d126c302b7c11bcdc183b355766a..f97d35de66c41b2e9f276ce5fec037aedde27985 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 69a941c7b1588b7db4a9acd0fe79da65824c8944..214301d568faa9850402912da2d05f3c77fd57f7 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 fe0423c39598b9a98fca8f1f7af48c5bb9c7eaa9..7de782a5b8f4ccb784707e4027f111c12538b27b 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 a45c1169762b5a6ffa021947ee2feab26b9530e2..ff60908f4554ad1b15bbbae2973929e25a9458ef 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 c578c28f368eba10cc6baacb4ea81e549436af14..685015a53292d864e5ad05171b2062f9835f3a8c 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 da34095c707fd7b19fb33156248fc6461a7090e0..f628c33d80b3269830b184d9f1b883b7decbe322 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 43b436c5d01bc245a3bbbdfcd6c54f08af0feaa3..f419ee8d75f0fe0b351c26c396cb19adce748de3 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 799ba2570902a066ead90e38a1ce17e5b351f4e8..dbffb5b5c69df26877ab9e28165263c659e4575b 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 b70e26ad263f19c9b21e605274febb63d2a29d10..9ddc37300f6b5cd88420ff885823e486e0cd3dd5 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 c7cff6f8168a2d0159a0df20ea2c97550dafd6db..4fc6d8bc637e1f4498a900b81f8e958c2e8938a1 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 ae29073eea93f94f283411c68f21aef8956791cf..c1498fa5545fe9c52814d152e90e19b3bb95543d 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 b990143636f1699bb9e0815de7501738aa8d4b59..6433e65c9507d20a65be6c0b14af1275c30e0c40 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 9e7d12e7673f52abc3d02518da23a51c6d0ff59a..13cec1e5ced9634300e8ca4a50678f4e86eb6674 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 7cc38a11e9978c683f96fc55be8014754d694a34..6d795700be79123c131698f9f2b750c830e582f9 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 11b8c6514b3d8ecf5da061687d9c949e0d771d5a..0cca56038cd94d406fc4150f5494855916677afc 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 75283fbb4b3fd1723d62a73baf663c14f37f41d3..b64e78139d632c2be13178981efb1b80069df73a 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 a83d1968a8450f49816add1bf2bbc2ffbba962c6..7e8e7da592a98e01faf4804e6983dba9fc07878c 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 97a0731331a15f6ae2a4be6d16acce0f5e85915d..5a60492ac7b35f449ceacccecec45b65457d22a6 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 389941cf6100226f892903cfc20cdb6932fd570d..acd8f15f7bff43226a534633adeb515c3ba1021b 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 e3c229b63311ffc1af8178e18442340c6759c1af..5a87d683691fe0052073674030514347b1326ed3 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 acbedebcffd92afc8274ac531db746f487f299e9..289cb4dacfc79ec012b6efb174054efaa0afe855 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 6accaf9580b2c6cd556c93d99a4a8fda12449229..f03a2f2cffee88911e9c11db08c5dd7dd8087b55 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 89b3ed814d64b0d673dd728bcc3dedb7a826516e..b35343b0a9a58cb3444faf02a832b65e888af04c 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 833e9c7b27c7bce4b66d52b7a45ca1455ca6f912..95cfde27d25c6da928c5f02dc211e4231fd840cb 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 e66ef2b69b5d45a7424a161f6e90767628d98402..960a227eb6531795bbbdbdfa8430238939079a5c 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 f977dba7cbd09462ac760219711c04dbb189ea0e..d5e1c6eb7b7bba56f7a57e4b34229290d466a529 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 2ba5c0fd55dbb7adc590e568e0b78350124ffd91..9d5252bc870c15e2d86a4558a5ffb24124eda574 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 7bb827c7d8061bfea72e0c2579015f8668bbebc9..52c6eb57cc3f51fafa7f39c165543e046dd52016 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 a1b10d1a384d3950c7631d6b1f59fa7b879f896a..3d72c1effeef7937e70ba72f2f01be235c8c0a0a 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 bc539abb210582c01797a4115916e00088f3be92..44a3e2d8c5563e757fe437c569a45658ce471b41 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 1a5ff06110725ef407e1d82f83b05ceddfc70b01..7e3e8fbc90fefca268bde1a8ec27361b8889ac78 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 1f6406c4534d455120efcdd0c1c6e9e3b98a9173..337b9facadfdb4f1afc9061f857cf78c37c8703c 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 21cef97d478dc8fb0a127d73453714d886facedf..6d0581841d7a3536baebd40f7c47ee7b80ecaaff 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 8a332d2f615cfc01e4612a9eb459628e8596c59b..9595b5b535f38eec6d99ecaac0460222a6b43da2 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 47eb61561dfcad83b0bd7c22fe91f5ef9519856e..f7e8bbbe395392bbb4eecd170f75cfa5a3bd68c9 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 fc9136c3e0d7824a67d88d4bbc5781e4b7ac375d..99a9a814be0beecbb71d98df7a328652694e7ce2 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 e6b18b90d4510afa4393d700110cc84200c2f5ef..80c6821133816978cb91c92e8391e463d7156e48 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 5cfa608823f78d65e3f80d9e6b9ff08e377acb31..0d668f471620cc81283142cc16cc0d1159cb84f9 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 73525c048e7f60d7613eef093f850c6d204215e4..8c2925814ce46fba77978ab697f54a284f99510d 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 abb288bfe35db2a721bb1d512275bbac03e7f145..5bf64aef95581f813894dc5dc00b40c658288e17 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 9edef4684978f43285bd6e076532ab795cfb9ccb..c6b9c8cac59e3f08ed26e47b80c57eab89089084 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 40ba8115fb8131fcf38c1ef07abeded673b747ab..9d8cea48fc5fddaae92e7ea726c5d6dc54268cd2 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 aae50df062326a2fd7b2f1a0957ec65d379b5af6..b2da478a0fae6ecca37de97b8770321ccaa3296b 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 c998220b99c62e30c4d32276478ed7e15c2ca7c5..35b4830fb0c4a1b1b3e4072d3214d1bce0c9d0bb 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 77c35885e21cf40653076729f8b1d327b28af52a..79e903a6086207f24f60b3d8233f75187072b2ad 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 a892bda03df9d12f83006851fe5d82be7bc82347..e691eba6a83e4bc2b8e86dea32dfc05cf078ecfa 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,