Merge branch 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa
authorLinus Torvalds <torvalds@g5.osdl.org>
Sun, 24 Sep 2006 00:21:12 +0000 (17:21 -0700)
committerLinus Torvalds <torvalds@g5.osdl.org>
Sun, 24 Sep 2006 00:21:12 +0000 (17:21 -0700)
* 'linus' of master.kernel.org:/pub/scm/linux/kernel/git/perex/alsa: (148 commits)
  [ALSA] intel8x0m - Free irq in suspend
  [ALSA] Move CONFIG_SND_AC97_POWER_SAVE to pci/Kconfig
  [ALSA] usb-audio: add mixer control names for the Aureon 5.1 MkII
  [ALSA] ES1938: remove duplicate field initialization
  [ALSA] usb-audio: increase number of packets per URB
  [ALSA] hda-codec - Fix headphone auto-toggle on sigmatel codec
  [ALSA] hda-intel - A slight cleanup of timeout check in azx_get_response()
  [ALSA] hda-codec - Fix mic input with STAC92xx codecs
  [ALSA] mixart: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] gus: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] opl4: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] sound core: Use SEEK_{SET,CUR,END} instead of hardcoded values
  [ALSA] hda-codec - Support multiple headphone pins
  [ALSA] hda_intel prefer 24bit instead of 20bit
  [ALSA] hda-codec - Add vendor ids for Motorola and Conexant
  [ALSA] hda-codec - Add device id for Motorola si3054-compatible codec
  [ALSA] Add missing compat ioctls for ALSA control API
  [ALSA] powermac - Fix Oops when conflicting with aoa driver
  [ALSA] aoa: add locking to tas codec
  [ALSA] hda-intel - Fix suspend/resume with MSI
  ...

111 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
include/sound/ac97_codec.h
include/sound/ad1848.h
include/sound/ak4xxx-adda.h
include/sound/asound.h
include/sound/control.h
include/sound/core.h
include/sound/emu10k1.h
include/sound/info.h
include/sound/pcm.h
include/sound/timer.h
include/sound/tlv.h [new file with mode: 0644]
include/sound/vx_core.h
sound/aoa/codecs/Kconfig
sound/aoa/codecs/snd-aoa-codec-tas.c
sound/core/control.c
sound/core/control_compat.c
sound/core/device.c
sound/core/hwdep.c
sound/core/info.c
sound/core/info_oss.c
sound/core/init.c
sound/core/oss/mixer_oss.c
sound/core/oss/pcm_oss.c
sound/core/pcm.c
sound/core/pcm_compat.c
sound/core/pcm_memory.c
sound/core/pcm_native.c
sound/core/rawmidi.c
sound/core/rtctimer.c
sound/core/seq/oss/seq_oss.c
sound/core/seq/seq_device.c
sound/core/seq/seq_info.c
sound/core/sound.c
sound/core/sound_oss.c
sound/core/timer.c
sound/drivers/Kconfig
sound/drivers/Makefile
sound/drivers/dummy.c
sound/drivers/mpu401/mpu401.c
sound/drivers/mts64.c [new file with mode: 0644]
sound/drivers/opl4/opl4_proc.c
sound/drivers/vx/vx_mixer.c
sound/i2c/other/ak4xxx-adda.c
sound/isa/ad1816a/ad1816a_lib.c
sound/isa/ad1848/ad1848_lib.c
sound/isa/es18xx.c
sound/isa/gus/gus_mem_proc.c
sound/isa/opl3sa2.c
sound/pci/Kconfig
sound/pci/ac97/ac97_codec.c
sound/pci/ac97/ac97_patch.c
sound/pci/ac97/ac97_patch.h
sound/pci/ac97/ac97_pcm.c
sound/pci/ac97/ac97_proc.c
sound/pci/ac97/ak4531_codec.c
sound/pci/ca0106/ca0106_mixer.c
sound/pci/cs4281.c
sound/pci/cs46xx/dsp_spos.c
sound/pci/cs46xx/dsp_spos_scb_lib.c
sound/pci/cs5535audio/Makefile
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1_main.c
sound/pci/emu10k1/emu10k1x.c
sound/pci/emu10k1/emufx.c
sound/pci/emu10k1/p16v.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_intel.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/ice1712/aureon.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/phase.c
sound/pci/ice1712/pontis.c
sound/pci/ice1712/prodigy192.c
sound/pci/ice1712/revo.c
sound/pci/ice1712/revo.h
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/mixart/mixart.c
sound/pci/mixart/mixart_mixer.c
sound/pci/pcxhr/pcxhr_mixer.c
sound/pci/riptide/riptide.c
sound/pci/rme9652/hdsp.c
sound/pci/trident/trident_main.c
sound/pci/via82xx.c
sound/pci/vx222/vx222.c
sound/pci/vx222/vx222_ops.c
sound/pci/ymfpci/ymfpci_main.c
sound/pcmcia/pdaudiocf/pdaudiocf.c
sound/pcmcia/vx/vxp_mixer.c
sound/pcmcia/vx/vxpocket.c
sound/ppc/beep.c
sound/ppc/keywest.c
sound/ppc/tumbler.c
sound/sparc/dbri.c
sound/synth/emux/emux_proc.c
sound/usb/usbaudio.c
sound/usb/usbmixer.c
sound/usb/usbmixer_maps.c
sound/usb/usbquirks.h

index f61af23dd85d727c9b802ee0b61af99f3230d23f..e6b57dd46a4f37ba4eb7c41da93b344e7aaa8289 100644 (file)
@@ -758,6 +758,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     position_fix - Fix DMA pointer (0 = auto, 1 = none, 2 = POSBUF, 3 = FIFO size)
     single_cmd  - Use single immediate commands to communicate with
                codecs (for debugging only)
+    disable_msi - Disable Message Signaled Interrupt (MSI)
 
     This module supports one card and autoprobe.
 
@@ -778,11 +779,16 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          6stack-digout 6-jack with a SPDIF out
          w810          3-jack
          z71v          3-jack (HP shared SPDIF)
-         asus          3-jack
+         asus          3-jack (ASUS Mobo)
+         asus-w1v      ASUS W1V
+         asus-dig      ASUS with SPDIF out
+         asus-dig2     ASUS with SPDIF out (using GPIO2)
          uniwill       3-jack
          F1734         2-jack
          lg            LG laptop (m1 express dual)
-         lg-lw         LG LW20 laptop
+         lg-lw         LG LW20/LW25 laptop
+         tcl           TCL S700
+         clevo         Clevo laptops (m520G, m665n)
          test          for testing/debugging purpose, almost all controls can be
                        adjusted.  Appearing only when compiled with
                        $CONFIG_SND_DEBUG=y
@@ -790,6 +796,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
        ALC260
          hp            HP machines
+         hp-3013       HP machines (3013-variant)
          fujitsu       Fujitsu S7020
          acer          Acer TravelMate
          basic         fixed pin assignment (old default model)
@@ -797,24 +804,32 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
        ALC262
          fujitsu       Fujitsu Laptop
+         hp-bpc        HP xw4400/6400/8400/9400 laptops
+         benq          Benq ED8
          basic         fixed pin assignment w/o SPDIF
          auto          auto-config reading BIOS (default)
 
        ALC882/885
          3stack-dig    3-jack with SPDIF I/O
          6stck-dig     6-jack digital with SPDIF I/O
+         arima         Arima W820Di1
          auto          auto-config reading BIOS (default)
 
        ALC883/888
          3stack-dig    3-jack with SPDIF I/O
          6stack-dig    6-jack digital with SPDIF I/O
-         6stack-dig-demo  6-stack digital for Intel demo board
+         3stack-6ch    3-jack 6-channel
+         3stack-6ch-dig 3-jack 6-channel with SPDIF I/O
+         6stack-dig-demo  6-jack digital for Intel demo board
+         acer          Acer laptops (Travelmate 3012WTMi, Aspire 5600, etc)
          auto          auto-config reading BIOS (default)
 
        ALC861/660
          3stack        3-jack
          3stack-dig    3-jack with SPDIF I/O
          6stack-dig    6-jack with SPDIF I/O
+         3stack-660    3-jack (for ALC660)
+         uniwill-m31   Uniwill M31 laptop
          auto          auto-config reading BIOS (default)
 
        CMI9880
@@ -843,10 +858,21 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
          3stack-dig    ditto with SPDIF
          laptop        3-jack with hp-jack automute
          laptop-dig    ditto with SPDIF
-         auto          auto-confgi reading BIOS (default)
+         auto          auto-config reading BIOS (default)
+
+       STAC9200/9205/9220/9221/9254
+         ref           Reference board
+         3stack        D945 3stack
+         5stack        D945 5stack + SPDIF
 
-       STAC7661(?)
+       STAC9227/9228/9229/927x
+         ref           Reference board
+         3stack        D965 3stack
+         5stack        D965 5stack + SPDIF
+
+       STAC9872
          vaio          Setup for VAIO FE550G/SZ110
+         vaio-ar Setup for VAIO AR
 
     If the default configuration doesn't work and one of the above
     matches with your device, report it together with the PCI
@@ -1213,6 +1239,14 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     
     Module supports only 1 card.  This module has no enable option.
 
+  Module snd-mts64
+  ----------------
+
+    Module for Ego Systems (ESI) Miditerminal 4140
+
+    This module supports multiple devices.
+    Requires parport (CONFIG_PARPORT).
+
   Module snd-nm256
   ----------------
 
index b8dc51ca776c84b9e4eae4edf1c8477dc337e19a..4807ef79a94d8723168842a014e0250796cd98e7 100644 (file)
 
       <para>
       For a device which allows hotplugging, you can use
-      <function>snd_card_free_in_thread</function>.  This one will
-      postpone the destruction and wait in a kernel-thread until all
-      devices are closed.
+      <function>snd_card_free_when_closed</function>.  This one will
+      postpone the destruction until all devices are closed.
       </para>
 
     </section>
index 758f8bf133c7c8f5553e302f55ca804fc89d985a..4c43521cc493bd382815193f1315e3c3c088cedf 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <linux/bitops.h>
 #include <linux/device.h>
+#include <linux/workqueue.h>
 #include "pcm.h"
 #include "control.h"
 #include "info.h"
 #define AC97_GP_DRSS_1011      0x0000  /* LR(C) 10+11(+12) */
 #define AC97_GP_DRSS_78                0x0400  /* LR 7+8 */
 
+/* powerdown bits */
+#define AC97_PD_ADC_STATUS     0x0001  /* ADC status (RO) */
+#define AC97_PD_DAC_STATUS     0x0002  /* DAC status (RO) */
+#define AC97_PD_MIXER_STATUS   0x0004  /* Analog mixer status (RO) */
+#define AC97_PD_VREF_STATUS    0x0008  /* Vref status (RO) */
+#define AC97_PD_PR0            0x0100  /* Power down PCM ADCs and input MUX */
+#define AC97_PD_PR1            0x0200  /* Power down PCM front DAC */
+#define AC97_PD_PR2            0x0400  /* Power down Mixer (Vref still on) */
+#define AC97_PD_PR3            0x0800  /* Power down Mixer (Vref off) */
+#define AC97_PD_PR4            0x1000  /* Power down AC-Link */
+#define AC97_PD_PR5            0x2000  /* Disable internal clock usage */
+#define AC97_PD_PR6            0x4000  /* Headphone amplifier */
+#define AC97_PD_EAPD           0x8000  /* External Amplifer Power Down (EAPD) */
+
 /* extended audio ID bit defines */
 #define AC97_EI_VRA            0x0001  /* Variable bit rate supported */
 #define AC97_EI_DRA            0x0002  /* Double rate supported */
 #define AC97_SCAP_INV_EAPD     (1<<7)  /* inverted EAPD */
 #define AC97_SCAP_DETECT_BY_VENDOR (1<<8) /* use vendor registers for read tests */
 #define AC97_SCAP_NO_SPDIF     (1<<9)  /* don't build SPDIF controls */
+#define AC97_SCAP_EAPD_LED     (1<<10) /* EAPD as mute LED */
 
 /* ac97->flags */
 #define AC97_HAS_PC_BEEP       (1<<0)  /* force PC Speaker usage */
@@ -491,6 +507,12 @@ struct snd_ac97 {
        /* jack-sharing info */
        unsigned char indep_surround;
        unsigned char channel_mode;
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       unsigned int power_up;  /* power states */
+       struct workqueue_struct *power_workq;
+       struct work_struct power_work;
+#endif
        struct device dev;
 };
 
@@ -532,6 +554,15 @@ unsigned short snd_ac97_read(struct snd_ac97 *ac97, unsigned short reg);
 void snd_ac97_write_cache(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
 int snd_ac97_update(struct snd_ac97 *ac97, unsigned short reg, unsigned short value);
 int snd_ac97_update_bits(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup);
+#else
+static inline int snd_ac97_update_power(struct snd_ac97 *ac97, int reg,
+                                       int powerup)
+{
+       return 0;
+}
+#endif
 #ifdef CONFIG_PM
 void snd_ac97_suspend(struct snd_ac97 *ac97);
 void snd_ac97_resume(struct snd_ac97 *ac97);
@@ -583,6 +614,7 @@ struct ac97_pcm {
                     copy_flag: 1,         /* lowlevel driver must fill all entries */
                     spdif: 1;             /* spdif pcm */
        unsigned short aslots;             /* active slots */
+       unsigned short cur_dbl;            /* current double-rate state */
        unsigned int rates;                /* available rates */
        struct {
                unsigned short slots;      /* driver input: requested AC97 slot numbers */
index 57af1fe7b30998327847ac1cff7c003ac5ef407c..c8de6f83338f675efec508eecf7a56e3385d7a36 100644 (file)
@@ -179,14 +179,13 @@ enum { AD1848_MIX_SINGLE, AD1848_MIX_DOUBLE, AD1848_MIX_CAPTURE };
 #define AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) \
        ((left_reg) | ((right_reg) << 8) | ((shift_left) << 16) | ((shift_right) << 19) | ((mask) << 24) | ((invert) << 22))
 
-int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value);
-
 /* for ease of use */
 struct ad1848_mix_elem {
        const char *name;
        int index;
        int type;
        unsigned long private_value;
+       unsigned int *tlv;
 };
 
 #define AD1848_SINGLE(xname, xindex, reg, shift, mask, invert) \
@@ -195,15 +194,26 @@ struct ad1848_mix_elem {
   .type = AD1848_MIX_SINGLE, \
   .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert) }
 
+#define AD1848_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .name = xname, \
+  .index = xindex, \
+  .type = AD1848_MIX_SINGLE, \
+  .private_value = AD1848_MIXVAL_SINGLE(reg, shift, mask, invert), \
+  .tlv = xtlv }
+
 #define AD1848_DOUBLE(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \
 { .name = xname, \
   .index = xindex, \
   .type = AD1848_MIX_DOUBLE, \
   .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert) }
 
-static inline int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c)
-{
-       return snd_ad1848_add_ctl(chip, c->name, c->index, c->type, c->private_value);
-}
+#define AD1848_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .name = xname, \
+  .index = xindex, \
+  .type = AD1848_MIX_DOUBLE, \
+  .private_value = AD1848_MIXVAL_DOUBLE(left_reg, right_reg, shift_left, shift_right, mask, invert), \
+  .tlv = xtlv }
+
+int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip, const struct ad1848_mix_elem *c);
 
 #endif /* __SOUND_AD1848_H */
index 3d98884920261483c00f47879432933be112583f..d0deca669b92fe7cc96e856d6f9dbbcdbcf24ce9 100644 (file)
@@ -39,26 +39,39 @@ struct snd_ak4xxx_ops {
 
 #define AK4XXX_IMAGE_SIZE      (AK4XXX_MAX_CHIPS * 16) /* 64 bytes */
 
+/* DAC label and channels */
+struct snd_akm4xxx_dac_channel {
+       char *name;             /* mixer volume name */
+       unsigned int num_channels;
+};
+
+/* ADC labels and channels */
+struct snd_akm4xxx_adc_channel {
+       char *name;             /* capture gain volume label */
+       char *switch_name;      /* capture switch */
+       unsigned int num_channels;
+};
+
 struct snd_akm4xxx {
        struct snd_card *card;
        unsigned int num_adcs;                  /* AK4524 or AK4528 ADCs */
        unsigned int num_dacs;                  /* AK4524 or AK4528 DACs */
        unsigned char images[AK4XXX_IMAGE_SIZE]; /* saved register image */
-       unsigned char ipga_gain[AK4XXX_MAX_CHIPS][2]; /* saved register image
-                                                      * for IPGA (AK4528)
-                                                      */
+       unsigned char volumes[AK4XXX_IMAGE_SIZE]; /* saved volume values */
        unsigned long private_value[AK4XXX_MAX_CHIPS];  /* helper for driver */
        void *private_data[AK4XXX_MAX_CHIPS];           /* helper for driver */
        /* template should fill the following fields */
        unsigned int idx_offset;                /* control index offset */
        enum {
                SND_AK4524, SND_AK4528, SND_AK4529,
-               SND_AK4355, SND_AK4358, SND_AK4381
+               SND_AK4355, SND_AK4358, SND_AK4381,
+               SND_AK5365
        } type;
-       unsigned int *num_stereo;       /* array of combined counts
-                                        * for the mixer
-                                        */
-       char **channel_names;           /* array of mixer channel names */
+
+       /* (array) information of combined codecs */
+       struct snd_akm4xxx_dac_channel *dac_info;
+       struct snd_akm4xxx_adc_channel *adc_info;
+
        struct snd_ak4xxx_ops ops;
 };
 
@@ -72,9 +85,9 @@ int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak);
        (ak)->images[(chip) * 16 + (reg)]
 #define snd_akm4xxx_set(ak,chip,reg,val) \
        ((ak)->images[(chip) * 16 + (reg)] = (val))
-#define snd_akm4xxx_get_ipga(ak,chip,reg) \
-       (ak)->ipga_gain[chip][(reg)-4]
-#define snd_akm4xxx_set_ipga(ak,chip,reg,val) \
-       ((ak)->ipga_gain[chip][(reg)-4] = (val))
+#define snd_akm4xxx_get_vol(ak,chip,reg) \
+       (ak)->volumes[(chip) * 16 + (reg)]
+#define snd_akm4xxx_set_vol(ak,chip,reg,val) \
+       ((ak)->volumes[(chip) * 16 + (reg)] = (val))
 
 #endif /* __SOUND_AK4XXX_ADDA_H */
index 41885f48ad915d36c2dbed39b8c14a98283a2ba6..c1621c650a9a084988b26d3bb14fb428fb38ea4e 100644 (file)
@@ -688,7 +688,7 @@ struct snd_timer_tread {
  *                                                                          *
  ****************************************************************************/
 
-#define SNDRV_CTL_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 3)
+#define SNDRV_CTL_VERSION              SNDRV_PROTOCOL_VERSION(2, 0, 4)
 
 struct snd_ctl_card_info {
        int card;                       /* card number */
@@ -727,10 +727,15 @@ typedef int __bitwise snd_ctl_elem_iface_t;
 #define SNDRV_CTL_ELEM_ACCESS_WRITE            (1<<1)
 #define SNDRV_CTL_ELEM_ACCESS_READWRITE                (SNDRV_CTL_ELEM_ACCESS_READ|SNDRV_CTL_ELEM_ACCESS_WRITE)
 #define SNDRV_CTL_ELEM_ACCESS_VOLATILE         (1<<2)  /* control value may be changed without a notification */
-#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP                (1<<2)  /* when was control changed */
+#define SNDRV_CTL_ELEM_ACCESS_TIMESTAMP                (1<<3)  /* when was control changed */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_READ         (1<<4)  /* TLV read is possible */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_WRITE                (1<<5)  /* TLV write is possible */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE    (SNDRV_CTL_ELEM_ACCESS_TLV_READ|SNDRV_CTL_ELEM_ACCESS_TLV_WRITE)
+#define SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND      (1<<6)  /* TLV command is possible */
 #define SNDRV_CTL_ELEM_ACCESS_INACTIVE         (1<<8)  /* control does actually nothing, but may be updated */
 #define SNDRV_CTL_ELEM_ACCESS_LOCK             (1<<9)  /* write lock */
 #define SNDRV_CTL_ELEM_ACCESS_OWNER            (1<<10) /* write lock owner */
+#define SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK     (1<<28) /* kernel use a TLV callback */ 
 #define SNDRV_CTL_ELEM_ACCESS_USER             (1<<29) /* user space element */
 #define SNDRV_CTL_ELEM_ACCESS_DINDIRECT                (1<<30) /* indirect access for matrix dimensions in the info structure */
 #define SNDRV_CTL_ELEM_ACCESS_INDIRECT         (1<<31) /* indirect access for element value in the value structure */
@@ -818,6 +823,12 @@ struct snd_ctl_elem_value {
         unsigned char reserved[128-sizeof(struct timespec)];
 };
 
+struct snd_ctl_tlv {
+        unsigned int numid;    /* control element numeric identification */
+        unsigned int length;   /* in bytes aligned to 4 */
+        unsigned int tlv[0];   /* first TLV */
+};
+
 enum {
        SNDRV_CTL_IOCTL_PVERSION = _IOR('U', 0x00, int),
        SNDRV_CTL_IOCTL_CARD_INFO = _IOR('U', 0x01, struct snd_ctl_card_info),
@@ -831,6 +842,9 @@ enum {
        SNDRV_CTL_IOCTL_ELEM_ADD = _IOWR('U', 0x17, struct snd_ctl_elem_info),
        SNDRV_CTL_IOCTL_ELEM_REPLACE = _IOWR('U', 0x18, struct snd_ctl_elem_info),
        SNDRV_CTL_IOCTL_ELEM_REMOVE = _IOWR('U', 0x19, struct snd_ctl_elem_id),
+       SNDRV_CTL_IOCTL_TLV_READ = _IOWR('U', 0x1a, struct snd_ctl_tlv),
+       SNDRV_CTL_IOCTL_TLV_WRITE = _IOWR('U', 0x1b, struct snd_ctl_tlv),
+       SNDRV_CTL_IOCTL_TLV_COMMAND = _IOWR('U', 0x1c, struct snd_ctl_tlv),
        SNDRV_CTL_IOCTL_HWDEP_NEXT_DEVICE = _IOWR('U', 0x20, int),
        SNDRV_CTL_IOCTL_HWDEP_INFO = _IOR('U', 0x21, struct snd_hwdep_info),
        SNDRV_CTL_IOCTL_PCM_NEXT_DEVICE = _IOR('U', 0x30, int),
@@ -855,6 +869,7 @@ enum sndrv_ctl_event_type {
 #define SNDRV_CTL_EVENT_MASK_VALUE     (1<<0)  /* element value was changed */
 #define SNDRV_CTL_EVENT_MASK_INFO      (1<<1)  /* element info was changed */
 #define SNDRV_CTL_EVENT_MASK_ADD       (1<<2)  /* element was added */
+#define SNDRV_CTL_EVENT_MASK_TLV       (1<<3)  /* element TLV tree was changed */
 #define SNDRV_CTL_EVENT_MASK_REMOVE    (~0U)   /* element was removed */
 
 struct snd_ctl_event {
index 2489b1eb0110a4ea51b4da1b936a73e0aca4142c..1de148b0fd9472f5fd961c7c109d9f62053d69f9 100644 (file)
@@ -30,6 +30,11 @@ struct snd_kcontrol;
 typedef int (snd_kcontrol_info_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_info * uinfo);
 typedef int (snd_kcontrol_get_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
 typedef int (snd_kcontrol_put_t) (struct snd_kcontrol * kcontrol, struct snd_ctl_elem_value * ucontrol);
+typedef int (snd_kcontrol_tlv_rw_t)(struct snd_kcontrol *kcontrol,
+                                   int op_flag, /* 0=read,1=write,-1=command */
+                                   unsigned int size,
+                                   unsigned int __user *tlv);
+
 
 struct snd_kcontrol_new {
        snd_ctl_elem_iface_t iface;     /* interface identifier */
@@ -42,6 +47,10 @@ struct snd_kcontrol_new {
        snd_kcontrol_info_t *info;
        snd_kcontrol_get_t *get;
        snd_kcontrol_put_t *put;
+       union {
+               snd_kcontrol_tlv_rw_t *c;
+               unsigned int *p;
+       } tlv;
        unsigned long private_value;
 };
 
@@ -58,6 +67,10 @@ struct snd_kcontrol {
        snd_kcontrol_info_t *info;
        snd_kcontrol_get_t *get;
        snd_kcontrol_put_t *put;
+       union {
+               snd_kcontrol_tlv_rw_t *c;
+               unsigned int *p;
+       } tlv;
        unsigned long private_value;
        void *private_data;
        void (*private_free)(struct snd_kcontrol *kcontrol);
index bab3ff457e40cfa00e21b40f2d9e14fee12f8e07..b056ea925ecf33c7e06bf6e3d7c4d27d8f66a9b1 100644 (file)
@@ -25,8 +25,8 @@
 #include <linux/sched.h>               /* wake_up() */
 #include <linux/mutex.h>               /* struct mutex */
 #include <linux/rwsem.h>               /* struct rw_semaphore */
-#include <linux/workqueue.h>           /* struct workqueue_struct */
 #include <linux/pm.h>                  /* pm_message_t */
+#include <linux/device.h>
 
 /* forward declarations */
 #ifdef CONFIG_PCI
@@ -71,7 +71,6 @@ struct snd_device_ops {
        int (*dev_free)(struct snd_device *dev);
        int (*dev_register)(struct snd_device *dev);
        int (*dev_disconnect)(struct snd_device *dev);
-       int (*dev_unregister)(struct snd_device *dev);
 };
 
 struct snd_device {
@@ -131,8 +130,8 @@ struct snd_card {
                                                                state */
        spinlock_t files_lock;          /* lock the files for this card */
        int shutdown;                   /* this card is going down */
+       int free_on_last_close;         /* free in context of file_release */
        wait_queue_head_t shutdown_sleep;
-       struct work_struct free_workq;  /* for free in workqueue */
        struct device *dev;
 
 #ifdef CONFIG_PM
@@ -188,6 +187,7 @@ struct snd_minor {
        int device;                     /* device number */
        const struct file_operations *f_ops;    /* file operations */
        void *private_data;             /* private data for f_ops->open */
+       struct class_device *class_dev; /* class device for sysfs */
 };
 
 /* sound.c */
@@ -202,6 +202,8 @@ int snd_register_device(int type, struct snd_card *card, int dev,
                        const char *name);
 int snd_unregister_device(int type, struct snd_card *card, int dev);
 void *snd_lookup_minor_data(unsigned int minor, int type);
+int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
+                             const struct class_device_attribute *attr);
 
 #ifdef CONFIG_SND_OSSEMUL
 int snd_register_oss_device(int type, struct snd_card *card, int dev,
@@ -244,7 +246,7 @@ struct snd_card *snd_card_new(int idx, const char *id,
                         struct module *module, int extra_size);
 int snd_card_disconnect(struct snd_card *card);
 int snd_card_free(struct snd_card *card);
-int snd_card_free_in_thread(struct snd_card *card);
+int snd_card_free_when_closed(struct snd_card *card);
 int snd_card_register(struct snd_card *card);
 int snd_card_info_init(void);
 int snd_card_info_done(void);
index 884bbf54cd36115610c3604aefb26397ed6f30f2..892e310c504d61b38b5f43562ea6a1840ec6b3d4 100644 (file)
@@ -1524,6 +1524,10 @@ struct snd_emu10k1_fx8010_control_gpr {
        unsigned int value[32];         /* initial values */
        unsigned int min;               /* minimum range */
        unsigned int max;               /* maximum range */
+       union {
+               snd_kcontrol_tlv_rw_t *c;
+               unsigned int *p;
+       } tlv;
        unsigned int translation;       /* translation type (EMU10K1_GPR_TRANSLATION*) */
 };
 
index 74f6996769c7db6d706d77a60be3eabf8cec0d25..97ffc4fb99697fb52964a3bf235b70194ec24546 100644 (file)
@@ -71,7 +71,6 @@ struct snd_info_entry {
        mode_t mode;
        long size;
        unsigned short content;
-       unsigned short disconnected: 1;
        union {
                struct snd_info_entry_text text;
                struct snd_info_entry_ops *ops;
@@ -83,6 +82,8 @@ struct snd_info_entry {
        void (*private_free)(struct snd_info_entry *entry);
        struct proc_dir_entry *p;
        struct mutex access;
+       struct list_head children;
+       struct list_head list;
 };
 
 #if defined(CONFIG_SND_OSSEMUL) && defined(CONFIG_PROC_FS)
@@ -122,8 +123,8 @@ int snd_info_restore_text(struct snd_info_entry * entry);
 int snd_info_card_create(struct snd_card * card);
 int snd_info_card_register(struct snd_card * card);
 int snd_info_card_free(struct snd_card * card);
+void snd_info_card_disconnect(struct snd_card * card);
 int snd_info_register(struct snd_info_entry * entry);
-int snd_info_unregister(struct snd_info_entry * entry);
 
 /* for card drivers */
 int snd_card_proc_new(struct snd_card *card, const char *name, struct snd_info_entry **entryp);
@@ -156,8 +157,8 @@ static inline void snd_info_free_entry(struct snd_info_entry * entry) { ; }
 static inline int snd_info_card_create(struct snd_card * card) { return 0; }
 static inline int snd_info_card_register(struct snd_card * card) { return 0; }
 static inline int snd_info_card_free(struct snd_card * card) { return 0; }
+static inline void snd_info_card_disconnect(struct snd_card * card) { }
 static inline int snd_info_register(struct snd_info_entry * entry) { return 0; }
-static inline int snd_info_unregister(struct snd_info_entry * entry) { return 0; }
 
 static inline int snd_card_proc_new(struct snd_card *card, const char *name,
                                    struct snd_info_entry **entryp) { return -EINVAL; }
index f84d84993a31e83cf9d603f3f3e88c12db9e0411..60d40b34efc0fb79241b08ffaf25a876a563c4ba 100644 (file)
@@ -190,7 +190,7 @@ struct snd_pcm_ops {
 
 struct snd_pcm_file {
        struct snd_pcm_substream *substream;
-       struct snd_pcm_file *next;
+       int no_compat_mmap;
 };
 
 struct snd_pcm_hw_rule;
@@ -384,7 +384,6 @@ struct snd_pcm_substream {
        struct snd_info_entry *proc_prealloc_entry;
 #endif
        /* misc flags */
-       unsigned int no_mmap_ctrl: 1;
        unsigned int hw_opened: 1;
 };
 
@@ -402,7 +401,6 @@ struct snd_pcm_str {
        /* -- OSS things -- */
        struct snd_pcm_oss_stream oss;
 #endif
-       struct snd_pcm_file *files;
 #ifdef CONFIG_SND_VERBOSE_PROCFS
        struct snd_info_entry *proc_root;
        struct snd_info_entry *proc_info_entry;
index 5ece2bf541dce81d9c289b10a81e23703369adab..d42c083db1dac0d9dbdfa599b2228675123a5526 100644 (file)
@@ -129,7 +129,6 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
 int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer);
 int snd_timer_global_free(struct snd_timer *timer);
 int snd_timer_global_register(struct snd_timer *timer);
-int snd_timer_global_unregister(struct snd_timer *timer);
 
 int snd_timer_open(struct snd_timer_instance **ti, char *owner, struct snd_timer_id *tid, unsigned int slave_id);
 int snd_timer_close(struct snd_timer_instance *timeri);
diff --git a/include/sound/tlv.h b/include/sound/tlv.h
new file mode 100644 (file)
index 0000000..d93a96b
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __SOUND_TLV_H
+#define __SOUND_TLV_H
+
+/*
+ *  Advanced Linux Sound Architecture - ALSA - Driver
+ *  Copyright (c) 2006 by Jaroslav Kysela <perex@suse.cz>
+ *
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ *
+ */
+
+/*
+ * TLV structure is right behind the struct snd_ctl_tlv:
+ *   unsigned int type         - see SNDRV_CTL_TLVT_*
+ *   unsigned int length
+ *   .... data aligned to sizeof(unsigned int), use
+ *        block_length = (length + (sizeof(unsigned int) - 1)) &
+ *                       ~(sizeof(unsigned int) - 1)) ....
+ */
+
+#define SNDRV_CTL_TLVT_CONTAINER 0     /* one level down - group of TLVs */
+#define SNDRV_CTL_TLVT_DB_SCALE        1       /* dB scale */
+#define SNDRV_CTL_TLVT_DB_LINEAR 2     /* linear volume */
+#define SNDRV_CTL_TLVT_DB_RANGE 3      /* dB range container */
+
+#define TLV_DB_SCALE_ITEM(min, step, mute)                     \
+       SNDRV_CTL_TLVT_DB_SCALE, 2 * sizeof(unsigned int),      \
+       (min), ((step) & 0xffff) | ((mute) ? 0x10000 : 0)
+#define DECLARE_TLV_DB_SCALE(name, min, step, mute) \
+       unsigned int name[] = { TLV_DB_SCALE_ITEM(min, step, mute) }
+
+/* linear volume between min_dB and max_dB (.01dB unit) */
+#define TLV_DB_LINEAR_ITEM(min_dB, max_dB)                 \
+       SNDRV_CTL_TLVT_DB_LINEAR, 2 * sizeof(unsigned int), \
+       (min_dB), (max_dB)
+#define DECLARE_TLV_DB_LINEAR(name, min_dB, max_dB)    \
+       unsigned int name[] = { TLV_DB_LINEAR_ITEM(min_dB, max_dB) }
+
+/* dB range container */
+/* Each item is: <min> <max> <TLV> */
+/* The below assumes that each item TLV is 4 words like DB_SCALE or LINEAR */
+#define TLV_DB_RANGE_HEAD(num)                 \
+       SNDRV_CTL_TLVT_DB_RANGE, 6 * (num) * sizeof(unsigned int)
+
+#define TLV_DB_GAIN_MUTE       -9999999
+
+#endif /* __SOUND_TLV_H */
index 9821a6194caae33ebb72dabe803a83a0b6b91e15..dbca14170615bb93731a0b99d8c45c08f83211cc 100644 (file)
@@ -128,6 +128,7 @@ struct snd_vx_hardware {
        unsigned int num_ins;
        unsigned int num_outs;
        unsigned int output_level_max;
+       unsigned int *output_level_db_scale;
 };
 
 /* hwdep id string */
index 90cf58f68630b1e97e6157f800ec6747aeeca1cd..d5fbd6016e9370ebd3493dc6e37f7658eacba3da 100644 (file)
@@ -1,6 +1,8 @@
 config SND_AOA_ONYX
        tristate "support Onyx chip"
        depends on SND_AOA
+       select I2C
+       select I2C_POWERMAC
        ---help---
        This option enables support for the Onyx (pcm3052)
        codec chip found in the latest Apple machines
@@ -18,6 +20,8 @@ config SND_AOA_ONYX
 config SND_AOA_TAS
        tristate "support TAS chips"
        depends on SND_AOA
+       select I2C
+       select I2C_POWERMAC
        ---help---
        This option enables support for the tas chips
        found in a lot of Apple Machines, especially
index 16c0b6b0a80521eb90134bf50acfa46b842c9b0e..2ef55a17917c64999740b02780e035b2457429c2 100644 (file)
@@ -66,6 +66,8 @@
 #include <asm/prom.h>
 #include <linux/delay.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
+
 MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("tas codec driver for snd-aoa");
@@ -91,6 +93,10 @@ struct tas {
        u8                      bass, treble;
        u8                      acr;
        int                     drc_range;
+       /* protects hardware access against concurrency from
+        * userspace when hitting controls and during
+        * codec init/suspend/resume */
+       struct mutex            mtx;
 };
 
 static int tas_reset_init(struct tas *tas);
@@ -231,8 +237,10 @@ static int tas_snd_vol_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = tas->cached_volume_l;
        ucontrol->value.integer.value[1] = tas->cached_volume_r;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -241,14 +249,18 @@ static int tas_snd_vol_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        if (tas->cached_volume_l == ucontrol->value.integer.value[0]
-        && tas->cached_volume_r == ucontrol->value.integer.value[1])
+        && tas->cached_volume_r == ucontrol->value.integer.value[1]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->cached_volume_l = ucontrol->value.integer.value[0];
        tas->cached_volume_r = ucontrol->value.integer.value[1];
        if (tas->hw_enabled)
                tas_set_volume(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -276,8 +288,10 @@ static int tas_snd_mute_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = !tas->mute_l;
        ucontrol->value.integer.value[1] = !tas->mute_r;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -286,14 +300,18 @@ static int tas_snd_mute_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        if (tas->mute_l == !ucontrol->value.integer.value[0]
-        && tas->mute_r == !ucontrol->value.integer.value[1])
+        && tas->mute_r == !ucontrol->value.integer.value[1]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->mute_l = !ucontrol->value.integer.value[0];
        tas->mute_r = !ucontrol->value.integer.value[1];
        if (tas->hw_enabled)
                tas_set_volume(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -322,8 +340,10 @@ static int tas_snd_mixer_get(struct snd_kcontrol *kcontrol,
        struct tas *tas = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = tas->mixer_l[idx];
        ucontrol->value.integer.value[1] = tas->mixer_r[idx];
+       mutex_unlock(&tas->mtx);
 
        return 0;
 }
@@ -334,15 +354,19 @@ static int tas_snd_mixer_put(struct snd_kcontrol *kcontrol,
        struct tas *tas = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
 
+       mutex_lock(&tas->mtx);
        if (tas->mixer_l[idx] == ucontrol->value.integer.value[0]
-        && tas->mixer_r[idx] == ucontrol->value.integer.value[1])
+        && tas->mixer_r[idx] == ucontrol->value.integer.value[1]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->mixer_l[idx] = ucontrol->value.integer.value[0];
        tas->mixer_r[idx] = ucontrol->value.integer.value[1];
 
        if (tas->hw_enabled)
                tas_set_mixer(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -375,7 +399,9 @@ static int tas_snd_drc_range_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = tas->drc_range;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -384,12 +410,16 @@ static int tas_snd_drc_range_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
-       if (tas->drc_range == ucontrol->value.integer.value[0])
+       mutex_lock(&tas->mtx);
+       if (tas->drc_range == ucontrol->value.integer.value[0]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->drc_range = ucontrol->value.integer.value[0];
        if (tas->hw_enabled)
                tas3004_set_drc(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -417,7 +447,9 @@ static int tas_snd_drc_switch_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = tas->drc_enabled;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -426,12 +458,16 @@ static int tas_snd_drc_switch_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
-       if (tas->drc_enabled == ucontrol->value.integer.value[0])
+       mutex_lock(&tas->mtx);
+       if (tas->drc_enabled == ucontrol->value.integer.value[0]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->drc_enabled = ucontrol->value.integer.value[0];
        if (tas->hw_enabled)
                tas3004_set_drc(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -463,7 +499,9 @@ static int tas_snd_capture_source_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.enumerated.item[0] = !!(tas->acr & TAS_ACR_INPUT_B);
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -471,15 +509,21 @@ static int tas_snd_capture_source_put(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
-       int oldacr = tas->acr;
+       int oldacr;
+
+       mutex_lock(&tas->mtx);
+       oldacr = tas->acr;
 
        tas->acr &= ~TAS_ACR_INPUT_B;
        if (ucontrol->value.enumerated.item[0])
                tas->acr |= TAS_ACR_INPUT_B;
-       if (oldacr == tas->acr)
+       if (oldacr == tas->acr) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
        if (tas->hw_enabled)
                tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -518,7 +562,9 @@ static int tas_snd_treble_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = tas->treble;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -527,12 +573,16 @@ static int tas_snd_treble_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
-       if (tas->treble == ucontrol->value.integer.value[0])
+       mutex_lock(&tas->mtx);
+       if (tas->treble == ucontrol->value.integer.value[0]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->treble = ucontrol->value.integer.value[0];
        if (tas->hw_enabled)
                tas_set_treble(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -560,7 +610,9 @@ static int tas_snd_bass_get(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
+       mutex_lock(&tas->mtx);
        ucontrol->value.integer.value[0] = tas->bass;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -569,12 +621,16 @@ static int tas_snd_bass_put(struct snd_kcontrol *kcontrol,
 {
        struct tas *tas = snd_kcontrol_chip(kcontrol);
 
-       if (tas->bass == ucontrol->value.integer.value[0])
+       mutex_lock(&tas->mtx);
+       if (tas->bass == ucontrol->value.integer.value[0]) {
+               mutex_unlock(&tas->mtx);
                return 0;
+       }
 
        tas->bass = ucontrol->value.integer.value[0];
        if (tas->hw_enabled)
                tas_set_bass(tas);
+       mutex_unlock(&tas->mtx);
        return 1;
 }
 
@@ -628,16 +684,16 @@ static int tas_reset_init(struct tas *tas)
 
        tmp = TAS_MCS_SCLK64 | TAS_MCS_SPORT_MODE_I2S | TAS_MCS_SPORT_WL_24BIT;
        if (tas_write_reg(tas, TAS_REG_MCS, 1, &tmp))
-               return -ENODEV;
+               goto outerr;
 
        tas->acr |= TAS_ACR_ANALOG_PDOWN | TAS_ACR_B_MONAUREAL |
                TAS_ACR_B_MON_SEL_RIGHT;
        if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
-               return -ENODEV;
+               goto outerr;
 
        tmp = 0;
        if (tas_write_reg(tas, TAS_REG_MCS2, 1, &tmp))
-               return -ENODEV;
+               goto outerr;
 
        tas3004_set_drc(tas);
 
@@ -649,9 +705,11 @@ static int tas_reset_init(struct tas *tas)
 
        tas->acr &= ~TAS_ACR_ANALOG_PDOWN;
        if (tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr))
-               return -ENODEV;
+               goto outerr;
 
        return 0;
+ outerr:
+       return -ENODEV;
 }
 
 static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock)
@@ -666,11 +724,13 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
                break;
        case CLOCK_SWITCH_SLAVE:
                /* Clocks are back, re-init the codec */
+               mutex_lock(&tas->mtx);
                tas_reset_init(tas);
                tas_set_volume(tas);
                tas_set_mixer(tas);
                tas->hw_enabled = 1;
                tas->codec.gpio->methods->all_amps_restore(tas->codec.gpio);
+               mutex_unlock(&tas->mtx);
                break;
        default:
                /* doesn't happen as of now */
@@ -684,19 +744,23 @@ static int tas_switch_clock(struct codec_info_item *cii, enum clock_switch clock
  * our i2c device is suspended, and then take note of that! */
 static int tas_suspend(struct tas *tas)
 {
+       mutex_lock(&tas->mtx);
        tas->hw_enabled = 0;
        tas->acr |= TAS_ACR_ANALOG_PDOWN;
        tas_write_reg(tas, TAS_REG_ACR, 1, &tas->acr);
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
 static int tas_resume(struct tas *tas)
 {
        /* reset codec */
+       mutex_lock(&tas->mtx);
        tas_reset_init(tas);
        tas_set_volume(tas);
        tas_set_mixer(tas);
        tas->hw_enabled = 1;
+       mutex_unlock(&tas->mtx);
        return 0;
 }
 
@@ -739,11 +803,14 @@ static int tas_init_codec(struct aoa_codec *codec)
                return -EINVAL;
        }
 
+       mutex_lock(&tas->mtx);
        if (tas_reset_init(tas)) {
                printk(KERN_ERR PFX "tas failed to initialise\n");
+               mutex_unlock(&tas->mtx);
                return -ENXIO;
        }
        tas->hw_enabled = 1;
+       mutex_unlock(&tas->mtx);
 
        if (tas->codec.soundbus_dev->attach_codec(tas->codec.soundbus_dev,
                                                   aoa_get_card(),
@@ -822,6 +889,7 @@ static int tas_create(struct i2c_adapter *adapter,
        if (!tas)
                return -ENOMEM;
 
+       mutex_init(&tas->mtx);
        tas->i2c.driver = &tas_driver;
        tas->i2c.adapter = adapter;
        tas->i2c.addr = addr;
@@ -850,6 +918,7 @@ static int tas_create(struct i2c_adapter *adapter,
  detach:
        i2c_detach_client(&tas->i2c);
  fail:
+       mutex_destroy(&tas->mtx);
        kfree(tas);
        return -EINVAL;
 }
@@ -908,6 +977,7 @@ static int tas_i2c_detach(struct i2c_client *client)
        /* power down codec chip */
        tas_write_reg(tas, TAS_REG_ACR, 1, &tmp);
 
+       mutex_destroy(&tas->mtx);
        kfree(tas);
        return 0;
 }
index bb397eaa718793cb977687628eed02a9d4477907..6973a9686b679c6f3fc7a5415a1795ce043e6a76 100644 (file)
@@ -75,6 +75,8 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
        init_waitqueue_head(&ctl->change_sleep);
        spin_lock_init(&ctl->read_lock);
        ctl->card = card;
+       ctl->prefer_pcm_subdevice = -1;
+       ctl->prefer_rawmidi_subdevice = -1;
        ctl->pid = current->pid;
        file->private_data = ctl;
        write_lock_irqsave(&card->ctl_files_rwlock, flags);
@@ -236,11 +238,16 @@ struct snd_kcontrol *snd_ctl_new1(const struct snd_kcontrol_new *ncontrol,
        kctl.id.index = ncontrol->index;
        kctl.count = ncontrol->count ? ncontrol->count : 1;
        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_DINDIRECT|SNDRV_CTL_ELEM_ACCESS_INDIRECT));
+                (ncontrol->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
+                                     SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+                                     SNDRV_CTL_ELEM_ACCESS_DINDIRECT|
+                                     SNDRV_CTL_ELEM_ACCESS_INDIRECT|
+                                     SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE|
+                                     SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK));
        kctl.info = ncontrol->info;
        kctl.get = ncontrol->get;
        kctl.put = ncontrol->put;
+       kctl.tlv.p = ncontrol->tlv.p;
        kctl.private_value = ncontrol->private_value;
        kctl.private_data = private_data;
        return snd_ctl_new(&kctl, access);
@@ -882,6 +889,8 @@ struct user_element {
        struct snd_ctl_elem_info info;
        void *elem_data;                /* element data */
        unsigned long elem_data_size;   /* size of element data in bytes */
+       void *tlv_data;                 /* TLV data */
+       unsigned long tlv_data_size;    /* TLV data size */
        void *priv_data;                /* private data (like strings for enumerated type) */
        unsigned long priv_data_size;   /* size of private data in bytes */
 };
@@ -916,9 +925,48 @@ static int snd_ctl_elem_user_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
+                                int op_flag,
+                                unsigned int size,
+                                unsigned int __user *tlv)
+{
+       struct user_element *ue = kcontrol->private_data;
+       int change = 0;
+       void *new_data;
+
+       if (op_flag > 0) {
+               if (size > 1024 * 128)  /* sane value */
+                       return -EINVAL;
+               new_data = kmalloc(size, GFP_KERNEL);
+               if (new_data == NULL)
+                       return -ENOMEM;
+               if (copy_from_user(new_data, tlv, size)) {
+                       kfree(new_data);
+                       return -EFAULT;
+               }
+               change = ue->tlv_data_size != size;
+               if (!change)
+                       change = memcmp(ue->tlv_data, new_data, size);
+               kfree(ue->tlv_data);
+               ue->tlv_data = new_data;
+               ue->tlv_data_size = size;
+       } else {
+               if (! ue->tlv_data_size || ! ue->tlv_data)
+                       return -ENXIO;
+               if (size < ue->tlv_data_size)
+                       return -ENOSPC;
+               if (copy_to_user(tlv, ue->tlv_data, ue->tlv_data_size))
+                       return -EFAULT;
+       }
+       return change;
+}
+
 static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
 {
-       kfree(kcontrol->private_data);
+       struct user_element *ue = kcontrol->private_data;
+       if (ue->tlv_data)
+               kfree(ue->tlv_data);
+       kfree(ue);
 }
 
 static int snd_ctl_elem_add(struct snd_ctl_file *file,
@@ -937,7 +985,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
                return -EINVAL;
        access = info->access == 0 ? SNDRV_CTL_ELEM_ACCESS_READWRITE :
                (info->access & (SNDRV_CTL_ELEM_ACCESS_READWRITE|
-                                SNDRV_CTL_ELEM_ACCESS_INACTIVE));
+                                SNDRV_CTL_ELEM_ACCESS_INACTIVE|
+                                SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE));
        info->id.numid = 0;
        memset(&kctl, 0, sizeof(kctl));
        down_write(&card->controls_rwsem);
@@ -963,6 +1012,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
                kctl.get = snd_ctl_elem_user_get;
        if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
                kctl.put = snd_ctl_elem_user_put;
+       if (access & SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE) {
+               kctl.tlv.c = snd_ctl_elem_user_tlv;
+               access |= SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+       }
        switch (info->type) {
        case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
                private_size = sizeof(char);
@@ -997,6 +1050,7 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
        if (ue == NULL)
                return -ENOMEM;
        ue->info = *info;
+       ue->info.access = 0;
        ue->elem_data = (char *)ue + sizeof(*ue);
        ue->elem_data_size = private_size;
        kctl.private_free = snd_ctl_elem_user_free;
@@ -1067,6 +1121,67 @@ static int snd_ctl_subscribe_events(struct snd_ctl_file *file, int __user *ptr)
        return 0;
 }
 
+static int snd_ctl_tlv_ioctl(struct snd_ctl_file *file,
+                             struct snd_ctl_tlv __user *_tlv,
+                             int op_flag)
+{
+       struct snd_card *card = file->card;
+       struct snd_ctl_tlv tlv;
+       struct snd_kcontrol *kctl;
+       struct snd_kcontrol_volatile *vd;
+       unsigned int len;
+       int err = 0;
+
+       if (copy_from_user(&tlv, _tlv, sizeof(tlv)))
+               return -EFAULT;
+       if (tlv.length < sizeof(unsigned int) * 3)
+               return -EINVAL;
+       down_read(&card->controls_rwsem);
+       kctl = snd_ctl_find_numid(card, tlv.numid);
+       if (kctl == NULL) {
+               err = -ENOENT;
+               goto __kctl_end;
+       }
+       if (kctl->tlv.p == NULL) {
+               err = -ENXIO;
+               goto __kctl_end;
+       }
+       vd = &kctl->vd[tlv.numid - kctl->id.numid];
+       if ((op_flag == 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) == 0) ||
+           (op_flag > 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_WRITE) == 0) ||
+           (op_flag < 0 && (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_COMMAND) == 0)) {
+               err = -ENXIO;
+               goto __kctl_end;
+       }
+       if (vd->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+               if (file && vd->owner != NULL && vd->owner != file) {
+                       err = -EPERM;
+                       goto __kctl_end;
+               }
+               err = kctl->tlv.c(kctl, op_flag, tlv.length, _tlv->tlv); 
+               if (err > 0) {
+                       up_read(&card->controls_rwsem);
+                       snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_TLV, &kctl->id);
+                       return 0;
+               }
+       } else {
+               if (op_flag) {
+                       err = -ENXIO;
+                       goto __kctl_end;
+               }
+               len = kctl->tlv.p[1] + 2 * sizeof(unsigned int);
+               if (tlv.length < len) {
+                       err = -ENOMEM;
+                       goto __kctl_end;
+               }
+               if (copy_to_user(_tlv->tlv, kctl->tlv.p, len))
+                       err = -EFAULT;
+       }
+      __kctl_end:
+       up_read(&card->controls_rwsem);
+       return err;
+}
+
 static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct snd_ctl_file *ctl;
@@ -1086,11 +1201,11 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
        case SNDRV_CTL_IOCTL_CARD_INFO:
                return snd_ctl_card_info(card, ctl, cmd, argp);
        case SNDRV_CTL_IOCTL_ELEM_LIST:
-               return snd_ctl_elem_list(ctl->card, argp);
+               return snd_ctl_elem_list(card, argp);
        case SNDRV_CTL_IOCTL_ELEM_INFO:
                return snd_ctl_elem_info_user(ctl, argp);
        case SNDRV_CTL_IOCTL_ELEM_READ:
-               return snd_ctl_elem_read_user(ctl->card, argp);
+               return snd_ctl_elem_read_user(card, argp);
        case SNDRV_CTL_IOCTL_ELEM_WRITE:
                return snd_ctl_elem_write_user(ctl, argp);
        case SNDRV_CTL_IOCTL_ELEM_LOCK:
@@ -1105,6 +1220,12 @@ static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg
                return snd_ctl_elem_remove(ctl, argp);
        case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
                return snd_ctl_subscribe_events(ctl, ip);
+       case SNDRV_CTL_IOCTL_TLV_READ:
+               return snd_ctl_tlv_ioctl(ctl, argp, 0);
+       case SNDRV_CTL_IOCTL_TLV_WRITE:
+               return snd_ctl_tlv_ioctl(ctl, argp, 1);
+       case SNDRV_CTL_IOCTL_TLV_COMMAND:
+               return snd_ctl_tlv_ioctl(ctl, argp, -1);
        case SNDRV_CTL_IOCTL_POWER:
                return -ENOPROTOOPT;
        case SNDRV_CTL_IOCTL_POWER_STATE:
@@ -1338,6 +1459,11 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
        struct snd_card *card = device->device_data;
        struct list_head *flist;
        struct snd_ctl_file *ctl;
+       int err, cardnum;
+
+       snd_assert(card != NULL, return -ENXIO);
+       cardnum = card->number;
+       snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
 
        down_read(&card->controls_rwsem);
        list_for_each(flist, &card->ctl_files) {
@@ -1346,6 +1472,10 @@ static int snd_ctl_dev_disconnect(struct snd_device *device)
                kill_fasync(&ctl->fasync, SIGIO, POLL_ERR);
        }
        up_read(&card->controls_rwsem);
+
+       if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
+                                        card, -1)) < 0)
+               return err;
        return 0;
 }
 
@@ -1366,23 +1496,6 @@ static int snd_ctl_dev_free(struct snd_device *device)
        return 0;
 }
 
-/*
- * de-registration of the control device
- */
-static int snd_ctl_dev_unregister(struct snd_device *device)
-{
-       struct snd_card *card = device->device_data;
-       int err, cardnum;
-
-       snd_assert(card != NULL, return -ENXIO);
-       cardnum = card->number;
-       snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO);
-       if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL,
-                                        card, -1)) < 0)
-               return err;
-       return snd_ctl_dev_free(device);
-}
-
 /*
  * create control core:
  * called from init.c
@@ -1393,7 +1506,6 @@ int snd_ctl_create(struct snd_card *card)
                .dev_free = snd_ctl_dev_free,
                .dev_register = snd_ctl_dev_register,
                .dev_disconnect = snd_ctl_dev_disconnect,
-               .dev_unregister = snd_ctl_dev_unregister
        };
 
        snd_assert(card != NULL, return -ENXIO);
index 3c0161bb5ba45e45974bcf610612dac75f2c88f8..ab48962c48ce9716973392f2a426ab540e424b24 100644 (file)
@@ -407,6 +407,10 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
        case SNDRV_CTL_IOCTL_POWER_STATE:
        case SNDRV_CTL_IOCTL_ELEM_LOCK:
        case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
+       case SNDRV_CTL_IOCTL_ELEM_REMOVE:
+       case SNDRV_CTL_IOCTL_TLV_READ:
+       case SNDRV_CTL_IOCTL_TLV_WRITE:
+       case SNDRV_CTL_IOCTL_TLV_COMMAND:
                return snd_ctl_ioctl(file, cmd, (unsigned long)argp);
        case SNDRV_CTL_IOCTL_ELEM_LIST32:
                return snd_ctl_elem_list_compat(ctl->card, argp);
index 6ce4da4a1081995317c79523c22bfefd63786529..ccb25816ac9ececd151dad12fe042fe65ad97374 100644 (file)
@@ -71,7 +71,7 @@ EXPORT_SYMBOL(snd_device_new);
  * @device_data: the data pointer to release
  *
  * Removes the device from the list on the card and invokes the
- * callback, dev_unregister or dev_free, corresponding to the state.
+ * callbacks, dev_disconnect and dev_free, corresponding to the state.
  * Then release the device.
  *
  * Returns zero if successful, or a negative error code on failure or if the
@@ -90,16 +90,14 @@ int snd_device_free(struct snd_card *card, void *device_data)
                        continue;
                /* unlink */
                list_del(&dev->list);
-               if ((dev->state == SNDRV_DEV_REGISTERED ||
-                    dev->state == SNDRV_DEV_DISCONNECTED) &&
-                   dev->ops->dev_unregister) {
-                       if (dev->ops->dev_unregister(dev))
-                               snd_printk(KERN_ERR "device unregister failure\n");
-               } else {
-                       if (dev->ops->dev_free) {
-                               if (dev->ops->dev_free(dev))
-                                       snd_printk(KERN_ERR "device free failure\n");
-                       }
+               if (dev->state == SNDRV_DEV_REGISTERED &&
+                   dev->ops->dev_disconnect)
+                       if (dev->ops->dev_disconnect(dev))
+                               snd_printk(KERN_ERR
+                                          "device disconnect failure\n");
+               if (dev->ops->dev_free) {
+                       if (dev->ops->dev_free(dev))
+                               snd_printk(KERN_ERR "device free failure\n");
                }
                kfree(dev);
                return 0;
index 8bd0dcc93eba98f787664085bf9734f2ed89f05d..9aa9d94891f0a8eb01b41faac8fcefe54e1aa2a1 100644 (file)
@@ -42,7 +42,7 @@ static DEFINE_MUTEX(register_mutex);
 static int snd_hwdep_free(struct snd_hwdep *hwdep);
 static int snd_hwdep_dev_free(struct snd_device *device);
 static int snd_hwdep_dev_register(struct snd_device *device);
-static int snd_hwdep_dev_unregister(struct snd_device *device);
+static int snd_hwdep_dev_disconnect(struct snd_device *device);
 
 
 static struct snd_hwdep *snd_hwdep_search(struct snd_card *card, int device)
@@ -353,7 +353,7 @@ int snd_hwdep_new(struct snd_card *card, char *id, int device,
        static struct snd_device_ops ops = {
                .dev_free = snd_hwdep_dev_free,
                .dev_register = snd_hwdep_dev_register,
-               .dev_unregister = snd_hwdep_dev_unregister
+               .dev_disconnect = snd_hwdep_dev_disconnect,
        };
 
        snd_assert(rhwdep != NULL, return -EINVAL);
@@ -439,7 +439,7 @@ static int snd_hwdep_dev_register(struct snd_device *device)
        return 0;
 }
 
-static int snd_hwdep_dev_unregister(struct snd_device *device)
+static int snd_hwdep_dev_disconnect(struct snd_device *device)
 {
        struct snd_hwdep *hwdep = device->device_data;
 
@@ -454,9 +454,9 @@ static int snd_hwdep_dev_unregister(struct snd_device *device)
                snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device);
 #endif
        snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device);
-       list_del(&hwdep->list);
+       list_del_init(&hwdep->list);
        mutex_unlock(&register_mutex);
-       return snd_hwdep_free(hwdep);
+       return 0;
 }
 
 #ifdef CONFIG_PROC_FS
@@ -497,7 +497,7 @@ static void __init snd_hwdep_proc_init(void)
 
 static void __exit snd_hwdep_proc_done(void)
 {
-       snd_info_unregister(snd_hwdep_proc_entry);
+       snd_info_free_entry(snd_hwdep_proc_entry);
 }
 #else /* !CONFIG_PROC_FS */
 #define snd_hwdep_proc_init()
index 340332c6d97337889e73927730747feecd6e37a6..e43662b33f16e6ce7bf3b975175639194d6ced3f 100644 (file)
@@ -78,6 +78,7 @@ struct snd_info_private_data {
 
 static int snd_info_version_init(void);
 static int snd_info_version_done(void);
+static void snd_info_disconnect(struct snd_info_entry *entry);
 
 
 /* resize the proc r/w buffer */
@@ -174,15 +175,15 @@ static loff_t snd_info_entry_llseek(struct file *file, loff_t offset, int orig)
        switch (entry->content) {
        case SNDRV_INFO_CONTENT_TEXT:
                switch (orig) {
-               case 0: /* SEEK_SET */
+               case SEEK_SET:
                        file->f_pos = offset;
                        ret = file->f_pos;
                        goto out;
-               case 1: /* SEEK_CUR */
+               case SEEK_CUR:
                        file->f_pos += offset;
                        ret = file->f_pos;
                        goto out;
-               case 2: /* SEEK_END */
+               case SEEK_END:
                default:
                        ret = -EINVAL;
                        goto out;
@@ -304,7 +305,7 @@ static int snd_info_entry_open(struct inode *inode, struct file *file)
        mutex_lock(&info_mutex);
        p = PDE(inode);
        entry = p == NULL ? NULL : (struct snd_info_entry *)p->data;
-       if (entry == NULL || entry->disconnected) {
+       if (entry == NULL || ! entry->p) {
                mutex_unlock(&info_mutex);
                return -ENODEV;
        }
@@ -586,10 +587,10 @@ int __exit snd_info_done(void)
        snd_info_version_done();
        if (snd_proc_root) {
 #if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
-               snd_info_unregister(snd_seq_root);
+               snd_info_free_entry(snd_seq_root);
 #endif
 #ifdef CONFIG_SND_OSSEMUL
-               snd_info_unregister(snd_oss_root);
+               snd_info_free_entry(snd_oss_root);
 #endif
                snd_remove_proc_entry(&proc_root, snd_proc_root);
        }
@@ -648,17 +649,28 @@ int snd_info_card_register(struct snd_card *card)
  * de-register the card proc file
  * called from init.c
  */
-int snd_info_card_free(struct snd_card *card)
+void snd_info_card_disconnect(struct snd_card *card)
 {
-       snd_assert(card != NULL, return -ENXIO);
+       snd_assert(card != NULL, return);
+       mutex_lock(&info_mutex);
        if (card->proc_root_link) {
                snd_remove_proc_entry(snd_proc_root, card->proc_root_link);
                card->proc_root_link = NULL;
        }
-       if (card->proc_root) {
-               snd_info_unregister(card->proc_root);
-               card->proc_root = NULL;
-       }
+       if (card->proc_root)
+               snd_info_disconnect(card->proc_root);
+       mutex_unlock(&info_mutex);
+}
+
+/*
+ * release the card proc file resources
+ * called from init.c
+ */
+int snd_info_card_free(struct snd_card *card)
+{
+       snd_assert(card != NULL, return -ENXIO);
+       snd_info_free_entry(card->proc_root);
+       card->proc_root = NULL;
        return 0;
 }
 
@@ -767,6 +779,8 @@ static struct snd_info_entry *snd_info_create_entry(const char *name)
        entry->mode = S_IFREG | S_IRUGO;
        entry->content = SNDRV_INFO_CONTENT_TEXT;
        mutex_init(&entry->access);
+       INIT_LIST_HEAD(&entry->children);
+       INIT_LIST_HEAD(&entry->list);
        return entry;
 }
 
@@ -819,30 +833,35 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
 
 EXPORT_SYMBOL(snd_info_create_card_entry);
 
-static int snd_info_dev_free_entry(struct snd_device *device)
+static void snd_info_disconnect(struct snd_info_entry *entry)
 {
-       struct snd_info_entry *entry = device->device_data;
-       snd_info_free_entry(entry);
-       return 0;
-}
+       struct list_head *p, *n;
+       struct proc_dir_entry *root;
 
-static int snd_info_dev_register_entry(struct snd_device *device)
-{
-       struct snd_info_entry *entry = device->device_data;
-       return snd_info_register(entry);
+       list_for_each_safe(p, n, &entry->children) {
+               snd_info_disconnect(list_entry(p, struct snd_info_entry, list));
+       }
+
+       if (! entry->p)
+               return;
+       list_del_init(&entry->list);
+       root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
+       snd_assert(root, return);
+       snd_remove_proc_entry(root, entry->p);
+       entry->p = NULL;
 }
 
-static int snd_info_dev_disconnect_entry(struct snd_device *device)
+static int snd_info_dev_free_entry(struct snd_device *device)
 {
        struct snd_info_entry *entry = device->device_data;
-       entry->disconnected = 1;
+       snd_info_free_entry(entry);
        return 0;
 }
 
-static int snd_info_dev_unregister_entry(struct snd_device *device)
+static int snd_info_dev_register_entry(struct snd_device *device)
 {
        struct snd_info_entry *entry = device->device_data;
-       return snd_info_unregister(entry);
+       return snd_info_register(entry);
 }
 
 /**
@@ -871,8 +890,7 @@ int snd_card_proc_new(struct snd_card *card, const char *name,
        static struct snd_device_ops ops = {
                .dev_free = snd_info_dev_free_entry,
                .dev_register = snd_info_dev_register_entry,
-               .dev_disconnect = snd_info_dev_disconnect_entry,
-               .dev_unregister = snd_info_dev_unregister_entry
+               /* disconnect is done via snd_info_card_disconnect() */
        };
        struct snd_info_entry *entry;
        int err;
@@ -901,6 +919,11 @@ void snd_info_free_entry(struct snd_info_entry * entry)
 {
        if (entry == NULL)
                return;
+       if (entry->p) {
+               mutex_lock(&info_mutex);
+               snd_info_disconnect(entry);
+               mutex_unlock(&info_mutex);
+       }
        kfree(entry->name);
        if (entry->private_free)
                entry->private_free(entry);
@@ -935,38 +958,14 @@ int snd_info_register(struct snd_info_entry * entry)
        p->size = entry->size;
        p->data = entry;
        entry->p = p;
+       if (entry->parent)
+               list_add_tail(&entry->list, &entry->parent->children);
        mutex_unlock(&info_mutex);
        return 0;
 }
 
 EXPORT_SYMBOL(snd_info_register);
 
-/**
- * snd_info_unregister - de-register the info entry
- * @entry: the info entry
- *
- * De-registers the info entry and releases the instance.
- *
- * Returns zero if successful, or a negative error code on failure.
- */
-int snd_info_unregister(struct snd_info_entry * entry)
-{
-       struct proc_dir_entry *root;
-
-       if (! entry)
-               return 0;
-       snd_assert(entry->p != NULL, return -ENXIO);
-       root = entry->parent == NULL ? snd_proc_root : entry->parent->p;
-       snd_assert(root, return -ENXIO);
-       mutex_lock(&info_mutex);
-       snd_remove_proc_entry(root, entry->p);
-       mutex_unlock(&info_mutex);
-       snd_info_free_entry(entry);
-       return 0;
-}
-
-EXPORT_SYMBOL(snd_info_unregister);
-
 /*
 
  */
@@ -999,8 +998,7 @@ static int __init snd_info_version_init(void)
 
 static int __exit snd_info_version_done(void)
 {
-       if (snd_info_version_entry)
-               snd_info_unregister(snd_info_version_entry);
+       snd_info_free_entry(snd_info_version_entry);
        return 0;
 }
 
index bb2c40d0ab66cd6f890ad86ac713246c561b3c7e..3ebc34919c76fc746a12643135b73d9c0f5b2435 100644 (file)
@@ -131,10 +131,8 @@ int snd_info_minor_register(void)
 
 int snd_info_minor_unregister(void)
 {
-       if (snd_sndstat_proc_entry) {
-               snd_info_unregister(snd_sndstat_proc_entry);
-               snd_sndstat_proc_entry = NULL;
-       }
+       snd_info_free_entry(snd_sndstat_proc_entry);
+       snd_sndstat_proc_entry = NULL;
        return 0;
 }
 
index 4d9258884e444c368be86da0c470a69726f2253d..d7607a25acdf7086e05cbe16d2d64c7e19ed5897 100644 (file)
@@ -81,8 +81,6 @@ static inline int init_info_for_card(struct snd_card *card)
 #define init_info_for_card(card)
 #endif
 
-static void snd_card_free_thread(void * __card);
-
 /**
  *  snd_card_new - create and initialize a soundcard structure
  *  @idx: card index (address) [0 ... (SNDRV_CARDS-1)]
@@ -145,7 +143,6 @@ struct snd_card *snd_card_new(int idx, const char *xid,
        INIT_LIST_HEAD(&card->ctl_files);
        spin_lock_init(&card->files_lock);
        init_waitqueue_head(&card->shutdown_sleep);
-       INIT_WORK(&card->free_workq, snd_card_free_thread, card);
 #ifdef CONFIG_PM
        mutex_init(&card->power_lock);
        init_waitqueue_head(&card->power_sleep);
@@ -310,6 +307,7 @@ int snd_card_disconnect(struct snd_card *card)
        if (err < 0)
                snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number);
 
+       snd_info_card_disconnect(card);
        return 0;       
 }
 
@@ -326,22 +324,10 @@ EXPORT_SYMBOL(snd_card_disconnect);
  *  Returns zero. Frees all associated devices and frees the control
  *  interface associated to given soundcard.
  */
-int snd_card_free(struct snd_card *card)
+static int snd_card_do_free(struct snd_card *card)
 {
        struct snd_shutdown_f_ops *s_f_ops;
 
-       if (card == NULL)
-               return -EINVAL;
-       mutex_lock(&snd_card_mutex);
-       snd_cards[card->number] = NULL;
-       mutex_unlock(&snd_card_mutex);
-
-#ifdef CONFIG_PM
-       wake_up(&card->power_sleep);
-#endif
-       /* wait, until all devices are ready for the free operation */
-       wait_event(card->shutdown_sleep, card->files == NULL);
-
 #if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
        if (snd_mixer_oss_notify_callback)
                snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE);
@@ -360,7 +346,7 @@ int snd_card_free(struct snd_card *card)
        }
        if (card->private_free)
                card->private_free(card);
-       snd_info_unregister(card->proc_id);
+       snd_info_free_entry(card->proc_id);
        if (snd_info_card_free(card) < 0) {
                snd_printk(KERN_WARNING "unable to free card info\n");
                /* Not fatal error */
@@ -370,61 +356,59 @@ int snd_card_free(struct snd_card *card)
                card->s_f_ops = s_f_ops->next;
                kfree(s_f_ops);
        }
+       kfree(card);
+       return 0;
+}
+
+static int snd_card_free_prepare(struct snd_card *card)
+{
+       if (card == NULL)
+               return -EINVAL;
+       (void) snd_card_disconnect(card);
        mutex_lock(&snd_card_mutex);
+       snd_cards[card->number] = NULL;
        snd_cards_lock &= ~(1 << card->number);
        mutex_unlock(&snd_card_mutex);
-       kfree(card);
+#ifdef CONFIG_PM
+       wake_up(&card->power_sleep);
+#endif
        return 0;
 }
 
-EXPORT_SYMBOL(snd_card_free);
-
-static void snd_card_free_thread(void * __card)
+int snd_card_free_when_closed(struct snd_card *card)
 {
-       struct snd_card *card = __card;
-       struct module * module = card->module;
-
-       if (!try_module_get(module)) {
-               snd_printk(KERN_ERR "unable to lock toplevel module for card %i in free thread\n", card->number);
-               module = NULL;
-       }
+       int free_now = 0;
+       int ret = snd_card_free_prepare(card);
+       if (ret)
+               return ret;
 
-       snd_card_free(card);
+       spin_lock(&card->files_lock);
+       if (card->files == NULL)
+               free_now = 1;
+       else
+               card->free_on_last_close = 1;
+       spin_unlock(&card->files_lock);
 
-       module_put(module);
+       if (free_now)
+               snd_card_do_free(card);
+       return 0;
 }
 
-/**
- *  snd_card_free_in_thread - call snd_card_free() in thread
- *  @card: soundcard structure
- *
- *  This function schedules the call of snd_card_free() function in a
- *  work queue.  When all devices are released (non-busy), the work
- *  is woken up and calls snd_card_free().
- *
- *  When a card can be disconnected at any time by hotplug service,
- *  this function should be used in disconnect (or detach) callback
- *  instead of calling snd_card_free() directly.
- *  
- *  Returns - zero otherwise a negative error code if the start of thread failed.
- */
-int snd_card_free_in_thread(struct snd_card *card)
-{
-       if (card->files == NULL) {
-               snd_card_free(card);
-               return 0;
-       }
+EXPORT_SYMBOL(snd_card_free_when_closed);
 
-       if (schedule_work(&card->free_workq))
-               return 0;
+int snd_card_free(struct snd_card *card)
+{
+       int ret = snd_card_free_prepare(card);
+       if (ret)
+               return ret;
 
-       snd_printk(KERN_ERR "schedule_work() failed in snd_card_free_in_thread for card %i\n", card->number);
-       /* try to free the structure immediately */
-       snd_card_free(card);
-       return -EFAULT;
+       /* wait, until all devices are ready for the free operation */
+       wait_event(card->shutdown_sleep, card->files == NULL);
+       snd_card_do_free(card);
+       return 0;
 }
 
-EXPORT_SYMBOL(snd_card_free_in_thread);
+EXPORT_SYMBOL(snd_card_free);
 
 static void choose_default_id(struct snd_card *card)
 {
@@ -625,9 +609,9 @@ int __init snd_card_info_init(void)
 
 int __exit snd_card_info_done(void)
 {
-       snd_info_unregister(snd_card_info_entry);
+       snd_info_free_entry(snd_card_info_entry);
 #ifdef MODULE
-       snd_info_unregister(snd_card_module_info_entry);
+       snd_info_free_entry(snd_card_module_info_entry);
 #endif
        return 0;
 }
@@ -708,15 +692,16 @@ EXPORT_SYMBOL(snd_card_file_add);
  *
  *  This function removes the file formerly added to the card via
  *  snd_card_file_add() function.
- *  If all files are removed and the release of the card is
- *  scheduled, it will wake up the the thread to call snd_card_free()
- *  (see snd_card_free_in_thread() function).
+ *  If all files are removed and snd_card_free_when_closed() was
+ *  called beforehand, it processes the pending release of
+ *  resources.
  *
  *  Returns zero or a negative error code.
  */
 int snd_card_file_remove(struct snd_card *card, struct file *file)
 {
        struct snd_monitor_file *mfile, *pfile = NULL;
+       int last_close = 0;
 
        spin_lock(&card->files_lock);
        mfile = card->files;
@@ -731,9 +716,14 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
                pfile = mfile;
                mfile = mfile->next;
        }
-       spin_unlock(&card->files_lock);
        if (card->files == NULL)
+               last_close = 1;
+       spin_unlock(&card->files_lock);
+       if (last_close) {
                wake_up(&card->shutdown_sleep);
+               if (card->free_on_last_close)
+                       snd_card_do_free(card);
+       }
        if (!mfile) {
                snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
                return -ENOENT;
index 75a9505c744556551f2e12c090e3eb2c03f4d789..f4c67042e3ac16ba72bb6effe6a2e93550fc7bf5 100644 (file)
@@ -1193,10 +1193,8 @@ static void snd_mixer_oss_proc_init(struct snd_mixer_oss *mixer)
 
 static void snd_mixer_oss_proc_done(struct snd_mixer_oss *mixer)
 {
-       if (mixer->proc_entry) {
-               snd_info_unregister(mixer->proc_entry);
-               mixer->proc_entry = NULL;
-       }
+       snd_info_free_entry(mixer->proc_entry);
+       mixer->proc_entry = NULL;
 }
 #else /* !CONFIG_PROC_FS */
 #define snd_mixer_oss_proc_init(mix)
@@ -1312,21 +1310,19 @@ static int snd_mixer_oss_notify_handler(struct snd_card *card, int cmd)
                card->mixer_oss = mixer;
                snd_mixer_oss_build(mixer);
                snd_mixer_oss_proc_init(mixer);
-       } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) {
-               mixer = card->mixer_oss;
-               if (mixer == NULL || !mixer->oss_dev_alloc)
-                       return 0;
-               snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
-               mixer->oss_dev_alloc = 0;
-       } else {                /* free */
+       } else {
                mixer = card->mixer_oss;
                if (mixer == NULL)
                        return 0;
+               if (mixer->oss_dev_alloc) {
 #ifdef SNDRV_OSS_INFO_DEV_MIXERS
-               snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
+                       snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number);
 #endif
-               if (mixer->oss_dev_alloc)
                        snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0);
+                       mixer->oss_dev_alloc = 0;
+               }
+               if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT)
+                       return 0;
                snd_mixer_oss_proc_done(mixer);
                return snd_mixer_oss_free1(mixer);
        }
index 472fce0ee0e8111210e68ead84424f4f477635f2..505b23ec4058c29a4407d8cd29c78e0c3509bdb1 100644 (file)
@@ -2846,11 +2846,9 @@ static void snd_pcm_oss_proc_done(struct snd_pcm *pcm)
        int stream;
        for (stream = 0; stream < 2; ++stream) {
                struct snd_pcm_str *pstr = &pcm->streams[stream];
-               if (pstr->oss.proc_entry) {
-                       snd_info_unregister(pstr->oss.proc_entry);
-                       pstr->oss.proc_entry = NULL;
-                       snd_pcm_oss_proc_free_setup_list(pstr);
-               }
+               snd_info_free_entry(pstr->oss.proc_entry);
+               pstr->oss.proc_entry = NULL;
+               snd_pcm_oss_proc_free_setup_list(pstr);
        }
 }
 #else /* !CONFIG_SND_VERBOSE_PROCFS */
@@ -2931,25 +2929,23 @@ static int snd_pcm_oss_disconnect_minor(struct snd_pcm *pcm)
                        snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM,
                                                  pcm->card, 1);
                }
-       }
-       return 0;
-}
-
-static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)
-{
-       snd_pcm_oss_disconnect_minor(pcm);
-       if (pcm->oss.reg) {
                if (dsp_map[pcm->card->number] == (int)pcm->device) {
 #ifdef SNDRV_OSS_INFO_DEV_AUDIO
                        snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number);
 #endif
                }
                pcm->oss.reg = 0;
-               snd_pcm_oss_proc_done(pcm);
        }
        return 0;
 }
 
+static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm)
+{
+       snd_pcm_oss_disconnect_minor(pcm);
+       snd_pcm_oss_proc_done(pcm);
+       return 0;
+}
+
 static struct snd_pcm_notify snd_pcm_oss_notify =
 {
        .n_register =   snd_pcm_oss_register_minor,
index 7581edd7b9ffbf354d8542b010aa16d671cfca63..bf8f412988b8ead58fe147d102c006fda12b78fb 100644 (file)
@@ -42,7 +42,6 @@ static int snd_pcm_free(struct snd_pcm *pcm);
 static int snd_pcm_dev_free(struct snd_device *device);
 static int snd_pcm_dev_register(struct snd_device *device);
 static int snd_pcm_dev_disconnect(struct snd_device *device);
-static int snd_pcm_dev_unregister(struct snd_device *device);
 
 static struct snd_pcm *snd_pcm_search(struct snd_card *card, int device)
 {
@@ -494,19 +493,13 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
 static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
 {
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       if (pstr->proc_xrun_debug_entry) {
-               snd_info_unregister(pstr->proc_xrun_debug_entry);
-               pstr->proc_xrun_debug_entry = NULL;
-       }
+       snd_info_free_entry(pstr->proc_xrun_debug_entry);
+       pstr->proc_xrun_debug_entry = NULL;
 #endif
-       if (pstr->proc_info_entry) {
-               snd_info_unregister(pstr->proc_info_entry);
-               pstr->proc_info_entry = NULL;
-       }
-       if (pstr->proc_root) {
-               snd_info_unregister(pstr->proc_root);
-               pstr->proc_root = NULL;
-       }
+       snd_info_free_entry(pstr->proc_info_entry);
+       pstr->proc_info_entry = NULL;
+       snd_info_free_entry(pstr->proc_root);
+       pstr->proc_root = NULL;
        return 0;
 }
 
@@ -570,29 +563,19 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
 
        return 0;
 }
-               
+
 static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
 {
-       if (substream->proc_info_entry) {
-               snd_info_unregister(substream->proc_info_entry);
-               substream->proc_info_entry = NULL;
-       }
-       if (substream->proc_hw_params_entry) {
-               snd_info_unregister(substream->proc_hw_params_entry);
-               substream->proc_hw_params_entry = NULL;
-       }
-       if (substream->proc_sw_params_entry) {
-               snd_info_unregister(substream->proc_sw_params_entry);
-               substream->proc_sw_params_entry = NULL;
-       }
-       if (substream->proc_status_entry) {
-               snd_info_unregister(substream->proc_status_entry);
-               substream->proc_status_entry = NULL;
-       }
-       if (substream->proc_root) {
-               snd_info_unregister(substream->proc_root);
-               substream->proc_root = NULL;
-       }
+       snd_info_free_entry(substream->proc_info_entry);
+       substream->proc_info_entry = NULL;
+       snd_info_free_entry(substream->proc_hw_params_entry);
+       substream->proc_hw_params_entry = NULL;
+       snd_info_free_entry(substream->proc_sw_params_entry);
+       substream->proc_sw_params_entry = NULL;
+       snd_info_free_entry(substream->proc_status_entry);
+       substream->proc_status_entry = NULL;
+       snd_info_free_entry(substream->proc_root);
+       substream->proc_root = NULL;
        return 0;
 }
 #else /* !CONFIG_SND_VERBOSE_PROCFS */
@@ -696,7 +679,6 @@ int snd_pcm_new(struct snd_card *card, char *id, int device,
                .dev_free = snd_pcm_dev_free,
                .dev_register = snd_pcm_dev_register,
                .dev_disconnect = snd_pcm_dev_disconnect,
-               .dev_unregister = snd_pcm_dev_unregister
        };
 
        snd_assert(rpcm != NULL, return -EINVAL);
@@ -740,6 +722,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
        substream = pstr->substream;
        while (substream) {
                substream_next = substream->next;
+               snd_pcm_timer_done(substream);
                snd_pcm_substream_proc_done(substream);
                kfree(substream);
                substream = substream_next;
@@ -756,7 +739,12 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
 
 static int snd_pcm_free(struct snd_pcm *pcm)
 {
+       struct snd_pcm_notify *notify;
+
        snd_assert(pcm != NULL, return -ENXIO);
+       list_for_each_entry(notify, &snd_pcm_notify_list, list) {
+               notify->n_unregister(pcm);
+       }
        if (pcm->private_free)
                pcm->private_free(pcm);
        snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -804,7 +792,8 @@ int snd_pcm_attach_substream(struct snd_pcm *pcm, int stream,
                kctl = snd_ctl_file(list);
                if (kctl->pid == current->pid) {
                        prefer_subdevice = kctl->prefer_pcm_subdevice;
-                       break;
+                       if (prefer_subdevice != -1)
+                               break;
                }
        }
        up_read(&card->controls_rwsem);
@@ -918,6 +907,28 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
        substream->pstr->substream_opened--;
 }
 
+static ssize_t show_pcm_class(struct class_device *class_device, char *buf)
+{
+       struct snd_pcm *pcm;
+       const char *str;
+       static const char *strs[SNDRV_PCM_CLASS_LAST + 1] = {
+               [SNDRV_PCM_CLASS_GENERIC] = "generic",
+               [SNDRV_PCM_CLASS_MULTI] = "multi",
+               [SNDRV_PCM_CLASS_MODEM] = "modem",
+               [SNDRV_PCM_CLASS_DIGITIZER] = "digitizer",
+       };
+
+       if (! (pcm = class_get_devdata(class_device)) ||
+           pcm->dev_class > SNDRV_PCM_CLASS_LAST)
+               str = "none";
+       else
+               str = strs[pcm->dev_class];
+        return snprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+static struct class_device_attribute pcm_attrs =
+       __ATTR(pcm_class, S_IRUGO, show_pcm_class, NULL);
+
 static int snd_pcm_dev_register(struct snd_device *device)
 {
        int cidx, err;
@@ -956,6 +967,8 @@ static int snd_pcm_dev_register(struct snd_device *device)
                        mutex_unlock(&register_mutex);
                        return err;
                }
+               snd_add_device_sysfs_file(devtype, pcm->card, pcm->device,
+                                         &pcm_attrs);
                for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
                        snd_pcm_timer_init(substream);
        }
@@ -971,35 +984,22 @@ static int snd_pcm_dev_register(struct snd_device *device)
 static int snd_pcm_dev_disconnect(struct snd_device *device)
 {
        struct snd_pcm *pcm = device->device_data;
-       struct list_head *list;
+       struct snd_pcm_notify *notify;
        struct snd_pcm_substream *substream;
-       int cidx;
+       int cidx, devtype;
 
        mutex_lock(&register_mutex);
+       if (list_empty(&pcm->list))
+               goto unlock;
+
        list_del_init(&pcm->list);
        for (cidx = 0; cidx < 2; cidx++)
                for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
                        if (substream->runtime)
                                substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED;
-       list_for_each(list, &snd_pcm_notify_list) {
-               struct snd_pcm_notify *notify;
-               notify = list_entry(list, struct snd_pcm_notify, list);
+       list_for_each_entry(notify, &snd_pcm_notify_list, list) {
                notify->n_disconnect(pcm);
        }
-       mutex_unlock(&register_mutex);
-       return 0;
-}
-
-static int snd_pcm_dev_unregister(struct snd_device *device)
-{
-       int cidx, devtype;
-       struct snd_pcm_substream *substream;
-       struct list_head *list;
-       struct snd_pcm *pcm = device->device_data;
-
-       snd_assert(pcm != NULL, return -ENXIO);
-       mutex_lock(&register_mutex);
-       list_del(&pcm->list);
        for (cidx = 0; cidx < 2; cidx++) {
                devtype = -1;
                switch (cidx) {
@@ -1011,23 +1011,20 @@ static int snd_pcm_dev_unregister(struct snd_device *device)
                        break;
                }
                snd_unregister_device(devtype, pcm->card, pcm->device);
-               for (substream = pcm->streams[cidx].substream; substream; substream = substream->next)
-                       snd_pcm_timer_done(substream);
-       }
-       list_for_each(list, &snd_pcm_notify_list) {
-               struct snd_pcm_notify *notify;
-               notify = list_entry(list, struct snd_pcm_notify, list);
-               notify->n_unregister(pcm);
        }
+ unlock:
        mutex_unlock(&register_mutex);
-       return snd_pcm_free(pcm);
+       return 0;
 }
 
 int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
 {
        struct list_head *p;
 
-       snd_assert(notify != NULL && notify->n_register != NULL && notify->n_unregister != NULL, return -EINVAL);
+       snd_assert(notify != NULL &&
+                  notify->n_register != NULL &&
+                  notify->n_unregister != NULL &&
+                  notify->n_disconnect, return -EINVAL);
        mutex_lock(&register_mutex);
        if (nfree) {
                list_del(&notify->list);
@@ -1090,8 +1087,7 @@ static void snd_pcm_proc_init(void)
 
 static void snd_pcm_proc_done(void)
 {
-       if (snd_pcm_proc_entry)
-               snd_info_unregister(snd_pcm_proc_entry);
+       snd_info_free_entry(snd_pcm_proc_entry);
 }
 
 #else /* !CONFIG_PROC_FS */
index 2b8aab6fd6cd312365a658c2aefd074a7768df64..2b539799d23b7a88a10f1583281b7f2263b55786 100644 (file)
@@ -478,7 +478,7 @@ static long snd_pcm_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
         * mmap of PCM status/control records because of the size
         * incompatibility.
         */
-       substream->no_mmap_ctrl = 1;
+       pcm_file->no_compat_mmap = 1;
 
        switch (cmd) {
        case SNDRV_PCM_IOCTL_PVERSION:
index 067d2056db9a65569580945f3e9d1529de32dce0..be030cb4d373207751fa516ea1b13534b8d143c4 100644 (file)
@@ -101,7 +101,7 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
 {
        snd_pcm_lib_preallocate_dma_free(substream);
 #ifdef CONFIG_SND_VERBOSE_PROCFS
-       snd_info_unregister(substream->proc_prealloc_entry);
+       snd_info_free_entry(substream->proc_prealloc_entry);
        substream->proc_prealloc_entry = NULL;
 #endif
        return 0;
index 439f047929e18e509961214878ffd36233d71371..0224c70414f516a9666966f38c4b83aa0d5e44e4 100644 (file)
@@ -1992,35 +1992,9 @@ int snd_pcm_hw_constraints_complete(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static void snd_pcm_add_file(struct snd_pcm_str *str,
-                            struct snd_pcm_file *pcm_file)
-{
-       pcm_file->next = str->files;
-       str->files = pcm_file;
-}
-
-static void snd_pcm_remove_file(struct snd_pcm_str *str,
-                               struct snd_pcm_file *pcm_file)
-{
-       struct snd_pcm_file * pcm_file1;
-       if (str->files == pcm_file) {
-               str->files = pcm_file->next;
-       } else {
-               pcm_file1 = str->files;
-               while (pcm_file1 && pcm_file1->next != pcm_file)
-                       pcm_file1 = pcm_file1->next;
-               if (pcm_file1 != NULL)
-                       pcm_file1->next = pcm_file->next;
-       }
-}
-
 static void pcm_release_private(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_file *pcm_file = substream->file;
-
        snd_pcm_unlink(substream);
-       snd_pcm_remove_file(substream->pstr, pcm_file);
-       kfree(pcm_file);
 }
 
 void snd_pcm_release_substream(struct snd_pcm_substream *substream)
@@ -2060,7 +2034,6 @@ int snd_pcm_open_substream(struct snd_pcm *pcm, int stream,
                return 0;
        }
 
-       substream->no_mmap_ctrl = 0;
        err = snd_pcm_hw_constraints_init(substream);
        if (err < 0) {
                snd_printd("snd_pcm_hw_constraints_init failed\n");
@@ -2105,19 +2078,16 @@ static int snd_pcm_open_file(struct file *file,
        if (err < 0)
                return err;
 
-       if (substream->ref_count > 1)
-               pcm_file = substream->file;
-       else {
-               pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
-               if (pcm_file == NULL) {
-                       snd_pcm_release_substream(substream);
-                       return -ENOMEM;
-               }
+       pcm_file = kzalloc(sizeof(*pcm_file), GFP_KERNEL);
+       if (pcm_file == NULL) {
+               snd_pcm_release_substream(substream);
+               return -ENOMEM;
+       }
+       pcm_file->substream = substream;
+       if (substream->ref_count == 1) {
                str = substream->pstr;
                substream->file = pcm_file;
                substream->pcm_release = pcm_release_private;
-               pcm_file->substream = substream;
-               snd_pcm_add_file(str, pcm_file);
        }
        file->private_data = pcm_file;
        *rpcm_file = pcm_file;
@@ -2209,6 +2179,7 @@ static int snd_pcm_release(struct inode *inode, struct file *file)
        fasync_helper(-1, file, 0, &substream->runtime->fasync);
        mutex_lock(&pcm->open_mutex);
        snd_pcm_release_substream(substream);
+       kfree(pcm_file);
        mutex_unlock(&pcm->open_mutex);
        wake_up(&pcm->open_wait);
        module_put(pcm->card->module);
@@ -3270,11 +3241,11 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area)
        offset = area->vm_pgoff << PAGE_SHIFT;
        switch (offset) {
        case SNDRV_PCM_MMAP_OFFSET_STATUS:
-               if (substream->no_mmap_ctrl)
+               if (pcm_file->no_compat_mmap)
                        return -ENXIO;
                return snd_pcm_mmap_status(substream, file, area);
        case SNDRV_PCM_MMAP_OFFSET_CONTROL:
-               if (substream->no_mmap_ctrl)
+               if (pcm_file->no_compat_mmap)
                        return -ENXIO;
                return snd_pcm_mmap_control(substream, file, area);
        default:
index 8c15c66eb4aa3a630803d12dfa9f7d34caca818b..269c467ca9bb2ba76f3c001f1d824414ccb5bd07 100644 (file)
@@ -55,7 +55,6 @@ static int snd_rawmidi_free(struct snd_rawmidi *rawmidi);
 static int snd_rawmidi_dev_free(struct snd_device *device);
 static int snd_rawmidi_dev_register(struct snd_device *device);
 static int snd_rawmidi_dev_disconnect(struct snd_device *device);
-static int snd_rawmidi_dev_unregister(struct snd_device *device);
 
 static LIST_HEAD(snd_rawmidi_devices);
 static DEFINE_MUTEX(register_mutex);
@@ -431,7 +430,8 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
                        kctl = snd_ctl_file(list);
                        if (kctl->pid == current->pid) {
                                subdevice = kctl->prefer_rawmidi_subdevice;
-                               break;
+                               if (subdevice != -1)
+                                       break;
                        }
                }
                up_read(&card->controls_rwsem);
@@ -1426,7 +1426,6 @@ int snd_rawmidi_new(struct snd_card *card, char *id, int device,
                .dev_free = snd_rawmidi_dev_free,
                .dev_register = snd_rawmidi_dev_register,
                .dev_disconnect = snd_rawmidi_dev_disconnect,
-               .dev_unregister = snd_rawmidi_dev_unregister
        };
 
        snd_assert(rrawmidi != NULL, return -EINVAL);
@@ -1479,6 +1478,14 @@ static void snd_rawmidi_free_substreams(struct snd_rawmidi_str *stream)
 static int snd_rawmidi_free(struct snd_rawmidi *rmidi)
 {
        snd_assert(rmidi != NULL, return -ENXIO);       
+
+       snd_info_free_entry(rmidi->proc_entry);
+       rmidi->proc_entry = NULL;
+       mutex_lock(&register_mutex);
+       if (rmidi->ops && rmidi->ops->dev_unregister)
+               rmidi->ops->dev_unregister(rmidi);
+       mutex_unlock(&register_mutex);
+
        snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT]);
        snd_rawmidi_free_substreams(&rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT]);
        if (rmidi->private_free)
@@ -1587,21 +1594,6 @@ static int snd_rawmidi_dev_disconnect(struct snd_device *device)
 
        mutex_lock(&register_mutex);
        list_del_init(&rmidi->list);
-       mutex_unlock(&register_mutex);
-       return 0;
-}
-
-static int snd_rawmidi_dev_unregister(struct snd_device *device)
-{
-       struct snd_rawmidi *rmidi = device->device_data;
-
-       snd_assert(rmidi != NULL, return -ENXIO);
-       mutex_lock(&register_mutex);
-       list_del(&rmidi->list);
-       if (rmidi->proc_entry) {
-               snd_info_unregister(rmidi->proc_entry);
-               rmidi->proc_entry = NULL;
-       }
 #ifdef CONFIG_SND_OSSEMUL
        if (rmidi->ossreg) {
                if ((int)rmidi->device == midi_map[rmidi->card->number]) {
@@ -1615,17 +1607,9 @@ static int snd_rawmidi_dev_unregister(struct snd_device *device)
                rmidi->ossreg = 0;
        }
 #endif /* CONFIG_SND_OSSEMUL */
-       if (rmidi->ops && rmidi->ops->dev_unregister)
-               rmidi->ops->dev_unregister(rmidi);
        snd_unregister_device(SNDRV_DEVICE_TYPE_RAWMIDI, rmidi->card, rmidi->device);
        mutex_unlock(&register_mutex);
-#if defined(CONFIG_SND_SEQUENCER) || (defined(MODULE) && defined(CONFIG_SND_SEQUENCER_MODULE))
-       if (rmidi->seq_dev) {
-               snd_device_free(rmidi->card, rmidi->seq_dev);
-               rmidi->seq_dev = NULL;
-       }
-#endif
-       return snd_rawmidi_free(rmidi);
+       return 0;
 }
 
 /**
index 84704ccb1829385416f28d7dbcc0a3ad2a5d5e9e..412dd62b654ec2907b3108df5415b0697032e29c 100644 (file)
@@ -156,7 +156,7 @@ static int __init rtctimer_init(void)
 static void __exit rtctimer_exit(void)
 {
        if (rtctimer) {
-               snd_timer_global_unregister(rtctimer);
+               snd_timer_global_free(rtctimer);
                rtctimer = NULL;
        }
 }
index e7234135641cd9ee97762ffe5b076f290ec04b35..92858cf8b6eb09e4b71b951698375e20a6a9efb2 100644 (file)
@@ -303,8 +303,7 @@ register_proc(void)
 static void
 unregister_proc(void)
 {
-       if (info_entry)
-               snd_info_unregister(info_entry);
+       snd_info_free_entry(info_entry);
        info_entry = NULL;
 }
 #endif /* CONFIG_PROC_FS */
index 102ff548ce6931df6ec5e946c5df04138cb63734..b79d011813c026436b9b8c311daec32962d48ac6 100644 (file)
@@ -90,7 +90,6 @@ static int snd_seq_device_free(struct snd_seq_device *dev);
 static int snd_seq_device_dev_free(struct snd_device *device);
 static int snd_seq_device_dev_register(struct snd_device *device);
 static int snd_seq_device_dev_disconnect(struct snd_device *device);
-static int snd_seq_device_dev_unregister(struct snd_device *device);
 
 static int init_device(struct snd_seq_device *dev, struct ops_list *ops);
 static int free_device(struct snd_seq_device *dev, struct ops_list *ops);
@@ -189,7 +188,6 @@ int snd_seq_device_new(struct snd_card *card, int device, char *id, int argsize,
                .dev_free = snd_seq_device_dev_free,
                .dev_register = snd_seq_device_dev_register,
                .dev_disconnect = snd_seq_device_dev_disconnect,
-               .dev_unregister = snd_seq_device_dev_unregister
        };
 
        if (result)
@@ -308,15 +306,6 @@ static int snd_seq_device_dev_disconnect(struct snd_device *device)
        return 0;
 }
 
-/*
- * unregister the existing device
- */
-static int snd_seq_device_dev_unregister(struct snd_device *device)
-{
-       struct snd_seq_device *dev = device->device_data;
-       return snd_seq_device_free(dev);
-}
-
 /*
  * register device driver
  * id = driver id
@@ -573,7 +562,7 @@ static void __exit alsa_seq_device_exit(void)
 {
        remove_drivers();
 #ifdef CONFIG_PROC_FS
-       snd_info_unregister(info_entry);
+       snd_info_free_entry(info_entry);
 #endif
        if (num_ops)
                snd_printk(KERN_ERR "drivers not released (%d)\n", num_ops);
index 142e9e6882c9a575e4efa7f2092afab3e20cbcf1..8a7fe5cca1c90022fa765fd025108c2b37331532 100644 (file)
@@ -64,9 +64,9 @@ int __init snd_seq_info_init(void)
 
 int __exit snd_seq_info_done(void)
 {
-       snd_info_unregister(queues_entry);
-       snd_info_unregister(clients_entry);
-       snd_info_unregister(timer_entry);
+       snd_info_free_entry(queues_entry);
+       snd_info_free_entry(clients_entry);
+       snd_info_free_entry(timer_entry);
        return 0;
 }
 #endif
index 7edd1fc58b1747c20c7f8c65b7815053899949d5..efa476c5210ac52832d8e7154984d42717d8588b 100644 (file)
@@ -268,7 +268,11 @@ int snd_register_device(int type, struct snd_card *card, int dev,
        snd_minors[minor] = preg;
        if (card)
                device = card->dev;
-       class_device_create(sound_class, NULL, MKDEV(major, minor), device, "%s", name);
+       preg->class_dev = class_device_create(sound_class, NULL,
+                                             MKDEV(major, minor),
+                                             device, "%s", name);
+       if (preg->class_dev)
+               class_set_devdata(preg->class_dev, private_data);
 
        mutex_unlock(&sound_mutex);
        return 0;
@@ -276,6 +280,24 @@ int snd_register_device(int type, struct snd_card *card, int dev,
 
 EXPORT_SYMBOL(snd_register_device);
 
+/* find the matching minor record
+ * return the index of snd_minor, or -1 if not found
+ */
+static int find_snd_minor(int type, struct snd_card *card, int dev)
+{
+       int cardnum, minor;
+       struct snd_minor *mptr;
+
+       cardnum = card ? card->number : -1;
+       for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
+               if ((mptr = snd_minors[minor]) != NULL &&
+                   mptr->type == type &&
+                   mptr->card == cardnum &&
+                   mptr->device == dev)
+                       return minor;
+       return -1;
+}
+
 /**
  * snd_unregister_device - unregister the device on the given card
  * @type: the device type, SNDRV_DEVICE_TYPE_XXX
@@ -289,32 +311,42 @@ EXPORT_SYMBOL(snd_register_device);
  */
 int snd_unregister_device(int type, struct snd_card *card, int dev)
 {
-       int cardnum, minor;
-       struct snd_minor *mptr;
+       int minor;
 
-       cardnum = card ? card->number : -1;
        mutex_lock(&sound_mutex);
-       for (minor = 0; minor < ARRAY_SIZE(snd_minors); ++minor)
-               if ((mptr = snd_minors[minor]) != NULL &&
-                   mptr->type == type &&
-                   mptr->card == cardnum &&
-                   mptr->device == dev)
-                       break;
-       if (minor == ARRAY_SIZE(snd_minors)) {
+       minor = find_snd_minor(type, card, dev);
+       if (minor < 0) {
                mutex_unlock(&sound_mutex);
                return -EINVAL;
        }
 
        class_device_destroy(sound_class, MKDEV(major, minor));
 
+       kfree(snd_minors[minor]);
        snd_minors[minor] = NULL;
        mutex_unlock(&sound_mutex);
-       kfree(mptr);
        return 0;
 }
 
 EXPORT_SYMBOL(snd_unregister_device);
 
+int snd_add_device_sysfs_file(int type, struct snd_card *card, int dev,
+                             const struct class_device_attribute *attr)
+{
+       int minor, ret = -EINVAL;
+       struct class_device *cdev;
+
+       mutex_lock(&sound_mutex);
+       minor = find_snd_minor(type, card, dev);
+       if (minor >= 0 && (cdev = snd_minors[minor]->class_dev) != NULL)
+               ret = class_device_create_file(cdev, attr);
+       mutex_unlock(&sound_mutex);
+       return ret;
+
+}
+
+EXPORT_SYMBOL(snd_add_device_sysfs_file);
+
 #ifdef CONFIG_PROC_FS
 /*
  *  INFO PART
@@ -387,8 +419,7 @@ int __init snd_minor_info_init(void)
 
 int __exit snd_minor_info_done(void)
 {
-       if (snd_minor_info_entry)
-               snd_info_unregister(snd_minor_info_entry);
+       snd_info_free_entry(snd_minor_info_entry);
        return 0;
 }
 #endif /* CONFIG_PROC_FS */
index 74f0fe5a1ba086c3aa5b7d531973eacfd5ecab24..b2fc40aa520b93e69a30fb918d3552db3ba94568 100644 (file)
@@ -270,8 +270,7 @@ int __init snd_minor_info_oss_init(void)
 
 int __exit snd_minor_info_oss_done(void)
 {
-       if (snd_minor_info_oss_entry)
-               snd_info_unregister(snd_minor_info_oss_entry);
+       snd_info_free_entry(snd_minor_info_oss_entry);
        return 0;
 }
 #endif /* CONFIG_PROC_FS */
index 0a984e881c1089ee68ac4ca11c8ad98a82d0902e..10a79aed33f86bcdc492509519cfcc41b11114bc 100644 (file)
@@ -88,7 +88,7 @@ static DEFINE_MUTEX(register_mutex);
 static int snd_timer_free(struct snd_timer *timer);
 static int snd_timer_dev_free(struct snd_device *device);
 static int snd_timer_dev_register(struct snd_device *device);
-static int snd_timer_dev_unregister(struct snd_device *device);
+static int snd_timer_dev_disconnect(struct snd_device *device);
 
 static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
 
@@ -718,7 +718,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
                }
        }
        if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
-               snd_timer_reschedule(timer, ticks_left);
+               snd_timer_reschedule(timer, timer->sticks);
        if (timer->running) {
                if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
                        timer->hw.stop(timer);
@@ -773,7 +773,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
        static struct snd_device_ops ops = {
                .dev_free = snd_timer_dev_free,
                .dev_register = snd_timer_dev_register,
-               .dev_unregister = snd_timer_dev_unregister
+               .dev_disconnect = snd_timer_dev_disconnect,
        };
 
        snd_assert(tid != NULL, return -EINVAL);
@@ -813,6 +813,21 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
 static int snd_timer_free(struct snd_timer *timer)
 {
        snd_assert(timer != NULL, return -ENXIO);
+
+       mutex_lock(&register_mutex);
+       if (! list_empty(&timer->open_list_head)) {
+               struct list_head *p, *n;
+               struct snd_timer_instance *ti;
+               snd_printk(KERN_WARNING "timer %p is busy?\n", timer);
+               list_for_each_safe(p, n, &timer->open_list_head) {
+                       list_del_init(p);
+                       ti = list_entry(p, struct snd_timer_instance, open_list);
+                       ti->timer = NULL;
+               }
+       }
+       list_del(&timer->device_list);
+       mutex_unlock(&register_mutex);
+
        if (timer->private_free)
                timer->private_free(timer);
        kfree(timer);
@@ -867,30 +882,13 @@ static int snd_timer_dev_register(struct snd_device *dev)
        return 0;
 }
 
-static int snd_timer_unregister(struct snd_timer *timer)
+static int snd_timer_dev_disconnect(struct snd_device *device)
 {
-       struct list_head *p, *n;
-       struct snd_timer_instance *ti;
-
-       snd_assert(timer != NULL, return -ENXIO);
+       struct snd_timer *timer = device->device_data;
        mutex_lock(&register_mutex);
-       if (! list_empty(&timer->open_list_head)) {
-               snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer);
-               list_for_each_safe(p, n, &timer->open_list_head) {
-                       list_del_init(p);
-                       ti = list_entry(p, struct snd_timer_instance, open_list);
-                       ti->timer = NULL;
-               }
-       }
-       list_del(&timer->device_list);
+       list_del_init(&timer->device_list);
        mutex_unlock(&register_mutex);
-       return snd_timer_free(timer);
-}
-
-static int snd_timer_dev_unregister(struct snd_device *device)
-{
-       struct snd_timer *timer = device->device_data;
-       return snd_timer_unregister(timer);
+       return 0;
 }
 
 void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
@@ -955,18 +953,12 @@ int snd_timer_global_register(struct snd_timer *timer)
        return snd_timer_dev_register(&dev);
 }
 
-int snd_timer_global_unregister(struct snd_timer *timer)
-{
-       return snd_timer_unregister(timer);
-}
-
 /*
  *  System timer
  */
 
 struct snd_timer_system_private {
        struct timer_list tlist;
-       struct timer * timer;
        unsigned long last_expires;
        unsigned long last_jiffies;
        unsigned long correction;
@@ -978,7 +970,7 @@ static void snd_timer_s_function(unsigned long data)
        struct snd_timer_system_private *priv = timer->private_data;
        unsigned long jiff = jiffies;
        if (time_after(jiff, priv->last_expires))
-               priv->correction = (long)jiff - (long)priv->last_expires;
+               priv->correction += (long)jiff - (long)priv->last_expires;
        snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
 }
 
@@ -994,7 +986,7 @@ static int snd_timer_s_start(struct snd_timer * timer)
                njiff++;
        } else {
                njiff += timer->sticks - priv->correction;
-               priv->correction -= timer->sticks;
+               priv->correction = 0;
        }
        priv->last_expires = priv->tlist.expires = njiff;
        add_timer(&priv->tlist);
@@ -1013,6 +1005,7 @@ static int snd_timer_s_stop(struct snd_timer * timer)
                timer->sticks = priv->last_expires - jiff;
        else
                timer->sticks = 1;
+       priv->correction = 0;
        return 0;
 }
 
@@ -1126,7 +1119,7 @@ static void __init snd_timer_proc_init(void)
 
 static void __exit snd_timer_proc_done(void)
 {
-       snd_info_unregister(snd_timer_proc_entry);
+       snd_info_free_entry(snd_timer_proc_entry);
 }
 #else /* !CONFIG_PROC_FS */
 #define snd_timer_proc_init()
@@ -1982,7 +1975,7 @@ static void __exit alsa_timer_exit(void)
        /* unregister the system timer */
        list_for_each_safe(p, n, &snd_timer_list) {
                struct snd_timer *timer = list_entry(p, struct snd_timer, device_list);
-               snd_timer_unregister(timer);
+               snd_timer_free(timer);
        }
        snd_timer_proc_done();
 #ifdef SNDRV_OSS_INFO_DEV_TIMERS
@@ -2005,5 +1998,4 @@ EXPORT_SYMBOL(snd_timer_notify);
 EXPORT_SYMBOL(snd_timer_global_new);
 EXPORT_SYMBOL(snd_timer_global_free);
 EXPORT_SYMBOL(snd_timer_global_register);
-EXPORT_SYMBOL(snd_timer_global_unregister);
 EXPORT_SYMBOL(snd_timer_interrupt);
index 395c4ef52ac9b04e7d5e288090d6153182353984..7971285dfd5b9a75ce796c927df888072af482d6 100644 (file)
@@ -73,6 +73,19 @@ config SND_MTPAV
          To compile this driver as a module, choose M here: the module
          will be called snd-mtpav.
 
+config SND_MTS64
+       tristate "ESI Miditerminal 4140 driver"
+       depends on SND && PARPORT
+       select SND_RAWMIDI
+       help
+         The ESI Miditerminal 4140 is a 4 In 4 Out MIDI Interface with 
+          additional SMPTE Timecode capabilities for the parallel port.
+
+         Say 'Y' to include support for this device.
+
+         To compile this driver as a module, chose 'M' here: the module 
+          will be called snd-mts64.
+
 config SND_SERIAL_U16550
        tristate "UART16550 serial MIDI driver"
        depends on SND
index cb98c3d662be5e048f3b5f8183dfc62923e62a5f..c9bad6d67e73a94c0b499c2abc94ae316cd5fc13 100644 (file)
@@ -5,6 +5,7 @@
 
 snd-dummy-objs := dummy.o
 snd-mtpav-objs := mtpav.o
+snd-mts64-objs := mts64.o
 snd-serial-u16550-objs := serial-u16550.o
 snd-virmidi-objs := virmidi.o
 
@@ -13,5 +14,6 @@ obj-$(CONFIG_SND_DUMMY) += snd-dummy.o
 obj-$(CONFIG_SND_VIRMIDI) += snd-virmidi.o
 obj-$(CONFIG_SND_SERIAL_U16550) += snd-serial-u16550.o
 obj-$(CONFIG_SND_MTPAV) += snd-mtpav.o
+obj-$(CONFIG_SND_MTS64) += snd-mts64.o
 
 obj-$(CONFIG_SND) += opl3/ opl4/ mpu401/ vx/
index ffeafaf2eccae64956ac2bfd8034257fbb10b92c..42001efa9f3ec04db9127c71c685624c9b75f48b 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/moduleparam.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
 #include <sound/initval.h>
@@ -285,7 +286,7 @@ static struct snd_pcm_hardware snd_card_dummy_playback =
        .channels_max =         USE_CHANNELS_MAX,
        .buffer_bytes_max =     MAX_BUFFER_SIZE,
        .period_bytes_min =     64,
-       .period_bytes_max =     MAX_BUFFER_SIZE,
+       .period_bytes_max =     MAX_PERIOD_SIZE,
        .periods_min =          USE_PERIODS_MIN,
        .periods_max =          USE_PERIODS_MAX,
        .fifo_size =            0,
@@ -443,10 +444,13 @@ static int __init snd_card_dummy_pcm(struct snd_dummy *dummy, int device, int su
 }
 
 #define DUMMY_VOLUME(xname, xindex, addr) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .index = xindex, \
   .info = snd_dummy_volume_info, \
   .get = snd_dummy_volume_get, .put = snd_dummy_volume_put, \
-  .private_value = addr }
+  .private_value = addr, \
+  .tlv = { .p = db_scale_dummy } }
 
 static int snd_dummy_volume_info(struct snd_kcontrol *kcontrol,
                                 struct snd_ctl_elem_info *uinfo)
@@ -497,6 +501,8 @@ static int snd_dummy_volume_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_dummy, -4500, 30, 0);
+
 #define DUMMY_CAPSRC(xname, xindex, addr) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \
   .info = snd_dummy_capsrc_info, \
@@ -547,13 +553,13 @@ static struct snd_kcontrol_new snd_dummy_controls[] = {
 DUMMY_VOLUME("Master Volume", 0, MIXER_ADDR_MASTER),
 DUMMY_CAPSRC("Master Capture Switch", 0, MIXER_ADDR_MASTER),
 DUMMY_VOLUME("Synth Volume", 0, MIXER_ADDR_SYNTH),
-DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Synth Capture Switch", 0, MIXER_ADDR_SYNTH),
 DUMMY_VOLUME("Line Volume", 0, MIXER_ADDR_LINE),
-DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Line Capture Switch", 0, MIXER_ADDR_LINE),
 DUMMY_VOLUME("Mic Volume", 0, MIXER_ADDR_MIC),
-DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MASTER),
+DUMMY_CAPSRC("Mic Capture Switch", 0, MIXER_ADDR_MIC),
 DUMMY_VOLUME("CD Volume", 0, MIXER_ADDR_CD),
-DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_MASTER)
+DUMMY_CAPSRC("CD Capture Switch", 0, MIXER_ADDR_CD)
 };
 
 static int __init snd_card_dummy_new_mixer(struct snd_dummy *dummy)
index 17cc105b26fcf68b43999d85379f043f2a990ec6..2de181ad0b0506719626a9f74396f45411f3785f 100644 (file)
@@ -211,7 +211,7 @@ static void __devexit snd_mpu401_pnp_remove(struct pnp_dev *dev)
        struct snd_card *card = (struct snd_card *) pnp_get_drvdata(dev);
 
        snd_card_disconnect(card);
-       snd_card_free_in_thread(card);
+       snd_card_free_when_closed(card);
 }
 
 static struct pnp_driver snd_mpu401_pnp_driver = {
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
new file mode 100644 (file)
index 0000000..1699873
--- /dev/null
@@ -0,0 +1,1091 @@
+/*     
+ *   ALSA Driver for Ego Systems Inc. (ESI) Miditerminal 4140
+ *   Copyright (c) 2006 by Matthias K├Ânig <mk@phasorlab.de>
+ *
+ *   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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include <sound/driver.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/parport.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+#include <sound/control.h>
+
+#define CARD_NAME "Miditerminal 4140"
+#define DRIVER_NAME "MTS64"
+#define PLATFORM_DRIVER "snd_mts64"
+
+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 struct platform_device *platform_devices[SNDRV_CARDS]; 
+static int device_count;
+
+module_param_array(index, int, NULL, S_IRUGO);
+MODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard.");
+module_param_array(id, charp, NULL, S_IRUGO);
+MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
+module_param_array(enable, bool, NULL, S_IRUGO);
+MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
+
+MODULE_AUTHOR("Matthias Koenig <mk@phasorlab.de>");
+MODULE_DESCRIPTION("ESI Miditerminal 4140");
+MODULE_LICENSE("GPL");
+MODULE_SUPPORTED_DEVICE("{{ESI,Miditerminal 4140}}");
+
+/*********************************************************************
+ * Chip specific
+ *********************************************************************/
+#define MTS64_NUM_INPUT_PORTS 5
+#define MTS64_NUM_OUTPUT_PORTS 4
+#define MTS64_SMPTE_SUBSTREAM 4
+
+struct mts64 {
+       spinlock_t lock;
+       struct snd_card *card;
+       struct snd_rawmidi *rmidi;
+       struct pardevice *pardev;
+       int pardev_claimed;
+
+       int open_count;
+       int current_midi_output_port;
+       int current_midi_input_port;
+       u8 mode[MTS64_NUM_INPUT_PORTS];
+       struct snd_rawmidi_substream *midi_input_substream[MTS64_NUM_INPUT_PORTS];
+       int smpte_switch;
+       u8 time[4]; /* [0]=hh, [1]=mm, [2]=ss, [3]=ff */
+       u8 fps;
+};
+
+static int snd_mts64_free(struct mts64 *mts)
+{
+       kfree(mts);
+       return 0;
+}
+
+static int __devinit snd_mts64_create(struct snd_card *card, 
+                                     struct pardevice *pardev, 
+                                     struct mts64 **rchip)
+{
+       struct mts64 *mts;
+
+       *rchip = NULL;
+
+       mts = kzalloc(sizeof(struct mts64), GFP_KERNEL);
+       if (mts == NULL) 
+               return -ENOMEM;
+
+       /* Init chip specific data */
+       spin_lock_init(&mts->lock);
+       mts->card = card;
+       mts->pardev = pardev;
+       mts->current_midi_output_port = -1;
+       mts->current_midi_input_port = -1;
+
+       *rchip = mts;
+
+       return 0;
+}
+
+/*********************************************************************
+ * HW register related constants
+ *********************************************************************/
+
+/* Status Bits */
+#define MTS64_STAT_BSY             0x80
+#define MTS64_STAT_BIT_SET         0x20  /* readout process, bit is set */
+#define MTS64_STAT_PORT            0x10  /* read byte is a port number */
+
+/* Control Bits */
+#define MTS64_CTL_READOUT          0x08  /* enable readout */
+#define MTS64_CTL_WRITE_CMD        0x06  
+#define MTS64_CTL_WRITE_DATA       0x02  
+#define MTS64_CTL_STROBE           0x01  
+
+/* Command */
+#define MTS64_CMD_RESET            0xfe
+#define MTS64_CMD_PROBE            0x8f  /* Used in probing procedure */
+#define MTS64_CMD_SMPTE_SET_TIME   0xe8
+#define MTS64_CMD_SMPTE_SET_FPS    0xee
+#define MTS64_CMD_SMPTE_STOP       0xef
+#define MTS64_CMD_SMPTE_FPS_24     0xe3
+#define MTS64_CMD_SMPTE_FPS_25     0xe2
+#define MTS64_CMD_SMPTE_FPS_2997   0xe4 
+#define MTS64_CMD_SMPTE_FPS_30D    0xe1
+#define MTS64_CMD_SMPTE_FPS_30     0xe0
+#define MTS64_CMD_COM_OPEN         0xf8  /* setting the communication mode */
+#define MTS64_CMD_COM_CLOSE1       0xff  /* clearing communication mode */
+#define MTS64_CMD_COM_CLOSE2       0xf5
+
+/*********************************************************************
+ * Hardware specific functions
+ *********************************************************************/
+static void mts64_enable_readout(struct parport *p);
+static void mts64_disable_readout(struct parport *p);
+static int mts64_device_ready(struct parport *p);
+static int mts64_device_init(struct parport *p);
+static int mts64_device_open(struct mts64 *mts);
+static int mts64_device_close(struct mts64 *mts);
+static u8 mts64_map_midi_input(u8 c);
+static int mts64_probe(struct parport *p);
+static u16 mts64_read(struct parport *p);
+static u8 mts64_read_char(struct parport *p);
+static void mts64_smpte_start(struct parport *p,
+                             u8 hours, u8 minutes,
+                             u8 seconds, u8 frames,
+                             u8 idx);
+static void mts64_smpte_stop(struct parport *p);
+static void mts64_write_command(struct parport *p, u8 c);
+static void mts64_write_data(struct parport *p, u8 c);
+static void mts64_write_midi(struct mts64 *mts, u8 c, int midiport);
+
+
+/*  Enables the readout procedure
+ *
+ *  Before we can read a midi byte from the device, we have to set
+ *  bit 3 of control port.
+ */
+static void mts64_enable_readout(struct parport *p)
+{
+       u8 c;
+
+       c = parport_read_control(p);
+       c |= MTS64_CTL_READOUT;
+       parport_write_control(p, c); 
+}
+
+/*  Disables readout 
+ *
+ *  Readout is disabled by clearing bit 3 of control
+ */
+static void mts64_disable_readout(struct parport *p)
+{
+       u8 c;
+
+       c = parport_read_control(p);
+       c &= ~MTS64_CTL_READOUT;
+       parport_write_control(p, c);
+}
+
+/*  waits for device ready
+ *
+ *  Checks if BUSY (Bit 7 of status) is clear
+ *  1 device ready
+ *  0 failure
+ */
+static int mts64_device_ready(struct parport *p)
+{
+       int i;
+       u8 c;
+
+       for (i = 0; i < 0xffff; ++i) {
+               c = parport_read_status(p);
+               c &= MTS64_STAT_BSY;
+               if (c != 0) 
+                       return 1;
+       } 
+
+       return 0;
+}
+
+/*  Init device (LED blinking startup magic)
+ *
+ *  Returns:
+ *  0 init ok
+ *  -EIO failure
+ */
+static int __devinit mts64_device_init(struct parport *p)
+{
+       int i;
+
+       mts64_write_command(p, MTS64_CMD_RESET);
+
+       for (i = 0; i < 64; ++i) {
+               msleep(100);
+
+               if (mts64_probe(p) == 0) {
+                       /* success */
+                       mts64_disable_readout(p);
+                       return 0;
+               }
+       }
+       mts64_disable_readout(p);
+
+       return -EIO;
+}
+
+/* 
+ *  Opens the device (set communication mode)
+ */
+static int mts64_device_open(struct mts64 *mts)
+{
+       int i;
+       struct parport *p = mts->pardev->port;
+
+       for (i = 0; i < 5; ++i)
+               mts64_write_command(p, MTS64_CMD_COM_OPEN);
+
+       return 0;
+}
+
+/*  
+ *  Close device (clear communication mode)
+ */
+static int mts64_device_close(struct mts64 *mts)
+{
+       int i;
+       struct parport *p = mts->pardev->port;
+
+       for (i = 0; i < 5; ++i) {
+               mts64_write_command(p, MTS64_CMD_COM_CLOSE1);
+               mts64_write_command(p, MTS64_CMD_COM_CLOSE2);
+       }
+
+       return 0;
+}
+
+/*  map hardware port to substream number
+ * 
+ *  When reading a byte from the device, the device tells us
+ *  on what port the byte is. This HW port has to be mapped to
+ *  the midiport (substream number).
+ *  substream 0-3 are Midiports 1-4
+ *  substream 4 is SMPTE Timecode
+ *  The mapping is done by the table:
+ *  HW | 0 | 1 | 2 | 3 | 4 
+ *  SW | 0 | 1 | 4 | 2 | 3
+ */
+static u8 mts64_map_midi_input(u8 c)
+{
+       static u8 map[] = { 0, 1, 4, 2, 3 };
+
+       return map[c];
+}
+
+
+/*  Probe parport for device
+ *
+ *  Do we have a Miditerminal 4140 on parport? 
+ *  Returns:
+ *  0       device found
+ *  -ENODEV no device
+ */
+static int __devinit mts64_probe(struct parport *p)
+{
+       u8 c;
+
+       mts64_smpte_stop(p);
+       mts64_write_command(p, MTS64_CMD_PROBE);
+
+       msleep(50);
+       
+       c = mts64_read(p);
+
+       c &= 0x00ff;
+       if (c != MTS64_CMD_PROBE) 
+               return -ENODEV;
+       else 
+               return 0;
+
+}
+
+/*  Read byte incl. status from device
+ *
+ *  Returns:
+ *  data in lower 8 bits and status in upper 8 bits
+ */
+static u16 mts64_read(struct parport *p)
+{
+       u8 data, status;
+
+       mts64_device_ready(p);
+       mts64_enable_readout(p);
+       status = parport_read_status(p);
+       data = mts64_read_char(p);
+       mts64_disable_readout(p);
+
+       return (status << 8) | data;
+}
+
+/*  Read a byte from device
+ *
+ *  Note, that readout mode has to be enabled.
+ *  readout procedure is as follows: 
+ *  - Write number of the Bit to read to DATA
+ *  - Read STATUS
+ *  - Bit 5 of STATUS indicates if Bit is set
+ *
+ *  Returns:
+ *  Byte read from device
+ */
+static u8 mts64_read_char(struct parport *p)
+{
+       u8 c = 0;
+       u8 status;
+       u8 i;
+
+       for (i = 0; i < 8; ++i) {
+               parport_write_data(p, i);
+               c >>= 1;
+               status = parport_read_status(p);
+               if (status & MTS64_STAT_BIT_SET) 
+                       c |= 0x80;
+       }
+       
+       return c;
+}
+
+/*  Starts SMPTE Timecode generation
+ *
+ *  The device creates SMPTE Timecode by hardware.
+ *  0 24 fps
+ *  1 25 fps
+ *  2 29.97 fps
+ *  3 30 fps (Drop-frame)
+ *  4 30 fps
+ */
+static void mts64_smpte_start(struct parport *p,
+                             u8 hours, u8 minutes,
+                             u8 seconds, u8 frames,
+                             u8 idx)
+{
+       static u8 fps[5] = { MTS64_CMD_SMPTE_FPS_24, 
+                            MTS64_CMD_SMPTE_FPS_25,
+                            MTS64_CMD_SMPTE_FPS_2997, 
+                            MTS64_CMD_SMPTE_FPS_30D,
+                            MTS64_CMD_SMPTE_FPS_30    };
+
+       mts64_write_command(p, MTS64_CMD_SMPTE_SET_TIME);
+       mts64_write_command(p, frames);
+       mts64_write_command(p, seconds);
+       mts64_write_command(p, minutes);
+       mts64_write_command(p, hours);
+
+       mts64_write_command(p, MTS64_CMD_SMPTE_SET_FPS);
+       mts64_write_command(p, fps[idx]);
+}
+
+/*  Stops SMPTE Timecode generation
+ */
+static void mts64_smpte_stop(struct parport *p)
+{
+       mts64_write_command(p, MTS64_CMD_SMPTE_STOP);
+}
+
+/*  Write a command byte to device
+ */
+static void mts64_write_command(struct parport *p, u8 c)
+{
+       mts64_device_ready(p);
+
+       parport_write_data(p, c);
+
+       parport_write_control(p, MTS64_CTL_WRITE_CMD);
+       parport_write_control(p, MTS64_CTL_WRITE_CMD | MTS64_CTL_STROBE);
+       parport_write_control(p, MTS64_CTL_WRITE_CMD);
+}
+
+/*  Write a data byte to device 
+ */
+static void mts64_write_data(struct parport *p, u8 c)
+{
+       mts64_device_ready(p);
+
+       parport_write_data(p, c);
+
+       parport_write_control(p, MTS64_CTL_WRITE_DATA);
+       parport_write_control(p, MTS64_CTL_WRITE_DATA | MTS64_CTL_STROBE);
+       parport_write_control(p, MTS64_CTL_WRITE_DATA);
+}
+
+/*  Write a MIDI byte to midiport
+ *
+ *  midiport ranges from 0-3 and maps to Ports 1-4
+ *  assumptions: communication mode is on
+ */
+static void mts64_write_midi(struct mts64 *mts, u8 c,
+                            int midiport)
+{
+       struct parport *p = mts->pardev->port;
+
+       /* check current midiport */
+       if (mts->current_midi_output_port != midiport)
+               mts64_write_command(p, midiport);
+
+       /* write midi byte */
+       mts64_write_data(p, c);
+}
+
+/*********************************************************************
+ * Control elements
+ *********************************************************************/
+
+/* SMPTE Switch */
+static int snd_mts64_ctl_smpte_switch_info(struct snd_kcontrol *kctl,
+                                          struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_switch_get(struct snd_kcontrol* kctl,
+                                         struct snd_ctl_elem_value *uctl)
+{
+       struct mts64 *mts = snd_kcontrol_chip(kctl);
+
+       spin_lock_irq(&mts->lock);
+       uctl->value.integer.value[0] = mts->smpte_switch;
+       spin_unlock_irq(&mts->lock);
+
+       return 0;
+}
+
+/* smpte_switch is not accessed from IRQ handler, so we just need
+   to protect the HW access */
+static int snd_mts64_ctl_smpte_switch_put(struct snd_kcontrol* kctl,
+                                         struct snd_ctl_elem_value *uctl)
+{
+       struct mts64 *mts = snd_kcontrol_chip(kctl);
+       int changed = 0;
+
+       spin_lock_irq(&mts->lock);
+       if (mts->smpte_switch == uctl->value.integer.value[0])
+               goto __out;
+
+       changed = 1;
+       mts->smpte_switch = uctl->value.integer.value[0];
+       if (mts->smpte_switch) {
+               mts64_smpte_start(mts->pardev->port,
+                                 mts->time[0], mts->time[1],
+                                 mts->time[2], mts->time[3],
+                                 mts->fps);
+       } else {
+               mts64_smpte_stop(mts->pardev->port);
+       }
+__out:
+       spin_unlock_irq(&mts->lock);
+       return changed;
+}
+
+static struct snd_kcontrol_new mts64_ctl_smpte_switch __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name  = "SMPTE Playback Switch",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 0,
+       .info = snd_mts64_ctl_smpte_switch_info,
+       .get  = snd_mts64_ctl_smpte_switch_get,
+       .put  = snd_mts64_ctl_smpte_switch_put
+};
+
+/* Time */
+static int snd_mts64_ctl_smpte_time_h_info(struct snd_kcontrol *kctl,
+                                          struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 23;
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_f_info(struct snd_kcontrol *kctl,
+                                          struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 99;
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_info(struct snd_kcontrol *kctl,
+                                        struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 59;
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_get(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *uctl)
+{
+       struct mts64 *mts = snd_kcontrol_chip(kctl);
+       int idx = kctl->private_value;
+
+       spin_lock_irq(&mts->lock);
+       uctl->value.integer.value[0] = mts->time[idx];
+       spin_unlock_irq(&mts->lock);
+
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_time_put(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *uctl)
+{
+       struct mts64 *mts = snd_kcontrol_chip(kctl);
+       int idx = kctl->private_value;
+       int changed = 0;
+
+       spin_lock_irq(&mts->lock);
+       if (mts->time[idx] != uctl->value.integer.value[0]) {
+               changed = 1;
+               mts->time[idx] = uctl->value.integer.value[0];
+       }
+       spin_unlock_irq(&mts->lock);
+
+       return changed;
+}
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_hours __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name  = "SMPTE Time Hours",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 0,
+       .info = snd_mts64_ctl_smpte_time_h_info,
+       .get  = snd_mts64_ctl_smpte_time_get,
+       .put  = snd_mts64_ctl_smpte_time_put
+};
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_minutes __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name  = "SMPTE Time Minutes",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 1,
+       .info = snd_mts64_ctl_smpte_time_info,
+       .get  = snd_mts64_ctl_smpte_time_get,
+       .put  = snd_mts64_ctl_smpte_time_put
+};
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_seconds __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name  = "SMPTE Time Seconds",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 2,
+       .info = snd_mts64_ctl_smpte_time_info,
+       .get  = snd_mts64_ctl_smpte_time_get,
+       .put  = snd_mts64_ctl_smpte_time_put
+};
+
+static struct snd_kcontrol_new mts64_ctl_smpte_time_frames __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name  = "SMPTE Time Frames",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 3,
+       .info = snd_mts64_ctl_smpte_time_f_info,
+       .get  = snd_mts64_ctl_smpte_time_get,
+       .put  = snd_mts64_ctl_smpte_time_put
+};
+
+/* FPS */
+static int snd_mts64_ctl_smpte_fps_info(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_info *uinfo)
+{
+       static char *texts[5] = { "24",
+                                 "25",
+                                 "29.97",
+                                 "30D",
+                                 "30"    };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 5;
+       if (uinfo->value.enumerated.item > 4)
+               uinfo->value.enumerated.item = 4;
+       strcpy(uinfo->value.enumerated.name,
+              texts[uinfo->value.enumerated.item]);
+       
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_fps_get(struct snd_kcontrol *kctl,
+                                      struct snd_ctl_elem_value *uctl)
+{
+       struct mts64 *mts = snd_kcontrol_chip(kctl);
+
+       spin_lock_irq(&mts->lock);
+       uctl->value.enumerated.item[0] = mts->fps;
+       spin_unlock_irq(&mts->lock);
+
+       return 0;
+}
+
+static int snd_mts64_ctl_smpte_fps_put(struct snd_kcontrol *kctl,
+                                      struct snd_ctl_elem_value *uctl)
+{
+       struct mts64 *mts = snd_kcontrol_chip(kctl);
+       int changed = 0;
+
+       spin_lock_irq(&mts->lock);
+       if (mts->fps != uctl->value.enumerated.item[0]) {
+               changed = 1;
+               mts->fps = uctl->value.enumerated.item[0];
+       }
+       spin_unlock_irq(&mts->lock);
+
+       return changed;
+}
+
+static struct snd_kcontrol_new mts64_ctl_smpte_fps __devinitdata = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name  = "SMPTE Fps",
+       .index = 0,
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+       .private_value = 0,
+       .info  = snd_mts64_ctl_smpte_fps_info,
+       .get   = snd_mts64_ctl_smpte_fps_get,
+       .put   = snd_mts64_ctl_smpte_fps_put
+};
+
+
+static int __devinit snd_mts64_ctl_create(struct snd_card *card, 
+                                         struct mts64 *mts) 
+{
+       int err, i;
+       static struct snd_kcontrol_new *control[] = {
+               &mts64_ctl_smpte_switch,
+               &mts64_ctl_smpte_time_hours,
+               &mts64_ctl_smpte_time_minutes,
+               &mts64_ctl_smpte_time_seconds,
+               &mts64_ctl_smpte_time_frames,
+               &mts64_ctl_smpte_fps,
+               0  };
+
+       for (i = 0; control[i]; ++i) {
+               err = snd_ctl_add(card, snd_ctl_new1(control[i], mts));
+               if (err < 0) {
+                       snd_printd("Cannot create control: %s\n", 
+                                  control[i]->name);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+/*********************************************************************
+ * Rawmidi
+ *********************************************************************/
+#define MTS64_MODE_INPUT_TRIGGERED 0x01
+
+static int snd_mts64_rawmidi_open(struct snd_rawmidi_substream *substream)
+{
+       struct mts64 *mts = substream->rmidi->private_data;
+
+       if (mts->open_count == 0) {
+               /* We don't need a spinlock here, because this is just called 
+                  if the device has not been opened before. 
+                  So there aren't any IRQs from the device */
+               mts64_device_open(mts);
+
+               msleep(50);
+       }
+       ++(mts->open_count);
+
+       return 0;
+}
+
+static int snd_mts64_rawmidi_close(struct snd_rawmidi_substream *substream)
+{
+       struct mts64 *mts = substream->rmidi->private_data;
+       unsigned long flags;
+
+       --(mts->open_count);
+       if (mts->open_count == 0) {
+               /* We need the spinlock_irqsave here because we can still
+                  have IRQs at this point */
+               spin_lock_irqsave(&mts->lock, flags);
+               mts64_device_close(mts);
+               spin_unlock_irqrestore(&mts->lock, flags);
+
+               msleep(500);
+
+       } else if (mts->open_count < 0)
+               mts->open_count = 0;
+
+       return 0;
+}
+
+static void snd_mts64_rawmidi_output_trigger(struct snd_rawmidi_substream *substream,
+                                            int up)
+{
+       struct mts64 *mts = substream->rmidi->private_data;
+       u8 data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mts->lock, flags);
+       while (snd_rawmidi_transmit_peek(substream, &data, 1) == 1) {
+               mts64_write_midi(mts, data, substream->number+1);
+               snd_rawmidi_transmit_ack(substream, 1);
+       }
+       spin_unlock_irqrestore(&mts->lock, flags);
+}
+
+static void snd_mts64_rawmidi_input_trigger(struct snd_rawmidi_substream *substream,
+                                           int up)
+{
+       struct mts64 *mts = substream->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mts->lock, flags);
+       if (up)
+               mts->mode[substream->number] |= MTS64_MODE_INPUT_TRIGGERED;
+       else
+               mts->mode[substream->number] &= ~MTS64_MODE_INPUT_TRIGGERED;
+       
+       spin_unlock_irqrestore(&mts->lock, flags);
+}
+
+static struct snd_rawmidi_ops snd_mts64_rawmidi_output_ops = {
+       .open    = snd_mts64_rawmidi_open,
+       .close   = snd_mts64_rawmidi_close,
+       .trigger = snd_mts64_rawmidi_output_trigger
+};
+
+static struct snd_rawmidi_ops snd_mts64_rawmidi_input_ops = {
+       .open    = snd_mts64_rawmidi_open,
+       .close   = snd_mts64_rawmidi_close,
+       .trigger = snd_mts64_rawmidi_input_trigger
+};
+
+/* Create and initialize the rawmidi component */
+static int __devinit snd_mts64_rawmidi_create(struct snd_card *card)
+{
+       struct mts64 *mts = card->private_data;
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_substream *substream;
+       struct list_head *list;
+       int err;
+       
+       err = snd_rawmidi_new(card, CARD_NAME, 0, 
+                             MTS64_NUM_OUTPUT_PORTS, 
+                             MTS64_NUM_INPUT_PORTS, 
+                             &rmidi);
+       if (err < 0) 
+               return err;
+
+       rmidi->private_data = mts;
+       strcpy(rmidi->name, CARD_NAME);
+       rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+                           SNDRV_RAWMIDI_INFO_INPUT |
+                            SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       mts->rmidi = rmidi;
+
+       /* register rawmidi ops */
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 
+                           &snd_mts64_rawmidi_output_ops);
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 
+                           &snd_mts64_rawmidi_input_ops);
+
+       /* name substreams */
+       /* output */
+       list_for_each(list, 
+                     &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
+               substream = list_entry(list, struct snd_rawmidi_substream, list);
+               sprintf(substream->name,
+                       "Miditerminal %d", substream->number+1);
+       }
+       /* input */
+       list_for_each(list, 
+                     &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
+               substream = list_entry(list, struct snd_rawmidi_substream, list);
+               mts->midi_input_substream[substream->number] = substream;
+               switch(substream->number) {
+               case MTS64_SMPTE_SUBSTREAM:
+                       strcpy(substream->name, "Miditerminal SMPTE");
+                       break;
+               default:
+                       sprintf(substream->name,
+                               "Miditerminal %d", substream->number+1);
+               }
+       }
+
+       /* controls */
+       err = snd_mts64_ctl_create(card, mts);
+
+       return err;
+}
+
+/*********************************************************************
+ * parport stuff
+ *********************************************************************/
+static void snd_mts64_interrupt(int irq, void *private, struct pt_regs *r)
+{
+       struct mts64 *mts = ((struct snd_card*)private)->private_data;
+       u16 ret;
+       u8 status, data;
+       struct snd_rawmidi_substream *substream;
+
+       spin_lock(&mts->lock);
+       ret = mts64_read(mts->pardev->port);
+       data = ret & 0x00ff;
+       status = ret >> 8;
+
+       if (status & MTS64_STAT_PORT) {
+               mts->current_midi_input_port = mts64_map_midi_input(data);
+       } else {
+               if (mts->current_midi_input_port == -1) 
+                       goto __out;
+               substream = mts->midi_input_substream[mts->current_midi_input_port];
+               if (mts->mode[substream->number] & MTS64_MODE_INPUT_TRIGGERED)
+                       snd_rawmidi_receive(substream, &data, 1);
+       }
+__out:
+       spin_unlock(&mts->lock);
+}
+
+static int __devinit snd_mts64_probe_port(struct parport *p)
+{
+       struct pardevice *pardev;
+       int res;
+
+       pardev = parport_register_device(p, DRIVER_NAME,
+                                        NULL, NULL, NULL,
+                                        0, NULL);
+       if (!pardev)
+               return -EIO;
+       
+       if (parport_claim(pardev)) {
+               parport_unregister_device(pardev);
+               return -EIO;
+       }
+
+       res = mts64_probe(p);
+
+       parport_release(pardev);
+       parport_unregister_device(pardev);
+
+       return res;
+}
+
+static void __devinit snd_mts64_attach(struct parport *p)
+{
+       struct platform_device *device;
+
+       device = platform_device_alloc(PLATFORM_DRIVER, device_count);
+       if (!device) 
+               return;
+
+       /* Temporary assignment to forward the parport */
+       platform_set_drvdata(device, p);
+
+       if (platform_device_register(device) < 0) {
+               platform_device_put(device);
+               return;
+       }
+
+       /* Since we dont get the return value of probe
+        * We need to check if device probing succeeded or not */
+       if (!platform_get_drvdata(device)) {
+               platform_device_unregister(device);
+               return;
+       }
+
+       /* register device in global table */
+       platform_devices[device_count] = device;
+       device_count++;
+}
+
+static void snd_mts64_detach(struct parport *p)
+{
+       /* nothing to do here */
+}
+
+static struct parport_driver mts64_parport_driver = {
+       .name   = "mts64",
+       .attach = snd_mts64_attach,
+       .detach = snd_mts64_detach
+};
+
+/*********************************************************************
+ * platform stuff
+ *********************************************************************/
+static void snd_mts64_card_private_free(struct snd_card *card)
+{
+       struct mts64 *mts = card->private_data;
+       struct pardevice *pardev = mts->pardev;
+
+       if (pardev) {
+               if (mts->pardev_claimed)
+                       parport_release(pardev);
+               parport_unregister_device(pardev);
+       }
+
+       snd_mts64_free(mts);
+}
+
+static int __devinit snd_mts64_probe(struct platform_device *pdev)
+{
+       struct pardevice *pardev;
+       struct parport *p;
+       int dev = pdev->id;
+       struct snd_card *card = NULL;
+       struct mts64 *mts = NULL;
+       int err;
+
+       p = platform_get_drvdata(pdev);
+       platform_set_drvdata(pdev, NULL);
+
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+       if (!enable[dev]) 
+               return -ENOENT;
+       if ((err = snd_mts64_probe_port(p)) < 0)
+               return err;
+
+       card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+       if (card == NULL) {
+               snd_printd("Cannot create card\n");
+               return -ENOMEM;
+       }
+       strcpy(card->driver, DRIVER_NAME);
+       strcpy(card->shortname, "ESI " CARD_NAME);
+       sprintf(card->longname,  "%s at 0x%lx, irq %i", 
+               card->shortname, p->base, p->irq);
+
+       pardev = parport_register_device(p,                   /* port */
+                                        DRIVER_NAME,         /* name */
+                                        NULL,                /* preempt */
+                                        NULL,                /* wakeup */
+                                        snd_mts64_interrupt, /* ISR */
+                                        PARPORT_DEV_EXCL,    /* flags */
+                                        (void *)card);       /* private */
+       if (pardev == NULL) {
+               snd_printd("Cannot register pardevice\n");
+               err = -EIO;
+               goto __err;
+       }
+
+       if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
+               snd_printd("Cannot create main component\n");
+               parport_unregister_device(pardev);
+               goto __err;
+       }
+       card->private_data = mts;
+       card->private_free = snd_mts64_card_private_free;
+       
+       if ((err = snd_mts64_rawmidi_create(card)) < 0) {
+               snd_printd("Creating Rawmidi component failed\n");
+               goto __err;
+       }
+
+       /* claim parport */
+       if (parport_claim(pardev)) {
+               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+               err = -EIO;
+               goto __err;
+       }
+       mts->pardev_claimed = 1;
+
+       /* init device */
+       if ((err = mts64_device_init(p)) < 0)
+               goto __err;
+
+       platform_set_drvdata(pdev, card);
+
+       /* At this point card will be usable */
+       if ((err = snd_card_register(card)) < 0) {
+               snd_printd("Cannot register card\n");
+               goto __err;
+       }
+
+       snd_printk("ESI Miditerminal 4140 on 0x%lx\n", p->base);
+       return 0;
+
+__err:
+       snd_card_free(card);
+       return err;
+}
+
+static int snd_mts64_remove(struct platform_device *pdev)
+{
+       struct snd_card *card = platform_get_drvdata(pdev);
+
+       if (card)
+               snd_card_free(card);
+
+       return 0;
+}
+
+
+static struct platform_driver snd_mts64_driver = {
+       .probe  = snd_mts64_probe,
+       .remove = snd_mts64_remove,
+       .driver = {
+               .name = PLATFORM_DRIVER
+       }
+};
+
+/*********************************************************************
+ * module init stuff
+ *********************************************************************/
+static void snd_mts64_unregister_all(void)
+{
+       int i;
+
+       for (i = 0; i < SNDRV_CARDS; ++i) {
+               if (platform_devices[i]) {
+                       platform_device_unregister(platform_devices[i]);
+                       platform_devices[i] = NULL;
+               }
+       }               
+       platform_driver_unregister(&snd_mts64_driver);
+       parport_unregister_driver(&mts64_parport_driver);
+}
+
+static int __init snd_mts64_module_init(void)
+{
+       int err;
+
+       if ((err = platform_driver_register(&snd_mts64_driver)) < 0)
+               return err;
+
+       if (parport_register_driver(&mts64_parport_driver) != 0) {
+               platform_driver_unregister(&snd_mts64_driver);
+               return -EIO;
+       }
+
+       if (device_count == 0) {
+               snd_mts64_unregister_all();
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static void __exit snd_mts64_module_exit(void)
+{
+       snd_mts64_unregister_all();
+}
+
+module_init(snd_mts64_module_init);
+module_exit(snd_mts64_module_exit);
index e552ec34166fed892e7043e18e8f96e6b84c0620..1679300b75835398276f3fd57b02dab4591e52a6 100644 (file)
@@ -105,13 +105,13 @@ static long long snd_opl4_mem_proc_llseek(struct snd_info_entry *entry, void *fi
                                          struct file *file, long long offset, int orig)
 {
        switch (orig) {
-       case 0: /* SEEK_SET */
+       case SEEK_SET:
                file->f_pos = offset;
                break;
-       case 1: /* SEEK_CUR */
+       case SEEK_CUR:
                file->f_pos += offset;
                break;
-       case 2: /* SEEK_END, offset is negative */
+       case SEEK_END: /* offset is negative */
                file->f_pos = entry->size + offset;
                break;
        default:
@@ -159,8 +159,7 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
 
 void snd_opl4_free_proc(struct snd_opl4 *opl4)
 {
-       if (opl4->proc_entry)
-               snd_info_unregister(opl4->proc_entry);
+       snd_info_free_entry(opl4->proc_entry);
 }
 
 #endif /* CONFIG_PROC_FS */
index c1d7fcdd1973ec54fba500d752cdf94d832f682f..1613ed844ac6228e40574301511f72054a530a87 100644 (file)
@@ -23,6 +23,7 @@
 #include <sound/driver.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/vx_core.h>
 #include "vx_cmd.h"
 
@@ -455,10 +456,13 @@ static int vx_output_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
 
 static struct snd_kcontrol_new vx_control_output_level = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access =       (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                        SNDRV_CTL_ELEM_ACCESS_TLV_READ),
        .name =         "Master Playback Volume",
        .info =         vx_output_level_info,
        .get =          vx_output_level_get,
        .put =          vx_output_level_put,
+       /* tlv will be filled later */
 };
 
 /*
@@ -712,12 +716,17 @@ static int vx_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_
        return 0;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_audio_gain, -10975, 25, 0);
+
 static struct snd_kcontrol_new vx_control_audio_gain = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
+       .access =       (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                        SNDRV_CTL_ELEM_ACCESS_TLV_READ),
        /* name will be filled later */
        .info =         vx_audio_gain_info,
        .get =          vx_audio_gain_get,
-       .put =          vx_audio_gain_put
+       .put =          vx_audio_gain_put,
+       .tlv = { .p = db_scale_audio_gain },
 };
 static struct snd_kcontrol_new vx_control_output_switch = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -729,9 +738,12 @@ static struct snd_kcontrol_new vx_control_output_switch = {
 static struct snd_kcontrol_new vx_control_monitor_gain = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
        .name =         "Monitoring Volume",
+       .access =       (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                        SNDRV_CTL_ELEM_ACCESS_TLV_READ),
        .info =         vx_audio_gain_info,     /* shared */
        .get =          vx_audio_monitor_get,
-       .put =          vx_audio_monitor_put
+       .put =          vx_audio_monitor_put,
+       .tlv = { .p = db_scale_audio_gain },
 };
 static struct snd_kcontrol_new vx_control_monitor_switch = {
        .iface =        SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -918,6 +930,7 @@ int snd_vx_mixer_new(struct vx_core *chip)
        for (i = 0; i < chip->hw->num_outs; i++) {
                temp = vx_control_output_level;
                temp.index = i;
+               temp.tlv.p = chip->hw->output_level_db_scale;
                if ((err = snd_ctl_add(card, snd_ctl_new1(&temp, chip))) < 0)
                        return err;
        }
index dc7cc2001b74a572b67bc4d015e8e2f4f527f600..5da49e2eb35047248abea410872d1a90921b5acc 100644 (file)
 #include <linux/init.h>
 #include <sound/core.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/ak4xxx-adda.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");
 MODULE_DESCRIPTION("Routines for control of AK452x / AK43xx  AD/DA converters");
 MODULE_LICENSE("GPL");
 
+/* write the given register and save the data to the cache */
 void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
                       unsigned char val)
 {
@@ -41,15 +43,7 @@ void snd_akm4xxx_write(struct snd_akm4xxx *ak, int chip, unsigned char reg,
        ak->ops.write(ak, chip, reg, val);
 
        /* save the data */
-       if (ak->type == SND_AK4524 || ak->type == SND_AK4528) {
-               if ((reg != 0x04 && reg != 0x05) || (val & 0x80) == 0)
-                       snd_akm4xxx_set(ak, chip, reg, val);
-               else
-                       snd_akm4xxx_set_ipga(ak, chip, reg, val);
-       } else {
-               /* AK4529, or else */
-               snd_akm4xxx_set(ak, chip, reg, val);
-       }
+       snd_akm4xxx_set(ak, chip, reg, val);
        ak->ops.unlock(ak, chip);
 }
 
@@ -73,12 +67,6 @@ static void ak4524_reset(struct snd_akm4xxx *ak, int state)
                for (reg = 0x04; reg < maxreg; reg++)
                        snd_akm4xxx_write(ak, chip, reg,
                                          snd_akm4xxx_get(ak, chip, reg));
-               if (ak->type == SND_AK4528)
-                       continue;
-               /* IPGA */
-               for (reg = 0x04; reg < 0x06; reg++)
-                       snd_akm4xxx_write(ak, chip, reg,
-                                         snd_akm4xxx_get_ipga(ak, chip, reg));
        }
 }
 
@@ -137,11 +125,48 @@ void snd_akm4xxx_reset(struct snd_akm4xxx *ak, int state)
        case SND_AK4381:
                ak4381_reset(ak, state);
                break;
+       default:
+               break;
        }
 }
 
 EXPORT_SYMBOL(snd_akm4xxx_reset);
 
+
+/*
+ * Volume conversion table for non-linear volumes
+ * from -63.5dB (mute) to 0dB step 0.5dB
+ *
+ * Used for AK4524 input/ouput attenuation, AK4528, and
+ * AK5365 input attenuation
+ */
+static unsigned char vol_cvt_datt[128] = {
+       0x00, 0x01, 0x01, 0x02, 0x02, 0x03, 0x03, 0x04,
+       0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x06, 0x06,
+       0x06, 0x07, 0x07, 0x08, 0x08, 0x08, 0x09, 0x0a,
+       0x0a, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f,
+       0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x13, 0x14,
+       0x15, 0x16, 0x17, 0x17, 0x18, 0x19, 0x1a, 0x1c,
+       0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x23,
+       0x24, 0x25, 0x26, 0x28, 0x29, 0x2a, 0x2b, 0x2d,
+       0x2e, 0x30, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
+       0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3e, 0x3f, 0x40,
+       0x41, 0x42, 0x43, 0x44, 0x46, 0x47, 0x48, 0x4a,
+       0x4b, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53, 0x54,
+       0x55, 0x56, 0x58, 0x59, 0x5b, 0x5c, 0x5e, 0x5f,
+       0x60, 0x61, 0x62, 0x64, 0x65, 0x66, 0x67, 0x69,
+       0x6a, 0x6c, 0x6d, 0x6f, 0x70, 0x71, 0x72, 0x73,
+       0x75, 0x76, 0x77, 0x79, 0x7a, 0x7c, 0x7d, 0x7f,
+};
+
+/*
+ * dB tables
+ */
+static DECLARE_TLV_DB_SCALE(db_scale_vol_datt, -6350, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_8bit, -12750, 50, 1);
+static DECLARE_TLV_DB_SCALE(db_scale_7bit, -6350, 50, 1);
+static DECLARE_TLV_DB_LINEAR(db_scale_linear, TLV_DB_GAIN_MUTE, 0);
+
 /*
  * initialize all the ak4xxx chips
  */
@@ -155,8 +180,6 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
                0x01, 0x03, /* 1: ADC/DAC enable */
                0x04, 0x00, /* 4: ADC left muted */
                0x05, 0x00, /* 5: ADC right muted */
-               0x04, 0x80, /* 4: ADC IPGA gain 0dB */
-               0x05, 0x80, /* 5: ADC IPGA gain 0dB */
                0x06, 0x00, /* 6: DAC left muted */
                0x07, 0x00, /* 7: DAC right muted */
                0xff, 0xff
@@ -238,6 +261,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
        int chip, num_chips;
        unsigned char *ptr, reg, data, *inits;
 
+       memset(ak->images, 0, sizeof(ak->images));
+       memset(ak->volumes, 0, sizeof(ak->volumes));
+
        switch (ak->type) {
        case SND_AK4524:
                inits = inits_ak4524;
@@ -263,6 +289,9 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
                inits = inits_ak4381;
                num_chips = ak->num_dacs / 2;
                break;
+       case SND_AK5365:
+               /* FIXME: any init sequence? */
+               return;
        default:
                snd_BUG();
                return;
@@ -280,14 +309,23 @@ void snd_akm4xxx_init(struct snd_akm4xxx *ak)
 
 EXPORT_SYMBOL(snd_akm4xxx_init);
 
+/*
+ * Mixer callbacks
+ */
+#define AK_IPGA                        (1<<20) /* including IPGA */
+#define AK_VOL_CVT                     (1<<21) /* need dB conversion */
+#define AK_NEEDSMSB                    (1<<22) /* need MSB update bit */
+#define AK_INVERT                      (1<<23) /* data is inverted */
 #define AK_GET_CHIP(val)               (((val) >> 8) & 0xff)
 #define AK_GET_ADDR(val)               ((val) & 0xff)
-#define AK_GET_SHIFT(val)              (((val) >> 16) & 0x7f)
+#define AK_GET_SHIFT(val)              (((val) >> 16) & 0x0f)
+#define AK_GET_VOL_CVT(val)            (((val) >> 21) & 1)
+#define AK_GET_IPGA(val)               (((val) >> 20) & 1)
+#define AK_GET_NEEDSMSB(val)           (((val) >> 22) & 1)
 #define AK_GET_INVERT(val)             (((val) >> 23) & 1)
 #define AK_GET_MASK(val)               (((val) >> 24) & 0xff)
 #define AK_COMPOSE(chip,addr,shift,mask) \
        (((chip) << 8) | (addr) | ((shift) << 16) | ((mask) << 24))
-#define AK_INVERT                      (1<<23)
 
 static int snd_akm4xxx_volume_info(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_info *uinfo)
@@ -307,31 +345,39 @@ static int snd_akm4xxx_volume_get(struct snd_kcontrol *kcontrol,
        struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
        int chip = AK_GET_CHIP(kcontrol->private_value);
        int addr = AK_GET_ADDR(kcontrol->private_value);
-       int invert = AK_GET_INVERT(kcontrol->private_value);
-       unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-       unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-       
-       ucontrol->value.integer.value[0] = invert ? mask - val : val;
+
+       ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
        return 0;
 }
 
-static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_value *ucontrol)
+static int put_ak_reg(struct snd_kcontrol *kcontrol, int addr,
+                     unsigned char nval)
 {
        struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
-       int chip = AK_GET_CHIP(kcontrol->private_value);
-       int addr = AK_GET_ADDR(kcontrol->private_value);
-       int invert = AK_GET_INVERT(kcontrol->private_value);
        unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-       unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
-       int change;
+       int chip = AK_GET_CHIP(kcontrol->private_value);
 
-       if (invert)
+       if (snd_akm4xxx_get_vol(ak, chip, addr) == nval)
+               return 0;
+
+       snd_akm4xxx_set_vol(ak, chip, addr, nval);
+       if (AK_GET_VOL_CVT(kcontrol->private_value) && nval < 128)
+               nval = vol_cvt_datt[nval];
+       if (AK_GET_IPGA(kcontrol->private_value) && nval >= 128)
+               nval++; /* need to correct + 1 since both 127 and 128 are 0dB */
+       if (AK_GET_INVERT(kcontrol->private_value))
                nval = mask - nval;
-       change = snd_akm4xxx_get(ak, chip, addr) != nval;
-       if (change)
-               snd_akm4xxx_write(ak, chip, addr, nval);
-       return change;
+       if (AK_GET_NEEDSMSB(kcontrol->private_value))
+               nval |= 0x80;
+       snd_akm4xxx_write(ak, chip, addr, nval);
+       return 1;
+}
+
+static int snd_akm4xxx_volume_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       return put_ak_reg(kcontrol, AK_GET_ADDR(kcontrol->private_value),
+                         ucontrol->value.integer.value[0]);
 }
 
 static int snd_akm4xxx_stereo_volume_info(struct snd_kcontrol *kcontrol,
@@ -352,77 +398,21 @@ static int snd_akm4xxx_stereo_volume_get(struct snd_kcontrol *kcontrol,
        struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
        int chip = AK_GET_CHIP(kcontrol->private_value);
        int addr = AK_GET_ADDR(kcontrol->private_value);
-       int invert = AK_GET_INVERT(kcontrol->private_value);
-       unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-       unsigned char val = snd_akm4xxx_get(ak, chip, addr);
-       
-       ucontrol->value.integer.value[0] = invert ? mask - val : val;
-
-       val = snd_akm4xxx_get(ak, chip, addr+1);
-       ucontrol->value.integer.value[1] = invert ? mask - val : val;
 
+       ucontrol->value.integer.value[0] = snd_akm4xxx_get_vol(ak, chip, addr);
+       ucontrol->value.integer.value[1] = snd_akm4xxx_get_vol(ak, chip, addr+1);
        return 0;
 }
 
 static int snd_akm4xxx_stereo_volume_put(struct snd_kcontrol *kcontrol,
                                         struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
-       int chip = AK_GET_CHIP(kcontrol->private_value);
-       int addr = AK_GET_ADDR(kcontrol->private_value);
-       int invert = AK_GET_INVERT(kcontrol->private_value);
-       unsigned int mask = AK_GET_MASK(kcontrol->private_value);
-       unsigned char nval = ucontrol->value.integer.value[0] % (mask+1);
-       int change0, change1;
-
-       if (invert)
-               nval = mask - nval;
-       change0 = snd_akm4xxx_get(ak, chip, addr) != nval;
-       if (change0)
-               snd_akm4xxx_write(ak, chip, addr, nval);
-
-       nval = ucontrol->value.integer.value[1] % (mask+1);
-       if (invert)
-               nval = mask - nval;
-       change1 = snd_akm4xxx_get(ak, chip, addr+1) != nval;
-       if (change1)
-               snd_akm4xxx_write(ak, chip, addr+1, nval);
-
-
-       return change0 || change1;
-}
-
-static int snd_akm4xxx_ipga_gain_info(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_info *uinfo)
-{
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = 36;
-       return 0;
-}
-
-static int snd_akm4xxx_ipga_gain_get(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
-       int chip = AK_GET_CHIP(kcontrol->private_value);
        int addr = AK_GET_ADDR(kcontrol->private_value);
-       ucontrol->value.integer.value[0] =
-               snd_akm4xxx_get_ipga(ak, chip, addr) & 0x7f;
-       return 0;
-}
+       int change;
 
-static int snd_akm4xxx_ipga_gain_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
-       int chip = AK_GET_CHIP(kcontrol->private_value);
-       int addr = AK_GET_ADDR(kcontrol->private_value);
-       unsigned char nval = (ucontrol->value.integer.value[0] % 37) | 0x80;
-       int change = snd_akm4xxx_get_ipga(ak, chip, addr) != nval;
-       if (change)
-               snd_akm4xxx_write(ak, chip, addr, nval);
+       change = put_ak_reg(kcontrol, addr, ucontrol->value.integer.value[0]);
+       change |= put_ak_reg(kcontrol, addr + 1,
+                            ucontrol->value.integer.value[1]);
        return change;
 }
 
@@ -472,179 +462,280 @@ static int snd_akm4xxx_deemphasis_put(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+static int ak4xxx_switch_info(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+       return 0;
+}
+
+static int ak4xxx_switch_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+       int chip = AK_GET_CHIP(kcontrol->private_value);
+       int addr = AK_GET_ADDR(kcontrol->private_value);
+       int shift = AK_GET_SHIFT(kcontrol->private_value);
+       int invert = AK_GET_INVERT(kcontrol->private_value);
+       unsigned char val = snd_akm4xxx_get(ak, chip, addr);
+
+       if (invert)
+               val = ! val;
+       ucontrol->value.integer.value[0] = (val & (1<<shift)) != 0;
+       return 0;
+}
+
+static int ak4xxx_switch_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_akm4xxx *ak = snd_kcontrol_chip(kcontrol);
+       int chip = AK_GET_CHIP(kcontrol->private_value);
+       int addr = AK_GET_ADDR(kcontrol->private_value);
+       int shift = AK_GET_SHIFT(kcontrol->private_value);
+       int invert = AK_GET_INVERT(kcontrol->private_value);
+       long flag = ucontrol->value.integer.value[0];
+       unsigned char val, oval;
+       int change;
+
+       if (invert)
+               flag = ! flag;
+       oval = snd_akm4xxx_get(ak, chip, addr);
+       if (flag)
+               val = oval | (1<<shift);
+       else
+               val = oval & ~(1<<shift);
+       change = (oval != val);
+       if (change)
+               snd_akm4xxx_write(ak, chip, addr, val);
+       return change;
+}
+
 /*
  * build AK4xxx controls
  */
 
-int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
+static int build_dac_controls(struct snd_akm4xxx *ak)
 {
-       unsigned int idx, num_emphs;
-       struct snd_kcontrol *ctl;
-       int err;
-       int mixer_ch = 0;
-       int num_stereo;
-
-       ctl = kmalloc(sizeof(*ctl), GFP_KERNEL);
-       if (! ctl)
-               return -ENOMEM;
+       int idx, err, mixer_ch, num_stereo;
+       struct snd_kcontrol_new knew;
 
+       mixer_ch = 0;
        for (idx = 0; idx < ak->num_dacs; ) {
-               memset(ctl, 0, sizeof(*ctl));
-               if (ak->channel_names == NULL) {
-                       strcpy(ctl->id.name, "DAC Volume");
+               memset(&knew, 0, sizeof(knew));
+               if (! ak->dac_info || ! ak->dac_info[mixer_ch].name) {
+                       knew.name = "DAC Volume";
+                       knew.index = mixer_ch + ak->idx_offset * 2;
                        num_stereo = 1;
-                       ctl->id.index = mixer_ch + ak->idx_offset * 2;
                } else {
-                       strcpy(ctl->id.name, ak->channel_names[mixer_ch]);
-                       num_stereo = ak->num_stereo[mixer_ch];
-                       ctl->id.index = 0;
+                       knew.name = ak->dac_info[mixer_ch].name;
+                       num_stereo = ak->dac_info[mixer_ch].num_channels;
                }
-               ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               ctl->count = 1;
+               knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               knew.count = 1;
+               knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ;
                if (num_stereo == 2) {
-                       ctl->info = snd_akm4xxx_stereo_volume_info;
-                       ctl->get = snd_akm4xxx_stereo_volume_get;
-                       ctl->put = snd_akm4xxx_stereo_volume_put;
+                       knew.info = snd_akm4xxx_stereo_volume_info;
+                       knew.get = snd_akm4xxx_stereo_volume_get;
+                       knew.put = snd_akm4xxx_stereo_volume_put;
                } else {
-                       ctl->info = snd_akm4xxx_volume_info;
-                       ctl->get = snd_akm4xxx_volume_get;
-                       ctl->put = snd_akm4xxx_volume_put;
+                       knew.info = snd_akm4xxx_volume_info;
+                       knew.get = snd_akm4xxx_volume_get;
+                       knew.put = snd_akm4xxx_volume_put;
                }
                switch (ak->type) {
                case SND_AK4524:
                        /* register 6 & 7 */
-                       ctl->private_value =
-                               AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127);
+                       knew.private_value =
+                               AK_COMPOSE(idx/2, (idx%2) + 6, 0, 127) |
+                               AK_VOL_CVT;
+                       knew.tlv.p = db_scale_vol_datt;
                        break;
                case SND_AK4528:
                        /* register 4 & 5 */
-                       ctl->private_value =
-                               AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
+                       knew.private_value =
+                               AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127) |
+                               AK_VOL_CVT;
+                       knew.tlv.p = db_scale_vol_datt;
                        break;
                case SND_AK4529: {
                        /* registers 2-7 and b,c */
                        int val = idx < 6 ? idx + 2 : (idx - 6) + 0xb;
-                       ctl->private_value =
+                       knew.private_value =
                                AK_COMPOSE(0, val, 0, 255) | AK_INVERT;
+                       knew.tlv.p = db_scale_8bit;
                        break;
                }
                case SND_AK4355:
                        /* register 4-9, chip #0 only */
-                       ctl->private_value = AK_COMPOSE(0, idx + 4, 0, 255);
+                       knew.private_value = AK_COMPOSE(0, idx + 4, 0, 255);
+                       knew.tlv.p = db_scale_8bit;
                        break;
-               case SND_AK4358:
-                       if (idx >= 6)
-                               /* register 4-9, chip #0 only */
-                               ctl->private_value =
-                                       AK_COMPOSE(0, idx + 5, 0, 255);
-                       else
-                               /* register 4-9, chip #0 only */
-                               ctl->private_value =
-                                       AK_COMPOSE(0, idx + 4, 0, 255);
+               case SND_AK4358: {
+                       /* register 4-9 and 11-12, chip #0 only */
+                       int  addr = idx < 6 ? idx + 4 : idx + 5;
+                       knew.private_value =
+                               AK_COMPOSE(0, addr, 0, 127) | AK_NEEDSMSB;
+                       knew.tlv.p = db_scale_7bit;
                        break;
+               }
                case SND_AK4381:
                        /* register 3 & 4 */
-                       ctl->private_value =
+                       knew.private_value =
                                AK_COMPOSE(idx/2, (idx%2) + 3, 0, 255);
+                       knew.tlv.p = db_scale_linear;
                        break;
                default:
-                       err = -EINVAL;
-                       goto __error;
+                       return -EINVAL;
                }
 
-               ctl->private_data = ak;
-               err = snd_ctl_add(ak->card,
-                                 snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-                                             SNDRV_CTL_ELEM_ACCESS_WRITE));
+               err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
                if (err < 0)
-                       goto __error;
+                       return err;
 
                idx += num_stereo;
                mixer_ch++;
        }
-       for (idx = 0; idx < ak->num_adcs && ak->type == SND_AK4524; ++idx) {
-               memset(ctl, 0, sizeof(*ctl));
-               strcpy(ctl->id.name, "ADC Volume");
-               ctl->id.index = idx + ak->idx_offset * 2;
-               ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               ctl->count = 1;
-               ctl->info = snd_akm4xxx_volume_info;
-               ctl->get = snd_akm4xxx_volume_get;
-               ctl->put = snd_akm4xxx_volume_put;
-               /* register 4 & 5 */
-               ctl->private_value =
-                       AK_COMPOSE(idx/2, (idx%2) + 4, 0, 127);
-               ctl->private_data = ak;
-               err = snd_ctl_add(ak->card,
-                                 snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-                                             SNDRV_CTL_ELEM_ACCESS_WRITE));
-               if (err < 0)
-                       goto __error;
-
-               memset(ctl, 0, sizeof(*ctl));
-               strcpy(ctl->id.name, "IPGA Analog Capture Volume");
-               ctl->id.index = idx + ak->idx_offset * 2;
-               ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               ctl->count = 1;
-               ctl->info = snd_akm4xxx_ipga_gain_info;
-               ctl->get = snd_akm4xxx_ipga_gain_get;
-               ctl->put = snd_akm4xxx_ipga_gain_put;
+       return 0;
+}
+
+static int build_adc_controls(struct snd_akm4xxx *ak)
+{
+       int idx, err, mixer_ch, num_stereo;
+       struct snd_kcontrol_new knew;
+
+       mixer_ch = 0;
+       for (idx = 0; idx < ak->num_adcs;) {
+               memset(&knew, 0, sizeof(knew));
+               if (! ak->adc_info || ! ak->adc_info[mixer_ch].name) {
+                       knew.name = "ADC Volume";
+                       knew.index = mixer_ch + ak->idx_offset * 2;
+                       num_stereo = 1;
+               } else {
+                       knew.name = ak->adc_info[mixer_ch].name;
+                       num_stereo = ak->adc_info[mixer_ch].num_channels;
+               }
+               knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               knew.count = 1;
+               knew.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               if (num_stereo == 2) {
+                       knew.info = snd_akm4xxx_stereo_volume_info;
+                       knew.get = snd_akm4xxx_stereo_volume_get;
+                       knew.put = snd_akm4xxx_stereo_volume_put;
+               } else {
+                       knew.info = snd_akm4xxx_volume_info;
+                       knew.get = snd_akm4xxx_volume_get;
+                       knew.put = snd_akm4xxx_volume_put;
+               }
                /* register 4 & 5 */
-               ctl->private_value = AK_COMPOSE(idx/2, (idx%2) + 4, 0, 0);
-               ctl->private_data = ak;
-               err = snd_ctl_add(ak->card,
-                                 snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-                                             SNDRV_CTL_ELEM_ACCESS_WRITE));
+               if (ak->type == SND_AK5365)
+                       knew.private_value =
+                               AK_COMPOSE(idx/2, (idx%2) + 4, 0, 151) |
+                               AK_VOL_CVT | AK_IPGA;
+               else
+                       knew.private_value =
+                               AK_COMPOSE(idx/2, (idx%2) + 4, 0, 163) |
+                               AK_VOL_CVT | AK_IPGA;
+               knew.tlv.p = db_scale_vol_datt;
+               err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
                if (err < 0)
-                       goto __error;
+                       return err;
+
+               if (ak->type == SND_AK5365 && (idx % 2) == 0) {
+                       if (! ak->adc_info || 
+                           ! ak->adc_info[mixer_ch].switch_name)
+                               knew.name = "Capture Switch";
+                       else
+                               knew.name = ak->adc_info[mixer_ch].switch_name;
+                       knew.info = ak4xxx_switch_info;
+                       knew.get = ak4xxx_switch_get;
+                       knew.put = ak4xxx_switch_put;
+                       knew.access = 0;
+                       /* register 2, bit 0 (SMUTE): 0 = normal operation,
+                          1 = mute */
+                       knew.private_value =
+                               AK_COMPOSE(idx/2, 2, 0, 0) | AK_INVERT;
+                       err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
+                       if (err < 0)
+                               return err;
+               }
+
+               idx += num_stereo;
+               mixer_ch++;
        }
-       if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
-               num_emphs = 1;
-       else
-               num_emphs = ak->num_dacs / 2;
+       return 0;
+}
+
+static int build_deemphasis(struct snd_akm4xxx *ak, int num_emphs)
+{
+       int idx, err;
+       struct snd_kcontrol_new knew;
+
        for (idx = 0; idx < num_emphs; idx++) {
-               memset(ctl, 0, sizeof(*ctl));
-               strcpy(ctl->id.name, "Deemphasis");
-               ctl->id.index = idx + ak->idx_offset;
-               ctl->id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
-               ctl->count = 1;
-               ctl->info = snd_akm4xxx_deemphasis_info;
-               ctl->get = snd_akm4xxx_deemphasis_get;
-               ctl->put = snd_akm4xxx_deemphasis_put;
+               memset(&knew, 0, sizeof(knew));
+               knew.name = "Deemphasis";
+               knew.index = idx + ak->idx_offset;
+               knew.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+               knew.count = 1;
+               knew.info = snd_akm4xxx_deemphasis_info;
+               knew.get = snd_akm4xxx_deemphasis_get;
+               knew.put = snd_akm4xxx_deemphasis_put;
                switch (ak->type) {
                case SND_AK4524:
                case SND_AK4528:
                        /* register 3 */
-                       ctl->private_value = AK_COMPOSE(idx, 3, 0, 0);
+                       knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
                        break;
                case SND_AK4529: {
                        int shift = idx == 3 ? 6 : (2 - idx) * 2;
                        /* register 8 with shift */
-                       ctl->private_value = AK_COMPOSE(0, 8, shift, 0);
+                       knew.private_value = AK_COMPOSE(0, 8, shift, 0);
                        break;
                }
                case SND_AK4355:
                case SND_AK4358:
-                       ctl->private_value = AK_COMPOSE(idx, 3, 0, 0);
+                       knew.private_value = AK_COMPOSE(idx, 3, 0, 0);
                        break;
                case SND_AK4381:
-                       ctl->private_value = AK_COMPOSE(idx, 1, 1, 0);
+                       knew.private_value = AK_COMPOSE(idx, 1, 1, 0);
                        break;
+               default:
+                       return -EINVAL;
                }
-               ctl->private_data = ak;
-               err = snd_ctl_add(ak->card,
-                                 snd_ctl_new(ctl, SNDRV_CTL_ELEM_ACCESS_READ|
-                                             SNDRV_CTL_ELEM_ACCESS_WRITE));
+               err = snd_ctl_add(ak->card, snd_ctl_new1(&knew, ak));
                if (err < 0)
-                       goto __error;
+                       return err;
        }
-       err = 0;
-
- __error:
-       kfree(ctl);
-       return err;
+       return 0;
 }
 
+int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak)
+{
+       int err, num_emphs;
+
+       err = build_dac_controls(ak);
+       if (err < 0)
+               return err;
+
+       err = build_adc_controls(ak);
+       if (err < 0)
+               return err;
+
+       if (ak->type == SND_AK4355 || ak->type == SND_AK4358)
+               num_emphs = 1;
+       else
+               num_emphs = ak->num_dacs / 2;
+       err = build_deemphasis(ak, num_emphs);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+       
 EXPORT_SYMBOL(snd_akm4xxx_build_controls);
 
 static int __init alsa_akm4xxx_module_init(void)
index 8fcf2c151823c8a3de0f429b1b83bfe539a5e7a6..fd9b61eda0f37b907a1479ff4c6b3e3fec2fd20b 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <sound/core.h>
+#include <sound/tlv.h>
 #include <sound/ad1816a.h>
 
 #include <asm/io.h>
@@ -765,6 +766,13 @@ static int snd_ad1816a_put_mux(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
        return change;
 }
 
+#define AD1816A_SINGLE_TLV(xname, reg, shift, mask, invert, xtlv)      \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .info = snd_ad1816a_info_single, \
+  .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+  .tlv = { .p = (xtlv) } }
 #define AD1816A_SINGLE(xname, reg, shift, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_single, \
   .get = snd_ad1816a_get_single, .put = snd_ad1816a_put_single, \
@@ -822,6 +830,14 @@ static int snd_ad1816a_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
        return change;
 }
 
+#define AD1816A_DOUBLE_TLV(xname, reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .info = snd_ad1816a_info_double,              \
+  .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
+  .private_value = reg | (shift_left << 8) | (shift_right << 12) | (mask << 16) | (invert << 24), \
+  .tlv = { .p = (xtlv) } }
+
 #define AD1816A_DOUBLE(xname, reg, shift_left, shift_right, mask, invert) \
 { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .info = snd_ad1816a_info_double, \
   .get = snd_ad1816a_get_double, .put = snd_ad1816a_put_double, \
@@ -890,28 +906,44 @@ static int snd_ad1816a_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
        return change;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
 static struct snd_kcontrol_new snd_ad1816a_controls[] __devinitdata = {
 AD1816A_DOUBLE("Master Playback Switch", AD1816A_MASTER_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Master Playback Volume", AD1816A_MASTER_ATT, 8, 0, 31, 1,
+                  db_scale_5bit),
 AD1816A_DOUBLE("PCM Playback Switch", AD1816A_VOICE_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1),
+AD1816A_DOUBLE_TLV("PCM Playback Volume", AD1816A_VOICE_ATT, 8, 0, 63, 1,
+                  db_scale_6bit),
 AD1816A_DOUBLE("Line Playback Switch", AD1816A_LINE_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Line Playback Volume", AD1816A_LINE_GAIN_ATT, 8, 0, 31, 1,
+                  db_scale_5bit_12db_max),
 AD1816A_DOUBLE("CD Playback Switch", AD1816A_CD_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("CD Playback Volume", AD1816A_CD_GAIN_ATT, 8, 0, 31, 1,
+                  db_scale_5bit_12db_max),
 AD1816A_DOUBLE("Synth Playback Switch", AD1816A_SYNTH_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Synth Playback Volume", AD1816A_SYNTH_GAIN_ATT, 8, 0, 31, 1,
+                  db_scale_5bit_12db_max),
 AD1816A_DOUBLE("FM Playback Switch", AD1816A_FM_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1),
+AD1816A_DOUBLE_TLV("FM Playback Volume", AD1816A_FM_ATT, 8, 0, 63, 1,
+                  db_scale_6bit),
 AD1816A_SINGLE("Mic Playback Switch", AD1816A_MIC_GAIN_ATT, 15, 1, 1),
-AD1816A_SINGLE("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1),
+AD1816A_SINGLE_TLV("Mic Playback Volume", AD1816A_MIC_GAIN_ATT, 8, 31, 1,
+                  db_scale_5bit_12db_max),
 AD1816A_SINGLE("Mic Boost", AD1816A_MIC_GAIN_ATT, 14, 1, 0),
 AD1816A_DOUBLE("Video Playback Switch", AD1816A_VID_GAIN_ATT, 15, 7, 1, 1),
-AD1816A_DOUBLE("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1),
+AD1816A_DOUBLE_TLV("Video Playback Volume", AD1816A_VID_GAIN_ATT, 8, 0, 31, 1,
+                  db_scale_5bit_12db_max),
 AD1816A_SINGLE("Phone Capture Switch", AD1816A_PHONE_IN_GAIN_ATT, 15, 1, 1),
-AD1816A_SINGLE("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1),
+AD1816A_SINGLE_TLV("Phone Capture Volume", AD1816A_PHONE_IN_GAIN_ATT, 0, 15, 1,
+                  db_scale_4bit),
 AD1816A_SINGLE("Phone Playback Switch", AD1816A_PHONE_OUT_ATT, 7, 1, 1),
-AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
+AD1816A_SINGLE_TLV("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1,
+                  db_scale_5bit),
 {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
        .name = "Capture Source",
@@ -920,7 +952,8 @@ AD1816A_SINGLE("Phone Playback Volume", AD1816A_PHONE_OUT_ATT, 0, 31, 1),
        .put = snd_ad1816a_put_mux,
 },
 AD1816A_DOUBLE("Capture Switch", AD1816A_ADC_PGA, 15, 7, 1, 1),
-AD1816A_DOUBLE("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0),
+AD1816A_DOUBLE_TLV("Capture Volume", AD1816A_ADC_PGA, 8, 0, 15, 0,
+                  db_scale_rec_gain),
 AD1816A_SINGLE("3D Control - Switch", AD1816A_3D_PHAT_CTRL, 15, 1, 1),
 AD1816A_SINGLE("3D Control - Level", AD1816A_3D_PHAT_CTRL, 0, 15, 0),
 };
index e711f87d5fd1a3f20854da2d510d56ffd2b5bbb7..a6fbd5d1d62f2dfec49e74f1988b3854c6a58edc 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include <sound/ad1848.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/pcm_params.h>
 
 #include <asm/io.h>
@@ -118,6 +119,8 @@ void snd_ad1848_out(struct snd_ad1848 *chip,
 #endif
 }
 
+EXPORT_SYMBOL(snd_ad1848_out);
+
 static void snd_ad1848_dout(struct snd_ad1848 *chip,
                            unsigned char reg, unsigned char value)
 {
@@ -941,6 +944,8 @@ int snd_ad1848_create(struct snd_card *card,
        return 0;
 }
 
+EXPORT_SYMBOL(snd_ad1848_create);
+
 static struct snd_pcm_ops snd_ad1848_playback_ops = {
        .open =         snd_ad1848_playback_open,
        .close =        snd_ad1848_playback_close,
@@ -988,12 +993,16 @@ int snd_ad1848_pcm(struct snd_ad1848 *chip, int device, struct snd_pcm **rpcm)
        return 0;
 }
 
+EXPORT_SYMBOL(snd_ad1848_pcm);
+
 const struct snd_pcm_ops *snd_ad1848_get_pcm_ops(int direction)
 {
        return direction == SNDRV_PCM_STREAM_PLAYBACK ?
                &snd_ad1848_playback_ops : &snd_ad1848_capture_ops;
 }
 
+EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
+
 /*
  *  MIXER part
  */
@@ -1171,7 +1180,8 @@ static int snd_ad1848_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_e
 
 /*
  */
-int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int type, unsigned long value)
+int snd_ad1848_add_ctl_elem(struct snd_ad1848 *chip,
+                           const struct ad1848_mix_elem *c)
 {
        static struct snd_kcontrol_new newctls[] = {
                [AD1848_MIX_SINGLE] = {
@@ -1196,32 +1206,46 @@ int snd_ad1848_add_ctl(struct snd_ad1848 *chip, const char *name, int index, int
        struct snd_kcontrol *ctl;
        int err;
 
-       ctl = snd_ctl_new1(&newctls[type], chip);
+       ctl = snd_ctl_new1(&newctls[c->type], chip);
        if (! ctl)
                return -ENOMEM;
-       strlcpy(ctl->id.name, name, sizeof(ctl->id.name));
-       ctl->id.index = index;
-       ctl->private_value = value;
+       strlcpy(ctl->id.name, c->name, sizeof(ctl->id.name));
+       ctl->id.index = c->index;
+       ctl->private_value = c->private_value;
+       if (c->tlv) {
+               ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+               ctl->tlv.p = c->tlv;
+       }
        if ((err = snd_ctl_add(chip->card, ctl)) < 0)
                return err;
        return 0;
 }
 
+EXPORT_SYMBOL(snd_ad1848_add_ctl_elem);
+
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
 
 static struct ad1848_mix_elem snd_ad1848_controls[] = {
 AD1848_DOUBLE("PCM Playback Switch", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1),
+AD1848_DOUBLE_TLV("PCM Playback Volume", 0, AD1848_LEFT_OUTPUT, AD1848_RIGHT_OUTPUT, 0, 0, 63, 1,
+                 db_scale_6bit),
 AD1848_DOUBLE("Aux Playback Switch", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1),
+AD1848_DOUBLE_TLV("Aux Playback Volume", 0, AD1848_AUX1_LEFT_INPUT, AD1848_AUX1_RIGHT_INPUT, 0, 0, 31, 1,
+                 db_scale_5bit_12db_max),
 AD1848_DOUBLE("Aux Playback Switch", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 7, 7, 1, 1),
-AD1848_DOUBLE("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1),
-AD1848_DOUBLE("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0),
+AD1848_DOUBLE_TLV("Aux Playback Volume", 1, AD1848_AUX2_LEFT_INPUT, AD1848_AUX2_RIGHT_INPUT, 0, 0, 31, 1,
+                 db_scale_5bit_12db_max),
+AD1848_DOUBLE_TLV("Capture Volume", 0, AD1848_LEFT_INPUT, AD1848_RIGHT_INPUT, 0, 0, 15, 0,
+                 db_scale_rec_gain),
 {
        .name = "Capture Source",
        .type = AD1848_MIX_CAPTURE,
 },
 AD1848_SINGLE("Loopback Capture Switch", 0, AD1848_LOOPBACK, 0, 1, 0),
-AD1848_SINGLE("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0)
+AD1848_SINGLE_TLV("Loopback Capture Volume", 0, AD1848_LOOPBACK, 1, 63, 0,
+                 db_scale_6bit),
 };
                                         
 int snd_ad1848_mixer(struct snd_ad1848 *chip)
@@ -1245,12 +1269,7 @@ int snd_ad1848_mixer(struct snd_ad1848 *chip)
        return 0;
 }
 
-EXPORT_SYMBOL(snd_ad1848_out);
-EXPORT_SYMBOL(snd_ad1848_create);
-EXPORT_SYMBOL(snd_ad1848_pcm);
-EXPORT_SYMBOL(snd_ad1848_get_pcm_ops);
 EXPORT_SYMBOL(snd_ad1848_mixer);
-EXPORT_SYMBOL(snd_ad1848_add_ctl);
 
 /*
  *  INIT part
index 34998de9968c0ed071f79998785f00c801f43929..85818200333f33c6b1defe1de9fcd494c4496fd4 100644 (file)
@@ -2038,7 +2038,80 @@ MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
 static struct platform_device *platform_devices[SNDRV_CARDS];
 
 #ifdef CONFIG_PNP
-static int pnp_registered;
+static int pnp_registered, pnpc_registered;
+
+static struct pnp_device_id snd_audiodrive_pnpbiosids[] = {
+       { .id = "ESS1869" },
+       { .id = "" }            /* end */
+};
+
+MODULE_DEVICE_TABLE(pnp, snd_audiodrive_pnpbiosids);
+
+/* PnP main device initialization */
+static int __devinit snd_audiodrive_pnp_init_main(int dev, struct pnp_dev *pdev,
+                                                 struct pnp_resource_table *cfg)
+{
+       int err;
+
+       pnp_init_resource_table(cfg);
+       if (port[dev] != SNDRV_AUTO_PORT)
+               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
+       if (fm_port[dev] != SNDRV_AUTO_PORT)
+               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
+       if (mpu_port[dev] != SNDRV_AUTO_PORT)
+               pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
+       if (dma1[dev] != SNDRV_AUTO_DMA)
+               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
+       if (dma2[dev] != SNDRV_AUTO_DMA)
+               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
+       if (irq[dev] != SNDRV_AUTO_IRQ)
+               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
+       if (pnp_device_is_isapnp(pdev)) {
+               err = pnp_manual_config_dev(pdev, cfg, 0);
+               if (err < 0)
+                       snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
+       }
+       err = pnp_activate_dev(pdev);
+       if (err < 0) {
+               snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
+               return -EBUSY;
+       }
+       /* ok. hack using Vendor-Defined Card-Level registers */
+       /* skip csn and logdev initialization - already done in isapnp_configure */
+       if (pnp_device_is_isapnp(pdev)) {
+               isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
+               isapnp_write_byte(0x27, pnp_irq(pdev, 0));      /* Hardware Volume IRQ Number */
+               if (mpu_port[dev] != SNDRV_AUTO_PORT)
+                       isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
+               isapnp_write_byte(0x72, pnp_irq(pdev, 0));      /* second IRQ */
+               isapnp_cfg_end();
+       }
+       port[dev] = pnp_port_start(pdev, 0);
+       fm_port[dev] = pnp_port_start(pdev, 1);
+       mpu_port[dev] = pnp_port_start(pdev, 2);
+       dma1[dev] = pnp_dma(pdev, 0);
+       dma2[dev] = pnp_dma(pdev, 1);
+       irq[dev] = pnp_irq(pdev, 0);
+       snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
+       snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
+       return 0;
+}
+
+static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+                                       struct pnp_dev *pdev)
+{
+       struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
+
+       if (!cfg)
+               return -ENOMEM;
+       acard->dev = pdev;
+       if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
+               kfree(cfg);
+               return -EBUSY;
+       }
+       kfree(cfg);
+       return 0;
+}
 
 static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
        /* ESS 1868 (integrated on Compaq dual P-Pro motherboard and Genius 18PnP 3D) */
@@ -2061,13 +2134,11 @@ static struct pnp_card_device_id snd_audiodrive_pnpids[] = {
 
 MODULE_DEVICE_TABLE(pnp_card, snd_audiodrive_pnpids);
 
-static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
+static int __devinit snd_audiodrive_pnpc(int dev, struct snd_audiodrive *acard,
                                        struct pnp_card_link *card,
                                        const struct pnp_card_device_id *id)
 {
-       struct pnp_dev *pdev;
        struct pnp_resource_table * cfg = kmalloc(sizeof(struct pnp_resource_table), GFP_KERNEL);
-       int err;
 
        if (!cfg)
                return -ENOMEM;
@@ -2082,58 +2153,16 @@ static int __devinit snd_audiodrive_pnp(int dev, struct snd_audiodrive *acard,
                return -EBUSY;
        }
        /* Control port initialization */
-       err = pnp_activate_dev(acard->devc);
-       if (err < 0) {
+       if (pnp_activate_dev(acard->devc) < 0) {
                snd_printk(KERN_ERR PFX "PnP control configure failure (out of resources?)\n");
-               kfree(cfg);
                return -EAGAIN;
        }
        snd_printdd("pnp: port=0x%llx\n",
                        (unsigned long long)pnp_port_start(acard->devc, 0));
-       /* PnP initialization */
-       pdev = acard->dev;
-       pnp_init_resource_table(cfg);
-       if (port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[0], port[dev], 16);
-       if (fm_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[1], fm_port[dev], 4);
-       if (mpu_port[dev] != SNDRV_AUTO_PORT)
-               pnp_resource_change(&cfg->port_resource[2], mpu_port[dev], 2);
-       if (dma1[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[0], dma1[dev], 1);
-       if (dma2[dev] != SNDRV_AUTO_DMA)
-               pnp_resource_change(&cfg->dma_resource[1], dma2[dev], 1);
-       if (irq[dev] != SNDRV_AUTO_IRQ)
-               pnp_resource_change(&cfg->irq_resource[0], irq[dev], 1);
-       err = pnp_manual_config_dev(pdev, cfg, 0);
-       if (err < 0)
-               snd_printk(KERN_ERR PFX "PnP manual resources are invalid, using auto config\n");
-       err = pnp_activate_dev(pdev);
-       if (err < 0) {
-               snd_printk(KERN_ERR PFX "PnP configure failure (out of resources?)\n");
+       if (snd_audiodrive_pnp_init_main(dev, acard->dev, cfg) < 0) {
                kfree(cfg);
                return -EBUSY;
        }
-       /* ok. hack using Vendor-Defined Card-Level registers */
-       /* skip csn and logdev initialization - already done in isapnp_configure */
-       if (pnp_device_is_isapnp(pdev)) {
-               isapnp_cfg_begin(isapnp_card_number(pdev), isapnp_csn_number(pdev));
-               isapnp_write_byte(0x27, pnp_irq(pdev, 0));      /* Hardware Volume IRQ Number */
-               if (mpu_port[dev] != SNDRV_AUTO_PORT)
-                       isapnp_write_byte(0x28, pnp_irq(pdev, 0)); /* MPU-401 IRQ Number */
-               isapnp_write_byte(0x72, pnp_irq(pdev, 0));      /* second IRQ */
-               isapnp_cfg_end();
-       } else {
-               snd_printk(KERN_ERR PFX "unable to install ISA PnP hack, expect malfunction\n");
-       }
-       port[dev] = pnp_port_start(pdev, 0);
-       fm_port[dev] = pnp_port_start(pdev, 1);
-       mpu_port[dev] = pnp_port_start(pdev, 2);
-       dma1[dev] = pnp_dma(pdev, 0);
-       dma2[dev] = pnp_dma(pdev, 1);
-       irq[dev] = pnp_irq(pdev, 0);
-       snd_printdd("PnP ES18xx: port=0x%lx, fm port=0x%lx, mpu port=0x%lx\n", port[dev], fm_port[dev], mpu_port[dev]);
-       snd_printdd("PnP ES18xx: dma1=%i, dma2=%i, irq=%i\n", dma1[dev], dma2[dev], irq[dev]);
        kfree(cfg);
        return 0;
 }
@@ -2302,7 +2331,69 @@ static struct platform_driver snd_es18xx_nonpnp_driver = {
 #ifdef CONFIG_PNP
 static unsigned int __devinitdata es18xx_pnp_devices;
 
-static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
+static int __devinit snd_audiodrive_pnp_detect(struct pnp_dev *pdev,
+                                           const struct pnp_device_id *id)
+{
+       static int dev;
+       int err;
+       struct snd_card *card;
+
+       if (pnp_device_is_isapnp(pdev))
+               return -ENOENT; /* we have another procedure - card */
+       for (; dev < SNDRV_CARDS; dev++) {
+               if (enable[dev] && isapnp[dev])
+                       break;
+       }
+       if (dev >= SNDRV_CARDS)
+               return -ENODEV;
+
+       card = snd_es18xx_card_new(dev);
+       if (! card)
+               return -ENOMEM;
+       if ((err = snd_audiodrive_pnp(dev, card->private_data, pdev)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       snd_card_set_dev(card, &pdev->dev);
+       if ((err = snd_audiodrive_probe(card, dev)) < 0) {
+               snd_card_free(card);
+               return err;
+       }
+       pnp_set_drvdata(pdev, card);
+       dev++;
+       es18xx_pnp_devices++;
+       return 0;
+}
+
+static void __devexit snd_audiodrive_pnp_remove(struct pnp_dev * pdev)
+{
+       snd_card_free(pnp_get_drvdata(pdev));
+       pnp_set_drvdata(pdev, NULL);
+}
+
+#ifdef CONFIG_PM
+static int snd_audiodrive_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
+{
+       return snd_es18xx_suspend(pnp_get_drvdata(pdev), state);
+}
+static int snd_audiodrive_pnp_resume(struct pnp_dev *pdev)
+{
+       return snd_es18xx_resume(pnp_get_drvdata(pdev));
+}
+#endif
+
+static struct pnp_driver es18xx_pnp_driver = {
+       .name = "es18xx-pnpbios",
+       .id_table = snd_audiodrive_pnpbiosids,
+       .probe = snd_audiodrive_pnp_detect,
+       .remove = __devexit_p(snd_audiodrive_pnp_remove),
+#ifdef CONFIG_PM
+       .suspend = snd_audiodrive_pnp_suspend,
+       .resume = snd_audiodrive_pnp_resume,
+#endif
+};
+
+static int __devinit snd_audiodrive_pnpc_detect(struct pnp_card_link *pcard,
                                               const struct pnp_card_device_id *pid)
 {
        static int dev;
@@ -2320,7 +2411,7 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
        if (! card)
                return -ENOMEM;
 
-       if ((res = snd_audiodrive_pnp(dev, card->private_data, pcard, pid)) < 0) {
+       if ((res = snd_audiodrive_pnpc(dev, card->private_data, pcard, pid)) < 0) {
                snd_card_free(card);
                return res;
        }
@@ -2336,19 +2427,19 @@ static int __devinit snd_audiodrive_pnp_detect(struct pnp_card_link *pcard,
        return 0;
 }
 
-static void __devexit snd_audiodrive_pnp_remove(struct pnp_card_link * pcard)
+static void __devexit snd_audiodrive_pnpc_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_audiodrive_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state)
+static int snd_audiodrive_pnpc_suspend(struct pnp_card_link *pcard, pm_message_t state)
 {
        return snd_es18xx_suspend(pnp_get_card_drvdata(pcard), state);
 }
 
-static int snd_audiodrive_pnp_resume(struct pnp_card_link *pcard)
+static int snd_audiodrive_pnpc_resume(struct pnp_card_link *pcard)
 {
        return snd_es18xx_resume(pnp_get_card_drvdata(pcard));
 }
@@ -2359,11 +2450,11 @@ static struct pnp_card_driver es18xx_pnpc_driver = {
        .flags = PNP_DRIVER_RES_DISABLE,
        .name = "es18xx",
        .id_table = snd_audiodrive_pnpids,
-       .probe = snd_audiodrive_pnp_detect,
-       .remove = __devexit_p(snd_audiodrive_pnp_remove),
+       .probe = snd_audiodrive_pnpc_detect,
+       .remove = __devexit_p(snd_audiodrive_pnpc_remove),
 #ifdef CONFIG_PM
-       .suspend        = snd_audiodrive_pnp_suspend,
-       .resume         = snd_audiodrive_pnp_resume,
+       .suspend        = snd_audiodrive_pnpc_suspend,
+       .resume         = snd_audiodrive_pnpc_resume,
 #endif
 };
 #endif /* CONFIG_PNP */
@@ -2373,8 +2464,10 @@ static void __init_or_module snd_es18xx_unregister_all(void)
        int i;
 
 #ifdef CONFIG_PNP
-       if (pnp_registered)
+       if (pnpc_registered)
                pnp_unregister_card_driver(&es18xx_pnpc_driver);
+       if (pnp_registered)
+               pnp_unregister_driver(&es18xx_pnp_driver);
 #endif
        for (i = 0; i < ARRAY_SIZE(platform_devices); ++i)
                platform_device_unregister(platform_devices[i]);
@@ -2405,11 +2498,13 @@ static int __init alsa_card_es18xx_init(void)
        }
 
 #ifdef CONFIG_PNP
-       err = pnp_register_card_driver(&es18xx_pnpc_driver);
-       if (!err) {
+       err = pnp_register_driver(&es18xx_pnp_driver);
+       if (!err)
                pnp_registered = 1;
-               cards += es18xx_pnp_devices;
-       }
+       err = pnp_register_card_driver(&es18xx_pnpc_driver);
+       if (!err)
+               pnpc_registered = 1;
+       cards += es18xx_pnp_devices;
 #endif
 
        if(!cards) {
index 4080255007d5935df6f9a1cf289dd0d4dd47a29d..80f0a83818b2714f258c94f66d6dbe21fced0c4a 100644 (file)
@@ -61,13 +61,13 @@ static long long snd_gf1_mem_proc_llseek(struct snd_info_entry *entry,
        struct gus_proc_private *priv = entry->private_data;
 
        switch (orig) {
-       case 0: /* SEEK_SET */
+       case SEEK_SET:
                file->f_pos = offset;
                break;
-       case 1: /* SEEK_CUR */
+       case SEEK_CUR:
                file->f_pos += offset;
                break;
-       case 2: /* SEEK_END, offset is negative */
+       case SEEK_END: /* offset is negative */
                file->f_pos = priv->size + offset;
                break;
        default:
index 4031b61b797fe895310c88bc3525f11825495cca..da92bf6c392b87d761ff21de1a96b9127731ab12 100644 (file)
@@ -33,6 +33,7 @@
 #include <sound/mpu401.h>
 #include <sound/opl3.h>
 #include <sound/initval.h>
+#include <sound/tlv.h>
 
 #include <asm/io.h>
 
@@ -337,6 +338,14 @@ static irqreturn_t snd_opl3sa2_interrupt(int irq, void *dev_id, struct pt_regs *
   .info = snd_opl3sa2_info_single, \
   .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
   .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) }
+#define OPL3SA2_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .index = xindex, \
+  .info = snd_opl3sa2_info_single, \
+  .get = snd_opl3sa2_get_single, .put = snd_opl3sa2_put_single, \
+  .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24), \
+  .tlv = { .p = (xtlv) } }
 
 static int snd_opl3sa2_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -395,6 +404,14 @@ static int snd_opl3sa2_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_
   .info = snd_opl3sa2_info_double, \
   .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
   .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) }
+#define OPL3SA2_DOUBLE_TLV(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .index = xindex, \
+  .info = snd_opl3sa2_info_double, \
+  .get = snd_opl3sa2_get_double, .put = snd_opl3sa2_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = (xtlv) } }
 
 static int snd_opl3sa2_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -469,11 +486,16 @@ static int snd_opl3sa2_put_double(struct snd_kcontrol *kcontrol, struct snd_ctl_
        return change;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_master, -3000, 200, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+
 static struct snd_kcontrol_new snd_opl3sa2_controls[] = {
 OPL3SA2_DOUBLE("Master Playback Switch", 0, 0x07, 0x08, 7, 7, 1, 1),
-OPL3SA2_DOUBLE("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1),
+OPL3SA2_DOUBLE_TLV("Master Playback Volume", 0, 0x07, 0x08, 0, 0, 15, 1,
+                  db_scale_master),
 OPL3SA2_SINGLE("Mic Playback Switch", 0, 0x09, 7, 1, 1),
-OPL3SA2_SINGLE("Mic Playback Volume", 0, 0x09, 0, 31, 1)
+OPL3SA2_SINGLE_TLV("Mic Playback Volume", 0, 0x09, 0, 31, 1,
+                  db_scale_5bit_12db_max),
 };
 
 static struct snd_kcontrol_new snd_opl3sa2_tone_controls[] = {
index e49c0fe21b0d13d12283c5fe908e7cc60e74df6d..8a6b1803c763e1f71bc30fc69d010a9f42839042 100644 (file)
@@ -475,6 +475,7 @@ config SND_FM801_TEA575X
        depends on SND_FM801_TEA575X_BOOL
        default SND_FM801
        select VIDEO_V4L1
+       select VIDEO_DEV
 
 config SND_HDA_INTEL
        tristate "Intel HD Audio"
@@ -743,4 +744,17 @@ config SND_YMFPCI
          To compile this driver as a module, choose M here: the module
          will be called snd-ymfpci.
 
+config SND_AC97_POWER_SAVE
+       bool "AC97 Power-Saving Mode"
+       depends on SND_AC97_CODEC && EXPERIMENTAL
+       default n
+       help
+         Say Y here to enable the aggressive power-saving support of
+         AC97 codecs.  In this mode, the power-mode is dynamically
+         controlled at each open/close.
+
+         The mode is activated by passing power_save=1 option to
+         snd-ac97-codec driver.  You can toggle it dynamically over
+         sysfs, too.
+
 endmenu
index 51e83d7a839a3ccf09d6d2134eaed858ef638922..a79e91850ba361bbdb05dfdd65a3ed59638fc47e 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/tlv.h>
 #include <sound/ac97_codec.h>
 #include <sound/asoundef.h>
 #include <sound/initval.h>
@@ -47,6 +48,11 @@ static int enable_loopback;
 module_param(enable_loopback, bool, 0444);
 MODULE_PARM_DESC(enable_loopback, "Enable AC97 ADC/DAC Loopback Control");
 
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static int power_save;
+module_param(power_save, bool, 0644);
+MODULE_PARM_DESC(power_save, "Enable AC97 power-saving control");
+#endif
 /*
 
  */
@@ -151,7 +157,7 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 { 0x4e534300, 0xffffffff, "LM4540,43,45,46,48",        NULL,           NULL }, // only guess --jk
 { 0x4e534331, 0xffffffff, "LM4549",            NULL,           NULL },
 { 0x4e534350, 0xffffffff, "LM4550",            patch_lm4550,   NULL }, // volume wrap fix 
-{ 0x50534304, 0xffffffff, "UCB1400",           NULL,           NULL },
+{ 0x50534304, 0xffffffff, "UCB1400",           patch_ucb1400,  NULL },
 { 0x53494c20, 0xffffffe0, "Si3036,8",          mpatch_si3036,  mpatch_si3036, AC97_MODEM_PATCH },
 { 0x54524102, 0xffffffff, "TR28022",           NULL,           NULL },
 { 0x54524106, 0xffffffff, "TR28026",           NULL,           NULL },
@@ -187,6 +193,8 @@ static const struct ac97_codec_id snd_ac97_codec_ids[] = {
 };
 
 
+static void update_power_regs(struct snd_ac97 *ac97);
+
 /*
  *  I/O routines
  */
@@ -554,6 +562,18 @@ int snd_ac97_put_volsw(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value
        }
        err = snd_ac97_update_bits(ac97, reg, val_mask, val);
        snd_ac97_page_restore(ac97, page_save);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       /* check analog mixer power-down */
+       if ((val_mask & 0x8000) &&
+           (kcontrol->private_value & (1<<30))) {
+               if (val & 0x8000)
+                       ac97->power_up &= ~(1 << (reg>>1));
+               else
+                       ac97->power_up |= 1 << (reg>>1);
+               if (power_save)
+                       update_power_regs(ac97);
+       }
+#endif
        return err;
 }
 
@@ -962,6 +982,10 @@ static int snd_ac97_bus_dev_free(struct snd_device *device)
 static int snd_ac97_free(struct snd_ac97 *ac97)
 {
        if (ac97) {
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+               if (ac97->power_workq)
+                       destroy_workqueue(ac97->power_workq);
+#endif
                snd_ac97_proc_done(ac97);
                if (ac97->bus)
                        ac97->bus->codec[ac97->num] = NULL;
@@ -1117,7 +1141,9 @@ struct snd_kcontrol *snd_ac97_cnew(const struct snd_kcontrol_new *_template, str
 /*
  * create mute switch(es) for normal stereo controls
  */
-static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg, int check_stereo, struct snd_ac97 *ac97)
+static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
+                                    int check_stereo, int check_amix,
+                                    struct snd_ac97 *ac97)
 {
        struct snd_kcontrol *kctl;
        int err;
@@ -1137,10 +1163,14 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
        }
        if (mute_mask == 0x8080) {
                struct snd_kcontrol_new tmp = AC97_DOUBLE(name, reg, 15, 7, 1, 1);
+               if (check_amix)
+                       tmp.private_value |= (1 << 30);
                tmp.index = ac97->num;
                kctl = snd_ctl_new1(&tmp, ac97);
        } else {
                struct snd_kcontrol_new tmp = AC97_SINGLE(name, reg, 15, 1, 1);
+               if (check_amix)
+                       tmp.private_value |= (1 << 30);
                tmp.index = ac97->num;
                kctl = snd_ctl_new1(&tmp, ac97);
        }
@@ -1152,6 +1182,32 @@ static int snd_ac97_cmute_new_stereo(struct snd_card *card, char *name, int reg,
        return 0;
 }
 
+/*
+ * set dB information
+ */
+static DECLARE_TLV_DB_SCALE(db_scale_4bit, -4500, 300, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit, -4650, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_6bit, -9450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_12db_max, -3450, 150, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_rec_gain, 0, 150, 0);
+
+static unsigned int *find_db_scale(unsigned int maxval)
+{
+       switch (maxval) {
+       case 0x0f: return db_scale_4bit;
+       case 0x1f: return db_scale_5bit;
+       case 0x3f: return db_scale_6bit;
+       }
+       return NULL;
+}
+
+static void set_tlv_db_scale(struct snd_kcontrol *kctl, unsigned int *tlv)
+{      
+       kctl->tlv.p = tlv;
+       if (tlv)
+               kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+}
+
 /*
  * create a volume for normal stereo/mono controls
  */
@@ -1174,6 +1230,10 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
                tmp.index = ac97->num;
                kctl = snd_ctl_new1(&tmp, ac97);
        }
+       if (reg >= AC97_PHONE && reg <= AC97_PCM)
+               set_tlv_db_scale(kctl, db_scale_5bit_12db_max);
+       else
+               set_tlv_db_scale(kctl, find_db_scale(lo_max));
        err = snd_ctl_add(card, kctl);
        if (err < 0)
                return err;
@@ -1186,7 +1246,9 @@ static int snd_ac97_cvol_new(struct snd_card *card, char *name, int reg, unsigne
 /*
  * create a mute-switch and a volume for normal stereo/mono controls
  */
-static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int reg, int check_stereo, struct snd_ac97 *ac97)
+static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx,
+                                   int reg, int check_stereo, int check_amix,
+                                   struct snd_ac97 *ac97)
 {
        int err;
        char name[44];
@@ -1197,7 +1259,9 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int
 
        if (snd_ac97_try_bit(ac97, reg, 15)) {
                sprintf(name, "%s Switch", pfx);
-               if ((err = snd_ac97_cmute_new_stereo(card, name, reg, check_stereo, ac97)) < 0)
+               if ((err = snd_ac97_cmute_new_stereo(card, name, reg,
+                                                    check_stereo, check_amix,
+                                                    ac97)) < 0)
                        return err;
        }
        check_volume_resolution(ac97, reg, &lo_max, &hi_max);
@@ -1209,8 +1273,10 @@ static int snd_ac97_cmix_new_stereo(struct snd_card *card, const char *pfx, int
        return 0;
 }
 
-#define snd_ac97_cmix_new(card, pfx, reg, ac97)        snd_ac97_cmix_new_stereo(card, pfx, reg, 0, ac97)
-#define snd_ac97_cmute_new(card, name, reg, ac97)      snd_ac97_cmute_new_stereo(card, name, reg, 0, ac97)
+#define snd_ac97_cmix_new(card, pfx, reg, acheck, ac97) \
+       snd_ac97_cmix_new_stereo(card, pfx, reg, 0, acheck, ac97)
+#define snd_ac97_cmute_new(card, name, reg, acheck, ac97) \
+       snd_ac97_cmute_new_stereo(card, name, reg, 0, acheck, ac97)
 
 static unsigned int snd_ac97_determine_spdif_rates(struct snd_ac97 *ac97);
 
@@ -1226,9 +1292,11 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        /* AD claims to remove this control from AD1887, although spec v2.2 does not allow this */
        if (snd_ac97_try_volume_mix(ac97, AC97_MASTER)) {
                if (ac97->flags & AC97_HAS_NO_MASTER_VOL)
-                       err = snd_ac97_cmute_new(card, "Master Playback Switch", AC97_MASTER, ac97);
+                       err = snd_ac97_cmute_new(card, "Master Playback Switch",
+                                                AC97_MASTER, 0, ac97);
                else
-                       err = snd_ac97_cmix_new(card, "Master Playback", AC97_MASTER, ac97);
+                       err = snd_ac97_cmix_new(card, "Master Playback",
+                                               AC97_MASTER, 0, ac97);
                if (err < 0)
                        return err;
        }
@@ -1245,6 +1313,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 0, &max);
                kctl->private_value &= ~(0xff << 16);
                kctl->private_value |= (int)max << 16;
+               set_tlv_db_scale(kctl, find_db_scale(max));
                snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max);
        }
 
@@ -1258,6 +1327,7 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                snd_ac97_change_volume_params2(ac97, AC97_CENTER_LFE_MASTER, 8, &max);
                kctl->private_value &= ~(0xff << 16);
                kctl->private_value |= (int)max << 16;
+               set_tlv_db_scale(kctl, find_db_scale(max));
                snd_ac97_write_cache(ac97, AC97_CENTER_LFE_MASTER, ac97->regs[AC97_CENTER_LFE_MASTER] | max << 8);
        }
 
@@ -1265,19 +1335,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        if ((snd_ac97_try_volume_mix(ac97, AC97_SURROUND_MASTER)) 
                && !(ac97->flags & AC97_AD_MULTI)) {
                /* Surround Master (0x38) is with stereo mutes */
-               if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback", AC97_SURROUND_MASTER, 1, ac97)) < 0)
+               if ((err = snd_ac97_cmix_new_stereo(card, "Surround Playback",
+                                                   AC97_SURROUND_MASTER, 1, 0,
+                                                   ac97)) < 0)
                        return err;
        }
 
        /* build headphone controls */
        if (snd_ac97_try_volume_mix(ac97, AC97_HEADPHONE)) {
-               if ((err = snd_ac97_cmix_new(card, "Headphone Playback", AC97_HEADPHONE, ac97)) < 0)
+               if ((err = snd_ac97_cmix_new(card, "Headphone Playback",
+                                            AC97_HEADPHONE, 0, ac97)) < 0)
                        return err;
        }
        
        /* build master mono controls */
        if (snd_ac97_try_volume_mix(ac97, AC97_MASTER_MONO)) {
-               if ((err = snd_ac97_cmix_new(card, "Master Mono Playback", AC97_MASTER_MONO, ac97)) < 0)
+               if ((err = snd_ac97_cmix_new(card, "Master Mono Playback",
+                                            AC97_MASTER_MONO, 0, ac97)) < 0)
                        return err;
        }
        
@@ -1301,8 +1375,9 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                ((ac97->flags & AC97_HAS_PC_BEEP) ||
            snd_ac97_try_volume_mix(ac97, AC97_PC_BEEP))) {
                for (idx = 0; idx < 2; idx++)
-                       if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
+                       if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_pc_beep[idx], ac97))) < 0)
                                return err;
+               set_tlv_db_scale(kctl, db_scale_4bit);
                snd_ac97_write_cache(ac97, AC97_PC_BEEP,
                                     snd_ac97_read(ac97, AC97_PC_BEEP) | 0x801e);
        }
@@ -1310,7 +1385,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        /* build Phone controls */
        if (!(ac97->flags & AC97_HAS_NO_PHONE)) {
                if (snd_ac97_try_volume_mix(ac97, AC97_PHONE)) {
-                       if ((err = snd_ac97_cmix_new(card, "Phone Playback", AC97_PHONE, ac97)) < 0)
+                       if ((err = snd_ac97_cmix_new(card, "Phone Playback",
+                                                    AC97_PHONE, 1, ac97)) < 0)
                                return err;
                }
        }
@@ -1318,7 +1394,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        /* build MIC controls */
        if (!(ac97->flags & AC97_HAS_NO_MIC)) {
                if (snd_ac97_try_volume_mix(ac97, AC97_MIC)) {
-                       if ((err = snd_ac97_cmix_new(card, "Mic Playback", AC97_MIC, ac97)) < 0)
+                       if ((err = snd_ac97_cmix_new(card, "Mic Playback",
+                                                    AC97_MIC, 1, ac97)) < 0)
                                return err;
                        if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_boost, ac97))) < 0)
                                return err;
@@ -1327,14 +1404,16 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
 
        /* build Line controls */
        if (snd_ac97_try_volume_mix(ac97, AC97_LINE)) {
-               if ((err = snd_ac97_cmix_new(card, "Line Playback", AC97_LINE, ac97)) < 0)
+               if ((err = snd_ac97_cmix_new(card, "Line Playback",
+                                            AC97_LINE, 1, ac97)) < 0)
                        return err;
        }
        
        /* build CD controls */
        if (!(ac97->flags & AC97_HAS_NO_CD)) {
                if (snd_ac97_try_volume_mix(ac97, AC97_CD)) {
-                       if ((err = snd_ac97_cmix_new(card, "CD Playback", AC97_CD, ac97)) < 0)
+                       if ((err = snd_ac97_cmix_new(card, "CD Playback",
+                                                    AC97_CD, 1, ac97)) < 0)
                                return err;
                }
        }
@@ -1342,7 +1421,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        /* build Video controls */
        if (!(ac97->flags & AC97_HAS_NO_VIDEO)) {
                if (snd_ac97_try_volume_mix(ac97, AC97_VIDEO)) {
-                       if ((err = snd_ac97_cmix_new(card, "Video Playback", AC97_VIDEO, ac97)) < 0)
+                       if ((err = snd_ac97_cmix_new(card, "Video Playback",
+                                                    AC97_VIDEO, 1, ac97)) < 0)
                                return err;
                }
        }
@@ -1350,7 +1430,8 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        /* build Aux controls */
        if (!(ac97->flags & AC97_HAS_NO_AUX)) {
                if (snd_ac97_try_volume_mix(ac97, AC97_AUX)) {
-                       if ((err = snd_ac97_cmix_new(card, "Aux Playback", AC97_AUX, ac97)) < 0)
+                       if ((err = snd_ac97_cmix_new(card, "Aux Playback",
+                                                    AC97_AUX, 1, ac97)) < 0)
                                return err;
                }
        }
@@ -1363,31 +1444,38 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                else
                        init_val = 0x9f1f;
                for (idx = 0; idx < 2; idx++)
-                       if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
+                       if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_pcm[idx], ac97))) < 0)
                                return err;
+               set_tlv_db_scale(kctl, db_scale_5bit);
                ac97->spec.ad18xx.pcmreg[0] = init_val;
                if (ac97->scaps & AC97_SCAP_SURROUND_DAC) {
                        for (idx = 0; idx < 2; idx++)
-                               if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
+                               if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_surround[idx], ac97))) < 0)
                                        return err;
+                       set_tlv_db_scale(kctl, db_scale_5bit);
                        ac97->spec.ad18xx.pcmreg[1] = init_val;
                }
                if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) {
                        for (idx = 0; idx < 2; idx++)
-                               if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
+                               if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_center[idx], ac97))) < 0)
                                        return err;
+                       set_tlv_db_scale(kctl, db_scale_5bit);
                        for (idx = 0; idx < 2; idx++)
-                               if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
+                               if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_ad18xx_lfe[idx], ac97))) < 0)
                                        return err;
+                       set_tlv_db_scale(kctl, db_scale_5bit);
                        ac97->spec.ad18xx.pcmreg[2] = init_val;
                }
                snd_ac97_write_cache(ac97, AC97_PCM, init_val);
        } else {
                if (!(ac97->flags & AC97_HAS_NO_STD_PCM)) {
                        if (ac97->flags & AC97_HAS_NO_PCM_VOL)
-                               err = snd_ac97_cmute_new(card, "PCM Playback Switch", AC97_PCM, ac97);
+                               err = snd_ac97_cmute_new(card,
+                                                        "PCM Playback Switch",
+                                                        AC97_PCM, 0, ac97);
                        else
-                               err = snd_ac97_cmix_new(card, "PCM Playback", AC97_PCM, ac97);
+                               err = snd_ac97_cmix_new(card, "PCM Playback",
+                                                       AC97_PCM, 0, ac97);
                        if (err < 0)
                                return err;
                }
@@ -1398,19 +1486,23 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
                if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_src, ac97))) < 0)
                        return err;
                if (snd_ac97_try_bit(ac97, AC97_REC_GAIN, 15)) {
-                       if ((err = snd_ac97_cmute_new(card, "Capture Switch", AC97_REC_GAIN, ac97)) < 0)
+                       err = snd_ac97_cmute_new(card, "Capture Switch",
+                                                AC97_REC_GAIN, 0, ac97);
+                       if (err < 0)
                                return err;
                }
-               if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
+               if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_control_capture_vol, ac97))) < 0)
                        return err;
+               set_tlv_db_scale(kctl, db_scale_rec_gain);
                snd_ac97_write_cache(ac97, AC97_REC_SEL, 0x0000);
                snd_ac97_write_cache(ac97, AC97_REC_GAIN, 0x0000);
        }
        /* build MIC Capture controls */
        if (snd_ac97_try_volume_mix(ac97, AC97_REC_GAIN_MIC)) {
                for (idx = 0; idx < 2; idx++)
-                       if ((err = snd_ctl_add(card, snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
+                       if ((err = snd_ctl_add(card, kctl = snd_ac97_cnew(&snd_ac97_controls_mic_capture[idx], ac97))) < 0)
                                return err;
+               set_tlv_db_scale(kctl, db_scale_rec_gain);
                snd_ac97_write_cache(ac97, AC97_REC_GAIN_MIC, 0x0000);
        }
 
@@ -1481,6 +1573,12 @@ static int snd_ac97_mixer_build(struct snd_ac97 * ac97)
        }
 
        /* build S/PDIF controls */
+
+       /* Hack for ASUS P5P800-VM, which does not indicate S/PDIF capability */
+       if (ac97->subsystem_vendor == 0x1043 &&
+           ac97->subsystem_device == 0x810f)
+               ac97->ext_id |= AC97_EI_SPDIF;
+
        if ((ac97->ext_id & AC97_EI_SPDIF) && !(ac97->scaps & AC97_SCAP_NO_SPDIF)) {
                if (ac97->build_ops->build_spdif) {
                        if ((err = ac97->build_ops->build_spdif(ac97)) < 0)
@@ -1817,18 +1915,25 @@ static int snd_ac97_dev_register(struct snd_device *device)
        return 0;
 }
 
-/* unregister ac97 codec */
-static int snd_ac97_dev_unregister(struct snd_device *device)
+/* disconnect ac97 codec */
+static int snd_ac97_dev_disconnect(struct snd_device *device)
 {
        struct snd_ac97 *ac97 = device->device_data;
        if (ac97->dev.bus)
                device_unregister(&ac97->dev);
-       return snd_ac97_free(ac97);
+       return 0;
 }
 
 /* build_ops to do nothing */
 static struct snd_ac97_build_ops null_build_ops;
 
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+static void do_update_power(void *data)
+{
+       update_power_regs(data);
+}
+#endif
+
 /**
  * snd_ac97_mixer - create an Codec97 component
  * @bus: the AC97 bus which codec is attached to
@@ -1860,7 +1965,7 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
        static struct snd_device_ops ops = {
                .dev_free =     snd_ac97_dev_free,
                .dev_register = snd_ac97_dev_register,
-               .dev_unregister =       snd_ac97_dev_unregister,
+               .dev_disconnect =       snd_ac97_dev_disconnect,
        };
 
        snd_assert(rac97 != NULL, return -EINVAL);
@@ -1883,6 +1988,10 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
        bus->codec[ac97->num] = ac97;
        mutex_init(&ac97->reg_mutex);
        mutex_init(&ac97->page_mutex);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       ac97->power_workq = create_workqueue("ac97");
+       INIT_WORK(&ac97->power_work, do_update_power, ac97);
+#endif
 
 #ifdef CONFIG_PCI
        if (ac97->pci) {
@@ -2117,15 +2226,8 @@ int snd_ac97_mixer(struct snd_ac97_bus *bus, struct snd_ac97_template *template,
                        return -ENOMEM;
                }
        }
-       /* make sure the proper powerdown bits are cleared */
-       if (ac97->scaps && ac97_is_audio(ac97)) {
-               reg = snd_ac97_read(ac97, AC97_EXTENDED_STATUS);
-               if (ac97->scaps & AC97_SCAP_SURROUND_DAC) 
-                       reg &= ~AC97_EA_PRJ;
-               if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC) 
-                       reg &= ~(AC97_EA_PRI | AC97_EA_PRK);
-               snd_ac97_write_cache(ac97, AC97_EXTENDED_STATUS, reg);
-       }
+       if (ac97_is_audio(ac97))
+               update_power_regs(ac97);
        snd_ac97_proc_init(ac97);
        if ((err = snd_device_new(card, SNDRV_DEV_CODEC, ac97, &ops)) < 0) {
                snd_ac97_free(ac97);
@@ -2153,19 +2255,152 @@ static void snd_ac97_powerdown(struct snd_ac97 *ac97)
                snd_ac97_write(ac97, AC97_HEADPHONE, 0x9f9f);
        }
 
-       power = ac97->regs[AC97_POWERDOWN] | 0x8000;    /* EAPD */
-       power |= 0x4000;        /* Headphone amplifier powerdown */
-       power |= 0x0300;        /* ADC & DAC powerdown */
-       snd_ac97_write(ac97, AC97_POWERDOWN, power);
-       udelay(100);
-       power |= 0x0400;        /* Analog Mixer powerdown (Vref on) */
+       /* surround, CLFE, mic powerdown */
+       power = ac97->regs[AC97_EXTENDED_STATUS];
+       if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+               power |= AC97_EA_PRJ;
+       if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+               power |= AC97_EA_PRI | AC97_EA_PRK;
+       power |= AC97_EA_PRL;
+       snd_ac97_write(ac97, AC97_EXTENDED_STATUS, power);
+
+       /* powerdown external amplifier */
+       if (ac97->scaps & AC97_SCAP_INV_EAPD)
+               power = ac97->regs[AC97_POWERDOWN] & ~AC97_PD_EAPD;
+       else if (! (ac97->scaps & AC97_SCAP_EAPD_LED))
+               power = ac97->regs[AC97_POWERDOWN] | AC97_PD_EAPD;
+       power |= AC97_PD_PR6;   /* Headphone amplifier powerdown */
+       power |= AC97_PD_PR0 | AC97_PD_PR1;     /* ADC & DAC powerdown */
        snd_ac97_write(ac97, AC97_POWERDOWN, power);
        udelay(100);
-#if 0
-       /* FIXME: this causes click noises on some boards at resume */
-       power |= 0x3800;        /* AC-link powerdown, internal Clk disable */
+       power |= AC97_PD_PR2 | AC97_PD_PR3;     /* Analog Mixer powerdown */
        snd_ac97_write(ac97, AC97_POWERDOWN, power);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       if (power_save) {
+               udelay(100);
+               /* AC-link powerdown, internal Clk disable */
+               /* FIXME: this may cause click noises on some boards */
+               power |= AC97_PD_PR4 | AC97_PD_PR5;
+               snd_ac97_write(ac97, AC97_POWERDOWN, power);
+       }
+#endif
+}
+
+
+struct ac97_power_reg {
+       unsigned short reg;
+       unsigned short power_reg;
+       unsigned short mask;
+};
+
+enum { PWIDX_ADC, PWIDX_FRONT, PWIDX_CLFE, PWIDX_SURR, PWIDX_MIC, PWIDX_SIZE };
+
+static struct ac97_power_reg power_regs[PWIDX_SIZE] = {
+       [PWIDX_ADC] = { AC97_PCM_LR_ADC_RATE, AC97_POWERDOWN, AC97_PD_PR0},
+       [PWIDX_FRONT] = { AC97_PCM_FRONT_DAC_RATE, AC97_POWERDOWN, AC97_PD_PR1},
+       [PWIDX_CLFE] = { AC97_PCM_LFE_DAC_RATE, AC97_EXTENDED_STATUS,
+                        AC97_EA_PRI | AC97_EA_PRK},
+       [PWIDX_SURR] = { AC97_PCM_SURR_DAC_RATE, AC97_EXTENDED_STATUS,
+                        AC97_EA_PRJ},
+       [PWIDX_MIC] = { AC97_PCM_MIC_ADC_RATE, AC97_EXTENDED_STATUS,
+                       AC97_EA_PRL},
+};
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+/**
+ * snd_ac97_update_power - update the powerdown register
+ * @ac97: the codec instance
+ * @reg: the rate register, e.g. AC97_PCM_FRONT_DAC_RATE
+ * @powerup: non-zero when power up the part
+ *
+ * Update the AC97 powerdown register bits of the given part.
+ */
+int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
+{
+       int i;
+
+       if (! ac97)
+               return 0;
+
+       if (reg) {
+               /* SPDIF requires DAC power, too */
+               if (reg == AC97_SPDIF)
+                       reg = AC97_PCM_FRONT_DAC_RATE;
+               for (i = 0; i < PWIDX_SIZE; i++) {
+                       if (power_regs[i].reg == reg) {
+                               if (powerup)
+                                       ac97->power_up |= (1 << i);
+                               else
+                                       ac97->power_up &= ~(1 << i);
+                               break;
+                       }
+               }
+       }
+
+       if (! power_save)
+               return 0;
+
+       if (! powerup && ac97->power_workq)
+               /* adjust power-down bits after two seconds delay
+                * (for avoiding loud click noises for many (OSS) apps
+                *  that open/close frequently)
+                */
+               queue_delayed_work(ac97->power_workq, &ac97->power_work, HZ*2);
+       else
+               update_power_regs(ac97);
+
+       return 0;
+}
+
+EXPORT_SYMBOL(snd_ac97_update_power);
+#endif /* CONFIG_SND_AC97_POWER_SAVE */
+
+static void update_power_regs(struct snd_ac97 *ac97)
+{
+       unsigned int power_up, bits;
+       int i;
+
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       if (power_save)
+               power_up = ac97->power_up;
+       else {
 #endif
+               power_up = (1 << PWIDX_FRONT) | (1 << PWIDX_ADC);
+               power_up |= (1 << PWIDX_MIC);
+               if (ac97->scaps & AC97_SCAP_SURROUND_DAC)
+                       power_up |= (1 << PWIDX_SURR);
+               if (ac97->scaps & AC97_SCAP_CENTER_LFE_DAC)
+                       power_up |= (1 << PWIDX_CLFE);
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       }
+#endif
+       if (power_up) {
+               if (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2) {
+                       /* needs power-up analog mix and vref */
+                       snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                            AC97_PD_PR3, 0);
+                       msleep(1);
+                       snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                            AC97_PD_PR2, 0);
+               }
+       }
+       for (i = 0; i < PWIDX_SIZE; i++) {
+               if (power_up & (1 << i))
+                       bits = 0;
+               else
+                       bits = power_regs[i].mask;
+               snd_ac97_update_bits(ac97, power_regs[i].power_reg,
+                                    power_regs[i].mask, bits);
+       }
+       if (! power_up) {
+               if (! (ac97->regs[AC97_POWERDOWN] & AC97_PD_PR2)) {
+                       /* power down analog mix and vref */
+                       snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                            AC97_PD_PR2, AC97_PD_PR2);
+                       snd_ac97_update_bits(ac97, AC97_POWERDOWN,
+                                            AC97_PD_PR3, AC97_PD_PR3);
+               }
+       }
 }
 
 
@@ -2484,6 +2719,7 @@ static int tune_mute_led(struct snd_ac97 *ac97)
        msw->put = master_mute_sw_put;
        snd_ac97_remove_ctl(ac97, "External Amplifier", NULL);
        snd_ac97_update_bits(ac97, AC97_POWERDOWN, 0x8000, 0x8000); /* mute LED on */
+       ac97->scaps |= AC97_SCAP_EAPD_LED;
        return 0;
 }
 
index 094cfc1f3a190157b7bd7c0e3436cf00a9dcf858..dc28b111a06ddeea8e94783bf994bf78a7130166 100644 (file)
@@ -32,6 +32,7 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/control.h>
+#include <sound/tlv.h>
 #include <sound/ac97_codec.h>
 #include "ac97_patch.h"
 #include "ac97_id.h"
@@ -51,6 +52,20 @@ static int patch_build_controls(struct snd_ac97 * ac97, const struct snd_kcontro
        return 0;
 }
 
+/* replace with a new TLV */
+static void reset_tlv(struct snd_ac97 *ac97, const char *name,
+                     unsigned int *tlv)
+{
+       struct snd_ctl_elem_id sid;
+       struct snd_kcontrol *kctl;
+       memset(&sid, 0, sizeof(sid));
+       strcpy(sid.name, name);
+       sid.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kctl = snd_ctl_find_id(ac97->bus->card, &sid);
+       if (kctl && kctl->tlv.p)
+               kctl->tlv.p = tlv;
+}
+
 /* set to the page, update bits and restore the page */
 static int ac97_update_bits_page(struct snd_ac97 *ac97, unsigned short reg, unsigned short mask, unsigned short value, unsigned short page)
 {
@@ -466,7 +481,7 @@ int patch_wolfson05(struct snd_ac97 * ac97)
        ac97->build_ops = &patch_wolfson_wm9705_ops;
 #ifdef CONFIG_TOUCHSCREEN_WM9705
        /* WM9705 touchscreen uses AUX and VIDEO for touch */
-       ac97->flags |=3D AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
+       ac97->flags |= AC97_HAS_NO_VIDEO | AC97_HAS_NO_AUX;
 #endif
        return 0;
 }
@@ -1380,6 +1395,17 @@ static void ad1888_resume(struct snd_ac97 *ac97)
 
 #endif
 
+static const struct snd_ac97_res_table ad1819_restbl[] = {
+       { AC97_PHONE, 0x9f1f },
+       { AC97_MIC, 0x9f1f },
+       { AC97_LINE, 0x9f1f },
+       { AC97_CD, 0x9f1f },
+       { AC97_VIDEO, 0x9f1f },
+       { AC97_AUX, 0x9f1f },
+       { AC97_PCM, 0x9f1f },
+       { } /* terminator */
+};
+
 int patch_ad1819(struct snd_ac97 * ac97)
 {
        unsigned short scfg;
@@ -1387,6 +1413,7 @@ int patch_ad1819(struct snd_ac97 * ac97)
        // patch for Analog Devices
        scfg = snd_ac97_read(ac97, AC97_AD_SERIAL_CFG);
        snd_ac97_write_cache(ac97, AC97_AD_SERIAL_CFG, scfg | 0x7000); /* select all codecs */
+       ac97->res_table = ad1819_restbl;
        return 0;
 }
 
@@ -1522,12 +1549,16 @@ static const struct snd_kcontrol_new snd_ac97_controls_ad1885[] = {
        AC97_SINGLE("Line Jack Sense", AC97_AD_JACK_SPDIF, 8, 1, 1), /* inverted */
 };
 
+static DECLARE_TLV_DB_SCALE(db_scale_6bit_6db_max, -8850, 150, 0);
+
 static int patch_ad1885_specific(struct snd_ac97 * ac97)
 {
        int err;
 
        if ((err = patch_build_controls(ac97, snd_ac97_controls_ad1885, ARRAY_SIZE(snd_ac97_controls_ad1885))) < 0)
                return err;
+       reset_tlv(ac97, "Headphone Playback Volume",
+                 db_scale_6bit_6db_max);
        return 0;
 }
 
@@ -1551,12 +1582,27 @@ int patch_ad1885(struct snd_ac97 * ac97)
        return 0;
 }
 
+static int patch_ad1886_specific(struct snd_ac97 * ac97)
+{
+       reset_tlv(ac97, "Headphone Playback Volume",
+                 db_scale_6bit_6db_max);
+       return 0;
+}
+
+static struct snd_ac97_build_ops patch_ad1886_build_ops = {
+       .build_specific = &patch_ad1886_specific,
+#ifdef CONFIG_PM
+       .resume = ad18xx_resume
+#endif
+};
+
 int patch_ad1886(struct snd_ac97 * ac97)
 {
        patch_ad1881(ac97);
        /* Presario700 workaround */
        /* for Jack Sense/SPDIF Register misetting causing */
        snd_ac97_write_cache(ac97, AC97_AD_JACK_SPDIF, 0x0010);
+       ac97->build_ops = &patch_ad1886_build_ops;
        return 0;
 }
 
@@ -2015,6 +2061,8 @@ static const struct snd_kcontrol_new snd_ac97_spdif_controls_alc650[] = {
        /* AC97_SINGLE("IEC958 Input Monitor", AC97_ALC650_MULTICH, 13, 1, 0), */
 };
 
+static DECLARE_TLV_DB_SCALE(db_scale_5bit_3db_max, -4350, 150, 0);
+
 static int patch_alc650_specific(struct snd_ac97 * ac97)
 {
        int err;
@@ -2025,6 +2073,9 @@ static int patch_alc650_specific(struct snd_ac97 * ac97)
                if ((err = patch_build_controls(ac97, snd_ac97_spdif_controls_alc650, ARRAY_SIZE(snd_ac97_spdif_controls_alc650))) < 0)
                        return err;
        }
+       if (ac97->id != AC97_ID_ALC650F)
+               reset_tlv(ac97, "Master Playback Volume",
+                         db_scale_5bit_3db_max);
        return 0;
 }
 
@@ -2208,7 +2259,8 @@ int patch_alc655(struct snd_ac97 * ac97)
                val &= ~(1 << 1); /* Pin 47 is spdif input pin */
        else { /* ALC655 */
                if (ac97->subsystem_vendor == 0x1462 &&
-                   ac97->subsystem_device == 0x0131) /* MSI S270 laptop */
+                   (ac97->subsystem_device == 0x0131 || /* MSI S270 laptop */
+                    ac97->subsystem_device == 0x0161)) /* LG K1 Express */
                        val &= ~(1 << 1); /* Pin 47 is EAPD (for internal speaker) */
                else
                        val |= (1 << 1); /* Pin 47 is spdif input pin */
@@ -2759,6 +2811,10 @@ int patch_vt1616(struct snd_ac97 * ac97)
  */
 int patch_vt1617a(struct snd_ac97 * ac97)
 {
+       /* bring analog power consumption to normal, like WinXP driver
+        * for EPIA SP
+        */
+       snd_ac97_write_cache(ac97, 0x5c, 0x20);
        ac97->ext_id |= AC97_EI_SPDIF;  /* force the detection of spdif */
        ac97->rates[AC97_RATES_SPDIF] = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000;
        return 0;
@@ -2872,3 +2928,41 @@ int patch_lm4550(struct snd_ac97 *ac97)
        ac97->res_table = lm4550_restbl;
        return 0;
 }
+
+/* 
+ *  UCB1400 codec (http://www.semiconductors.philips.com/acrobat_download/datasheets/UCB1400-02.pdf)
+ */
+static const struct snd_kcontrol_new snd_ac97_controls_ucb1400[] = {
+/* enable/disable headphone driver which allows direct connection to
+   stereo headphone without the use of external DC blocking
+   capacitors */
+AC97_SINGLE("Headphone Driver", 0x6a, 6, 1, 0),
+/* Filter used to compensate the DC offset is added in the ADC to remove idle
+   tones from the audio band. */
+AC97_SINGLE("DC Filter", 0x6a, 4, 1, 0),
+/* Control smart-low-power mode feature. Allows automatic power down
+   of unused blocks in the ADC analog front end and the PLL. */
+AC97_SINGLE("Smart Low Power Mode", 0x6c, 4, 3, 0),
+};
+
+static int patch_ucb1400_specific(struct snd_ac97 * ac97)
+{
+       int idx, err;
+       for (idx = 0; idx < ARRAY_SIZE(snd_ac97_controls_ucb1400); idx++)
+               if ((err = snd_ctl_add(ac97->bus->card, snd_ctl_new1(&snd_ac97_controls_ucb1400[idx], ac97))) < 0)
+                       return err;
+       return 0;
+}
+
+static struct snd_ac97_build_ops patch_ucb1400_ops = {
+       .build_specific = patch_ucb1400_specific,
+};
+
+int patch_ucb1400(struct snd_ac97 * ac97)
+{
+       ac97->build_ops = &patch_ucb1400_ops;
+       /* enable headphone driver and smart low power mode by default */
+       snd_ac97_write(ac97, 0x6a, 0x0050);
+       snd_ac97_write(ac97, 0x6c, 0x0030);
+       return 0;
+}
index adcaa04586cbb500de7feeaaa893f36db7af97c6..741979217207501992d8e9493923ff0bfb498f0f 100644 (file)
@@ -58,5 +58,6 @@ int patch_cm9780(struct snd_ac97 * ac97);
 int patch_vt1616(struct snd_ac97 * ac97);
 int patch_vt1617a(struct snd_ac97 * ac97);
 int patch_it2646(struct snd_ac97 * ac97);
+int patch_ucb1400(struct snd_ac97 * ac97);
 int mpatch_si3036(struct snd_ac97 * ac97);
 int patch_lm4550(struct snd_ac97 * ac97);
index f684aa2c0067667a8d1e4e378035e4b3dcb2beb6..3758d07182f8013f2707971894ffe7a963c5ddf7 100644 (file)
@@ -269,6 +269,7 @@ int snd_ac97_set_rate(struct snd_ac97 *ac97, int reg, unsigned int rate)
                        return -EINVAL;
        }
 
+       snd_ac97_update_power(ac97, reg, 1);
        switch (reg) {
        case AC97_PCM_MIC_ADC_RATE:
                if ((ac97->regs[AC97_EXTENDED_STATUS] & AC97_EA_VRM) == 0)      /* MIC VRA */
@@ -606,6 +607,7 @@ int snd_ac97_pcm_open(struct ac97_pcm *pcm, unsigned int rate,
                        goto error;
                }
        }
+       pcm->cur_dbl = r;
        spin_unlock_irq(&pcm->bus->bus_lock);
        for (i = 3; i < 12; i++) {
                if (!(slots & (1 << i)))
@@ -651,6 +653,21 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
        unsigned short slots = pcm->aslots;
        int i, cidx;
 
+#ifdef CONFIG_SND_AC97_POWER_SAVE
+       int r = pcm->cur_dbl;
+       for (i = 3; i < 12; i++) {
+               if (!(slots & (1 << i)))
+                       continue;
+               for (cidx = 0; cidx < 4; cidx++) {
+                       if (pcm->r[r].rslots[cidx] & (1 << i)) {
+                               int reg = get_slot_reg(pcm, cidx, i, r);
+                               snd_ac97_update_power(pcm->r[r].codec[cidx],
+                                                     reg, 0);
+                       }
+               }
+       }
+#endif
+
        bus = pcm->bus;
        spin_lock_irq(&pcm->bus->bus_lock);
        for (i = 3; i < 12; i++) {
@@ -660,6 +677,7 @@ int snd_ac97_pcm_close(struct ac97_pcm *pcm)
                        bus->used_slots[pcm->stream][cidx] &= ~(1 << i);
        }
        pcm->aslots = 0;
+       pcm->cur_dbl = 0;
        spin_unlock_irq(&pcm->bus->bus_lock);
        return 0;
 }
index 2118df50b9d6123fa8f9fd2d244309cd549715b8..a3fdd7da911cd74887e80126029b0bac136a864c 100644 (file)
@@ -457,14 +457,10 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97)
 
 void snd_ac97_proc_done(struct snd_ac97 * ac97)
 {
-       if (ac97->proc_regs) {
-               snd_info_unregister(ac97->proc_regs);
-               ac97->proc_regs = NULL;
-       }
-       if (ac97->proc) {
-               snd_info_unregister(ac97->proc);
-               ac97->proc = NULL;
-       }
+       snd_info_free_entry(ac97->proc_regs);
+       ac97->proc_regs = NULL;
+       snd_info_free_entry(ac97->proc);
+       ac97->proc = NULL;
 }
 
 void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
@@ -485,8 +481,6 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus)
 
 void snd_ac97_bus_proc_done(struct snd_ac97_bus * bus)
 {
-       if (bus->proc) {
-               snd_info_unregister(bus->proc);
-               bus->proc = NULL;
-       }
+       snd_info_free_entry(bus->proc);
+       bus->proc = NULL;
 }
index 94c26ec0588207b6f95fa2dd16346aa98d9e99f8..c153cb79c518a4fc3ca3e8a3827ab75e92ccc0a5 100644 (file)
@@ -27,6 +27,7 @@
 
 #include <sound/core.h>
 #include <sound/ak4531_codec.h>
+#include <sound/tlv.h>
 
 MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>");
 MODULE_DESCRIPTION("Universal routines for AK4531 codec");
@@ -63,6 +64,14 @@ static void snd_ak4531_dump(struct snd_ak4531 *ak4531)
   .info = snd_ak4531_info_single, \
   .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
   .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22) }
+#define AK4531_SINGLE_TLV(xname, xindex, reg, shift, mask, invert, xtlv)    \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_single, \
+  .get = snd_ak4531_get_single, .put = snd_ak4531_put_single, \
+  .private_value = reg | (shift << 16) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = (xtlv) } }
 
 static int snd_ak4531_info_single(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -122,6 +131,14 @@ static int snd_ak4531_put_single(struct snd_kcontrol *kcontrol, struct snd_ctl_e
   .info = snd_ak4531_info_double, \
   .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
   .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22) }
+#define AK4531_DOUBLE_TLV(xname, xindex, left_reg, right_reg, left_shift, right_shift, mask, invert, xtlv) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+  .access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ, \
+  .name = xname, .index = xindex, \
+  .info = snd_ak4531_info_double, \
+  .get = snd_ak4531_get_double, .put = snd_ak4531_put_double, \
+  .private_value = left_reg | (right_reg << 8) | (left_shift << 16) | (right_shift << 19) | (mask << 24) | (invert << 22), \
+  .tlv = { .p = (xtlv) } }
 
 static int snd_ak4531_info_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
 {
@@ -250,50 +267,62 @@ static int snd_ak4531_put_input_sw(struct snd_kcontrol *kcontrol, struct snd_ctl
        return change;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_master, -6200, 200, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_mono, -2800, 400, 0);
+static DECLARE_TLV_DB_SCALE(db_scale_input, -5000, 200, 0);
+
 static struct snd_kcontrol_new snd_ak4531_controls[] = {
 
-AK4531_DOUBLE("Master Playback Switch", 0, AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1),
+AK4531_DOUBLE_TLV("Master Playback Switch", 0,
+                 AK4531_LMASTER, AK4531_RMASTER, 7, 7, 1, 1,
+                 db_scale_master),
 AK4531_DOUBLE("Master Playback Volume", 0, AK4531_LMASTER, AK4531_RMASTER, 0, 0, 0x1f, 1),
 
-AK4531_SINGLE("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1),
+AK4531_SINGLE_TLV("Master Mono Playback Switch", 0, AK4531_MONO_OUT, 7, 1, 1,
+                 db_scale_mono),
 AK4531_SINGLE("Master Mono Playback Volume", 0, AK4531_MONO_OUT, 0, 0x07, 1),
 
 AK4531_DOUBLE("PCM Switch", 0, AK4531_LVOICE, AK4531_RVOICE, 7, 7, 1, 1),
-AK4531_DOUBLE("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("PCM Volume", 0, AK4531_LVOICE, AK4531_RVOICE, 0, 0, 0x1f, 1,
+                 db_scale_input),
 AK4531_DOUBLE("PCM Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 3, 2, 1, 0),
 AK4531_DOUBLE("PCM Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 2, 2, 1, 0),
 
 AK4531_DOUBLE("PCM Switch", 1, AK4531_LFM, AK4531_RFM, 7, 7, 1, 1),
-AK4531_DOUBLE("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("PCM Volume", 1, AK4531_LFM, AK4531_RFM, 0, 0, 0x1f, 1,
+                 db_scale_input),
 AK4531_DOUBLE("PCM Playback Switch", 1, AK4531_OUT_SW1, AK4531_OUT_SW1, 6, 5, 1, 0),
 AK4531_INPUT_SW("PCM Capture Route", 1, AK4531_LIN_SW1, AK4531_RIN_SW1, 6, 5),
 
 AK4531_DOUBLE("CD Switch", 0, AK4531_LCD, AK4531_RCD, 7, 7, 1, 1),
-AK4531_DOUBLE("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("CD Volume", 0, AK4531_LCD, AK4531_RCD, 0, 0, 0x1f, 1,
+                 db_scale_input),
 AK4531_DOUBLE("CD Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 2, 1, 1, 0),
 AK4531_INPUT_SW("CD Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 2, 1),
 
 AK4531_DOUBLE("Line Switch", 0, AK4531_LLINE, AK4531_RLINE, 7, 7, 1, 1),
-AK4531_DOUBLE("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("Line Volume", 0, AK4531_LLINE, AK4531_RLINE, 0, 0, 0x1f, 1,
+                 db_scale_input),
 AK4531_DOUBLE("Line Playback Switch", 0, AK4531_OUT_SW1, AK4531_OUT_SW1, 4, 3, 1, 0),
 AK4531_INPUT_SW("Line Capture Route", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 4, 3),
 
 AK4531_DOUBLE("Aux Switch", 0, AK4531_LAUXA, AK4531_RAUXA, 7, 7, 1, 1),
-AK4531_DOUBLE("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1),
+AK4531_DOUBLE_TLV("Aux Volume", 0, AK4531_LAUXA, AK4531_RAUXA, 0, 0, 0x1f, 1,
+                 db_scale_input),
 AK4531_DOUBLE("Aux Playback Switch", 0, AK4531_OUT_SW2, AK4531_OUT_SW2, 5, 4, 1, 0),
 AK4531_INPUT_SW("Aux Capture Route", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 4, 3),
 
 AK4531_SINGLE("Mono Switch", 0, AK4531_MONO1, 7, 1, 1),
-AK4531_SINGLE("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1),
+AK4531_SINGLE_TLV("Mono Volume", 0, AK4531_MONO1, 0, 0x1f, 1, db_scale_input),
 AK4531_SINGLE("Mono Playback Switch", 0, AK4531_OUT_SW2, 0, 1, 0),
 AK4531_DOUBLE("Mono Capture Switch", 0, AK4531_LIN_SW2, AK4531_RIN_SW2, 0, 0, 1, 0),
 
 AK4531_SINGLE("Mono Switch", 1, AK4531_MONO2, 7, 1, 1),
-AK4531_SINGLE("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1),
+AK4531_SINGLE_TLV("Mono Volume", 1, AK4531_MONO2, 0, 0x1f, 1, db_scale_input),
 AK4531_SINGLE("Mono Playback Switch", 1, AK4531_OUT_SW2, 1, 1, 0),
 AK4531_DOUBLE("Mono Capture Switch", 1, AK4531_LIN_SW2, AK4531_RIN_SW2, 1, 1, 1, 0),
 
-AK4531_SINGLE("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1),
+AK4531_SINGLE_TLV("Mic Volume", 0, AK4531_MIC, 0, 0x1f, 1, db_scale_input),
 AK4531_SINGLE("Mic Switch", 0, AK4531_MIC, 7, 1, 1),
 AK4531_SINGLE("Mic Playback Switch", 0, AK4531_OUT_SW1, 0, 1, 0),
 AK4531_DOUBLE("Mic Capture Switch", 0, AK4531_LIN_SW1, AK4531_RIN_SW1, 0, 0, 1, 0),
index 146eed70dce6e62a06a32a24d31f6524cb3d1a5e..9855f528ea78173aeec031472bd7876aa5590b0a 100644 (file)
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/info.h>
+#include <sound/tlv.h>
 
 #include "ca0106.h"
 
+static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale1, -5175, 25, 1);
+static DECLARE_TLV_DB_SCALE(snd_ca0106_db_scale2, -10350, 50, 1);
+
 static int snd_ca0106_shared_spdif_info(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_info *uinfo)
 {
@@ -469,18 +473,24 @@ static int snd_ca0106_i2c_volume_put(struct snd_kcontrol *kcontrol,
 #define CA_VOLUME(xname,chid,reg) \
 {                                                              \
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,     \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |             \
+                 SNDRV_CTL_ELEM_ACCESS_TLV_READ,               \
        .info =  snd_ca0106_volume_info,                        \
        .get =   snd_ca0106_volume_get,                         \
        .put =   snd_ca0106_volume_put,                         \
+       .tlv = { .p = snd_ca0106_db_scale1 },                   \
        .private_value = ((chid) << 8) | (reg)                  \
 }
 
 #define I2C_VOLUME(xname,chid) \
 {                                                              \
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,     \
+       .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |             \
+                 SNDRV_CTL_ELEM_ACCESS_TLV_READ,               \
        .info =  snd_ca0106_i2c_volume_info,                    \
        .get =   snd_ca0106_i2c_volume_get,                     \
        .put =   snd_ca0106_i2c_volume_put,                     \
+       .tlv = { .p = snd_ca0106_db_scale2 },                   \
        .private_value = chid                                   \
 }
 
index 9631456ec3de84d5e278bc38daf17d99825abaf6..1990430a21c1d08ca61a69577b9272508300519b 100644 (file)
@@ -33,6 +33,7 @@
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
 #include <sound/ac97_codec.h>
+#include <sound/tlv.h>
 #include <sound/opl3.h>
 #include <sound/initval.h>
 
@@ -1054,6 +1055,8 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+static DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
+
 static struct snd_kcontrol_new snd_cs4281_fm_vol = 
 {
        .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -1062,6 +1065,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
        .get&