]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/asoc...
authorTakashi Iwai <tiwai@suse.de>
Tue, 22 May 2012 23:37:59 +0000 (01:37 +0200)
committerTakashi Iwai <tiwai@suse.de>
Tue, 22 May 2012 23:37:59 +0000 (01:37 +0200)
98 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
include/sound/asound.h
include/sound/asoundef.h
sound/atmel/ac97c.c
sound/core/jack.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/core/sound_oss.c
sound/drivers/aloop.c
sound/firewire/amdtp.c
sound/firewire/amdtp.h
sound/pci/Kconfig
sound/pci/ad1889.c
sound/pci/ali5451/ali5451.c
sound/pci/als300.c
sound/pci/als4000.c
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/au88x0/au88x0.c
sound/pci/aw2/aw2-alsa.c
sound/pci/azt3328.c
sound/pci/bt87x.c
sound/pci/ca0106/ca0106_main.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/cs46xx/cs46xx.c
sound/pci/cs5530.c
sound/pci/cs5535audio/cs5535audio.c
sound/pci/ctxfi/xfi.c
sound/pci/echoaudio/echoaudio.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1x.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/hda/Makefile
sound/pci/hda/hda_auto_parser.c [new file with mode: 0644]
sound/pci/hda/hda_auto_parser.h [new file with mode: 0644]
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/hda_local.h
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1724.c
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/korg1212/korg1212.c
sound/pci/lola/lola.c
sound/pci/lx6464es/lx6464es.c
sound/pci/maestro3.c
sound/pci/mixart/mixart.c
sound/pci/nm256/nm256.c
sound/pci/oxygen/oxygen.c
sound/pci/oxygen/virtuoso.c
sound/pci/oxygen/xonar_dg.c
sound/pci/pcxhr/pcxhr.c
sound/pci/riptide/riptide.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/pci/rme9652/rme9652.c
sound/pci/sis7019.c
sound/pci/sonicvibes.c
sound/pci/trident/trident.c
sound/pci/via82xx.c
sound/pci/via82xx_modem.c
sound/pci/vx222/vx222.c
sound/pci/ymfpci/ymfpci.c
sound/sh/sh_dac_audio.c
sound/soc/codecs/cs42l73.c
sound/soc/codecs/wm8994.c
sound/sound_core.c
sound/usb/card.c
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/endpoint.h
sound/usb/mixer.c
sound/usb/mixer.h
sound/usb/mixer_maps.c
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/proc.c
sound/usb/stream.c
sound/usb/usbaudio.h

index 8c16d50f6cb6f67284220f5fb29785224b48965a..221b81016dba75e48b8ec0e253c13d97f5be5127 100644 (file)
@@ -1545,7 +1545,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
 
     Module for sound cards based on the C-Media CMI8786/8787/8788 chip:
     * Asound A-8788
-    * Asus Xonar DG
+    * Asus Xonar DG/DGX
     * AuzenTech X-Meridian
     * AuzenTech X-Meridian 2G
     * Bgears b-Enspirer
index a2e4ff5ba9e9f12be58de88d38fdf0645289ca4b..0876a1e76aefbd97dc66eb76d480e7e05573f68d 100644 (file)
@@ -68,6 +68,20 @@ struct snd_aes_iec958 {
        unsigned char dig_subframe[4];  /* AES/IEC958 subframe bits */
 };
 
+/****************************************************************************
+ *                                                                          *
+ *        CEA-861 Audio InfoFrame. Used in HDMI and DisplayPort                    *
+ *                                                                          *
+ ****************************************************************************/
+
+struct snd_cea_861_aud_if {
+       unsigned char db1_ct_cc; /* coding type and channel count */
+       unsigned char db2_sf_ss; /* sample frequency and size */
+       unsigned char db3; /* not used, all zeros */
+       unsigned char db4_ca; /* channel allocation code */
+       unsigned char db5_dminh_lsv; /* downmix inhibit & level-shit values */
+};
+
 /****************************************************************************
  *                                                                          *
  *      Section for driver hardware dependent interface - /dev/snd/hw?      *
index 20ebf3298eba56273021957b738073001e528959..bb05c02f89b0ca8845108fd3fc1306f41944bc8a 100644 (file)
 #define IEC958_AES5_CON_CGMSA_COPYNOMORE (2<<0)        /* condition not be used */
 #define IEC958_AES5_CON_CGMSA_COPYNEVER        (3<<0)  /* no copying is permitted */
 
+/****************************************************************************
+ *                                                                          *
+ *        CEA-861 Audio InfoFrame. Used in HDMI and DisplayPort                    *
+ *                                                                          *
+ ****************************************************************************/
+#define CEA861_AUDIO_INFOFRAME_DB1CC           (7<<0) /* mask - channel count */
+#define CEA861_AUDIO_INFOFRAME_DB1CT           (0xf<<4) /* mask - coding type */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM (0<<4) /* refer to stream */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_IEC60958  (1<<4) /* IEC-60958 L-PCM */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_AC3       (2<<4) /* AC-3 */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MPEG1     (3<<4) /* MPEG1 Layers 1 & 2 */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MP3       (4<<4) /* MPEG1 Layer 3 */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MPEG2_MULTICH (5<<4) /* MPEG2 Multichannel */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_AAC       (6<<4) /* AAC */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DTS       (7<<4) /* DTS */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_ATRAC     (8<<4) /* ATRAC */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_ONEBIT    (9<<4) /* One Bit Audio */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DOLBY_DIG_PLUS (10<<4) /* Dolby Digital + */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DTS_HD    (11<<4) /* DTS-HD */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_MAT       (12<<4) /* MAT (MLP) */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_DST       (13<<4) /* DST */
+#define CEA861_AUDIO_INFOFRAME_DB1CT_WMA_PRO   (14<<4) /* WMA Pro */
+#define CEA861_AUDIO_INFOFRAME_DB2SF           (7<<2) /* mask - sample frequency */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM (0<<2) /* refer to stream */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_32000     (1<<2) /* 32kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_44100     (2<<2) /* 44.1kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_48000     (3<<2) /* 48kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_88200     (4<<2) /* 88.2kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_96000     (5<<2) /* 96kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_176400    (6<<2) /* 176.4kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SF_192000    (7<<2) /* 192kHz */
+#define CEA861_AUDIO_INFOFRAME_DB2SS           (3<<0) /* mask - sample size */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM (0<<0) /* refer to stream */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_16BIT     (1<<0) /* 16 bits */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_20BIT     (2<<0) /* 20 bits */
+#define CEA861_AUDIO_INFOFRAME_DB2SS_24BIT     (3<<0) /* 24 bits */
+#define CEA861_AUDIO_INFOFRAME_DB5_DM_INH      (1<<7) /* mask - inhibit downmixing */
+#define CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PERMITTED (0<<7) /* stereo downmix permitted */
+#define CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED (1<<7) /* stereo downmis prohibited */
+#define CEA861_AUDIO_INFOFRAME_DB5_LSV         (0xf<<3) /* mask - level-shift values */
+
 /*****************************************************************************
  *                                                                           *
  *                            MIDI v1.0 interface                            *
index 115313ef54d67e7d10cc6b6d4295a53bf49e340d..f5ded640b395312934396237299ac532ef2cb148 100644 (file)
@@ -991,6 +991,8 @@ static int __devinit atmel_ac97c_probe(struct platform_device *pdev)
                        gpio_direction_output(pdata->reset_pin, 1);
                        chip->reset_pin = pdata->reset_pin;
                }
+       } else {
+               chip->reset_pin = -EINVAL;
        }
 
        snd_card_set_dev(card, &pdev->dev);
index 471e1e3b0a996da3fec0d21c82a55b87f3460469..a06b1651fcba02f9003f5b9a07ad2c72fb2dea14 100644 (file)
@@ -155,7 +155,7 @@ EXPORT_SYMBOL(snd_jack_new);
  * @jack:   The jack to configure
  * @parent: The device to set as parent for the jack.
  *
- * Set the parent for the jack input device in the device tree.  This
+ * Set the parent for the jack devices in the device tree.  This
  * function is only valid prior to registration of the jack.  If no
  * parent is configured then the parent device will be the sound card.
  */
@@ -179,6 +179,9 @@ EXPORT_SYMBOL(snd_jack_set_parent);
  * mapping is provided but keys are enabled in the jack type then
  * BTN_n numeric buttons will be reported.
  *
+ * If jacks are not reporting via the input API this call will have no
+ * effect.
+ *
  * Note that this is intended to be use by simple devices with small
  * numbers of keys that can be reported.  It is also possible to
  * access the input device directly - devices with complex input
index 4d18941178e6e933cb0f80f969c4d705c4777caf..faedb1481b240d9195b097f1448f98df3dcb6912 100644 (file)
@@ -1894,6 +1894,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_uframes_t xfer = 0;
        snd_pcm_uframes_t offset = 0;
+       snd_pcm_uframes_t avail;
        int err = 0;
 
        if (size == 0)
@@ -1917,13 +1918,12 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
        }
 
        runtime->twake = runtime->control->avail_min ? : 1;
+       if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+               snd_pcm_update_hw_ptr(substream);
+       avail = snd_pcm_playback_avail(runtime);
        while (size > 0) {
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
-               snd_pcm_uframes_t avail;
                snd_pcm_uframes_t cont;
-               if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-                       snd_pcm_update_hw_ptr(substream);
-               avail = snd_pcm_playback_avail(runtime);
                if (!avail) {
                        if (nonblock) {
                                err = -EAGAIN;
@@ -1971,6 +1971,7 @@ static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream,
                offset += frames;
                size -= frames;
                xfer += frames;
+               avail -= frames;
                if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
                    snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {
                        err = snd_pcm_start(substream);
@@ -2111,6 +2112,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_uframes_t xfer = 0;
        snd_pcm_uframes_t offset = 0;
+       snd_pcm_uframes_t avail;
        int err = 0;
 
        if (size == 0)
@@ -2141,13 +2143,12 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
        }
 
        runtime->twake = runtime->control->avail_min ? : 1;
+       if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
+               snd_pcm_update_hw_ptr(substream);
+       avail = snd_pcm_capture_avail(runtime);
        while (size > 0) {
                snd_pcm_uframes_t frames, appl_ptr, appl_ofs;
-               snd_pcm_uframes_t avail;
                snd_pcm_uframes_t cont;
-               if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
-                       snd_pcm_update_hw_ptr(substream);
-               avail = snd_pcm_capture_avail(runtime);
                if (!avail) {
                        if (runtime->status->state ==
                            SNDRV_PCM_STATE_DRAINING) {
@@ -2202,6 +2203,7 @@ static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,
                offset += frames;
                size -= frames;
                xfer += frames;
+               avail -= frames;
        }
  _end_unlock:
        runtime->twake = 0;
index 3fe99e644eb838a34913f8c6870742e67c227a31..53b5ada8f7c36fd5199366662cc0ce5c5233e66c 100644 (file)
@@ -1360,7 +1360,14 @@ static int snd_pcm_prepare(struct snd_pcm_substream *substream,
 
 static int snd_pcm_pre_drain_init(struct snd_pcm_substream *substream, int state)
 {
-       substream->runtime->trigger_master = substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       switch (runtime->status->state) {
+       case SNDRV_PCM_STATE_OPEN:
+       case SNDRV_PCM_STATE_DISCONNECTED:
+       case SNDRV_PCM_STATE_SUSPENDED:
+               return -EBADFD;
+       }
+       runtime->trigger_master = substream;
        return 0;
 }
 
@@ -1379,6 +1386,9 @@ static int snd_pcm_do_drain_init(struct snd_pcm_substream *substream, int state)
                case SNDRV_PCM_STATE_RUNNING:
                        runtime->status->state = SNDRV_PCM_STATE_DRAINING;
                        break;
+               case SNDRV_PCM_STATE_XRUN:
+                       runtime->status->state = SNDRV_PCM_STATE_SETUP;
+                       break;
                default:
                        break;
                }
index c700920430618f94a0dc0f7957ed75b813481507..e9528333e36d01e6e0282255639da08b7174e505 100644 (file)
@@ -35,7 +35,7 @@
 #include <linux/sound.h>
 #include <linux/mutex.h>
 
-#define SNDRV_OSS_MINORS 128
+#define SNDRV_OSS_MINORS 256
 
 static struct snd_minor *snd_oss_minors[SNDRV_OSS_MINORS];
 static DEFINE_MUTEX(sound_oss_mutex);
@@ -111,7 +111,7 @@ int snd_register_oss_device(int type, struct snd_card *card, int dev,
        int register1 = -1, register2 = -1;
        struct device *carddev = snd_card_get_device_link(card);
 
-       if (card && card->number >= 8)
+       if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
                return 0; /* ignore silently */
        if (minor < 0)
                return minor;
@@ -170,7 +170,7 @@ int snd_unregister_oss_device(int type, struct snd_card *card, int dev)
        int track2 = -1;
        struct snd_minor *mptr;
 
-       if (card && card->number >= 8)
+       if (card && card->number >= SNDRV_MINOR_OSS_DEVICES)
                return 0;
        if (minor < 0)
                return minor;
index ad079b63b8bab170f6481112c568cfc7989b0294..8b5c36f4d30327e96a31f2e8afbd1b1dbb0f2d80 100644 (file)
@@ -117,6 +117,7 @@ struct loopback_pcm {
        /* timer stuff */
        unsigned int irq_pos;           /* fractional IRQ position */
        unsigned int period_size_frac;
+       unsigned int last_drift;
        unsigned long last_jiffies;
        struct timer_list timer;
 };
@@ -264,6 +265,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd)
                        return err;
                dpcm->last_jiffies = jiffies;
                dpcm->pcm_rate_shift = 0;
+               dpcm->last_drift = 0;
                spin_lock(&cable->lock);        
                cable->running |= stream;
                cable->pause &= ~stream;
@@ -444,34 +446,30 @@ static void copy_play_buf(struct loopback_pcm *play,
        }
 }
 
-#define BYTEPOS_UPDATE_POSONLY 0
-#define BYTEPOS_UPDATE_CLEAR   1
-#define BYTEPOS_UPDATE_COPY    2
-
-static void loopback_bytepos_update(struct loopback_pcm *dpcm,
-                                   unsigned int delta,
-                                   unsigned int cmd)
+static inline unsigned int bytepos_delta(struct loopback_pcm *dpcm,
+                                        unsigned int jiffies_delta)
 {
-       unsigned int count;
        unsigned long last_pos;
+       unsigned int delta;
 
        last_pos = byte_pos(dpcm, dpcm->irq_pos);
-       dpcm->irq_pos += delta * dpcm->pcm_bps;
-       count = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
-       if (!count)
-               return;
-       if (cmd == BYTEPOS_UPDATE_CLEAR)
-               clear_capture_buf(dpcm, count);
-       else if (cmd == BYTEPOS_UPDATE_COPY)
-               copy_play_buf(dpcm->cable->streams[SNDRV_PCM_STREAM_PLAYBACK],
-                             dpcm->cable->streams[SNDRV_PCM_STREAM_CAPTURE],
-                             count);
-       dpcm->buf_pos += count;
-       dpcm->buf_pos %= dpcm->pcm_buffer_size;
+       dpcm->irq_pos += jiffies_delta * dpcm->pcm_bps;
+       delta = byte_pos(dpcm, dpcm->irq_pos) - last_pos;
+       if (delta >= dpcm->last_drift)
+               delta -= dpcm->last_drift;
+       dpcm->last_drift = 0;
        if (dpcm->irq_pos >= dpcm->period_size_frac) {
                dpcm->irq_pos %= dpcm->period_size_frac;
                dpcm->period_update_pending = 1;
        }
+       return delta;
+}
+
+static inline void bytepos_finish(struct loopback_pcm *dpcm,
+                                 unsigned int delta)
+{
+       dpcm->buf_pos += delta;
+       dpcm->buf_pos %= dpcm->pcm_buffer_size;
 }
 
 static unsigned int loopback_pos_update(struct loopback_cable *cable)
@@ -481,7 +479,7 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
        struct loopback_pcm *dpcm_capt =
                        cable->streams[SNDRV_PCM_STREAM_CAPTURE];
        unsigned long delta_play = 0, delta_capt = 0;
-       unsigned int running;
+       unsigned int running, count1, count2;
        unsigned long flags;
 
        spin_lock_irqsave(&cable->lock, flags);
@@ -500,12 +498,13 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
                goto unlock;
                
        if (delta_play > delta_capt) {
-               loopback_bytepos_update(dpcm_play, delta_play - delta_capt,
-                                       BYTEPOS_UPDATE_POSONLY);
+               count1 = bytepos_delta(dpcm_play, delta_play - delta_capt);
+               bytepos_finish(dpcm_play, count1);
                delta_play = delta_capt;
        } else if (delta_play < delta_capt) {
-               loopback_bytepos_update(dpcm_capt, delta_capt - delta_play,
-                                       BYTEPOS_UPDATE_CLEAR);
+               count1 = bytepos_delta(dpcm_capt, delta_capt - delta_play);
+               clear_capture_buf(dpcm_capt, count1);
+               bytepos_finish(dpcm_capt, count1);
                delta_capt = delta_play;
        }
 
@@ -513,8 +512,17 @@ static unsigned int loopback_pos_update(struct loopback_cable *cable)
                goto unlock;
 
        /* note delta_capt == delta_play at this moment */
-       loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY);
-       loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY);
+       count1 = bytepos_delta(dpcm_play, delta_play);
+       count2 = bytepos_delta(dpcm_capt, delta_capt);
+       if (count1 < count2) {
+               dpcm_capt->last_drift = count2 - count1;
+               count1 = count2;
+       } else if (count1 > count2) {
+               dpcm_play->last_drift = count1 - count2;
+       }
+       copy_play_buf(dpcm_play, dpcm_capt, count1);
+       bytepos_finish(dpcm_play, count1);
+       bytepos_finish(dpcm_capt, count1);
  unlock:
        spin_unlock_irqrestore(&cable->lock, flags);
        return running;
index 87657dd7714ccccb59f4922f12a880680b7838c8..ea995af6d049ec63b86f8fb59cf374640db9cc56 100644 (file)
@@ -31,6 +31,8 @@
 #define INTERRUPT_INTERVAL     16
 #define QUEUE_LENGTH           48
 
+static void pcm_period_tasklet(unsigned long data);
+
 /**
  * amdtp_out_stream_init - initialize an AMDTP output stream structure
  * @s: the AMDTP output stream to initialize
@@ -47,6 +49,7 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
        s->flags = flags;
        s->context = ERR_PTR(-1);
        mutex_init(&s->mutex);
+       tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
        s->packet_index = 0;
 
        return 0;
@@ -164,6 +167,21 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 }
 EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
 
+/**
+ * amdtp_out_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP output stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+{
+       tasklet_kill(&s->period_tasklet);
+       s->pcm_buffer_pointer = 0;
+       s->pcm_period_pointer = 0;
+       s->pointer_flush = true;
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+
 static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
 {
        unsigned int phase, data_blocks;
@@ -376,11 +394,21 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
                s->pcm_period_pointer += data_blocks;
                if (s->pcm_period_pointer >= pcm->runtime->period_size) {
                        s->pcm_period_pointer -= pcm->runtime->period_size;
-                       snd_pcm_period_elapsed(pcm);
+                       s->pointer_flush = false;
+                       tasklet_hi_schedule(&s->period_tasklet);
                }
        }
 }
 
+static void pcm_period_tasklet(unsigned long data)
+{
+       struct amdtp_out_stream *s = (void *)data;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+       if (pcm)
+               snd_pcm_period_elapsed(pcm);
+}
+
 static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
                                size_t header_length, void *header, void *data)
 {
@@ -505,6 +533,24 @@ err_unlock:
 }
 EXPORT_SYMBOL(amdtp_out_stream_start);
 
+/**
+ * amdtp_out_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP output stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+{
+       /* this optimization is allowed to be racy */
+       if (s->pointer_flush)
+               fw_iso_context_flush_completions(s->context);
+       else
+               s->pointer_flush = true;
+
+       return ACCESS_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer);
+
 /**
  * amdtp_out_stream_update - update the stream after a bus reset
  * @s: the AMDTP output stream
@@ -532,6 +578,7 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
                return;
        }
 
+       tasklet_kill(&s->period_tasklet);
        fw_iso_context_stop(s->context);
        fw_iso_context_destroy(s->context);
        s->context = ERR_PTR(-1);
index 537a9cb83581b1b469b83914f39d3158413beb4f..b680c5ef01d694468f3b2736f357e49a09d50bf3 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
 #define SOUND_FIREWIRE_AMDTP_H_INCLUDED
 
+#include <linux/interrupt.h>
 #include <linux/mutex.h>
 #include <linux/spinlock.h>
 #include "packets-buffer.h"
@@ -55,6 +56,7 @@ struct amdtp_out_stream {
        struct iso_packets_buffer buffer;
 
        struct snd_pcm_substream *pcm;
+       struct tasklet_struct period_tasklet;
 
        int packet_index;
        unsigned int data_block_counter;
@@ -66,6 +68,7 @@ struct amdtp_out_stream {
 
        unsigned int pcm_buffer_pointer;
        unsigned int pcm_period_pointer;
+       bool pointer_flush;
 };
 
 int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
@@ -81,6 +84,8 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s);
 
 void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
                                     snd_pcm_format_t format);
+void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
+unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
 void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
 
 /**
@@ -122,18 +127,6 @@ static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
        return s->packet_index < 0;
 }
 
-/**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
- *
- * This function should be called from the PCM device's .prepare callback.
- */
-static inline void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
-{
-       s->pcm_buffer_pointer = 0;
-       s->pcm_period_pointer = 0;
-}
-
 /**
  * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
  * @s: the AMDTP output stream
@@ -149,18 +142,6 @@ static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
        ACCESS_ONCE(s->pcm) = pcm;
 }
 
-/**
- * amdtp_out_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP output stream that transports the PCM data
- *
- * Returns the current buffer position, in frames.
- */
-static inline unsigned long
-amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
-{
-       return ACCESS_ONCE(s->pcm_buffer_pointer);
-}
-
 static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
 {
        return sfc & 1;
index 5ca0939e4223b6104893fd77ccb3e1f5f982bd6c..ff3af6e77d610adc5cfba4748bdf25ac587238d7 100644 (file)
@@ -228,7 +228,7 @@ config SND_OXYGEN
          Say Y here to include support for sound cards based on the
          C-Media CMI8788 (Oxygen HD Audio) chip:
           * Asound A-8788
-          * Asus Xonar DG
+          * Asus Xonar DG/DGX
           * AuzenTech X-Meridian
           * AuzenTech X-Meridian 2G
           * Bgears b-Enspirer
index 9d91d61902b47edb7b7c8af381015653dc158d1f..e672ff4df2da15969f29a3882e541bd3ae441b7c 100644 (file)
@@ -1062,17 +1062,4 @@ static struct pci_driver ad1889_pci_driver = {
        .remove = __devexit_p(snd_ad1889_remove),
 };
 
-static int __init
-alsa_ad1889_init(void)
-{
-       return pci_register_driver(&ad1889_pci_driver);
-}
-
-static void __exit
-alsa_ad1889_fini(void)
-{
-       pci_unregister_driver(&ad1889_pci_driver);
-}
-
-module_init(alsa_ad1889_init);
-module_exit(alsa_ad1889_fini);
+module_pci_driver(ad1889_pci_driver);
index bdd6164e9c7eca267115564ad30e05e4860b18f8..9dfc27bf6cc645d5c1ecc6dd3d2d68920221a570 100644 (file)
@@ -2294,7 +2294,7 @@ static void __devexit snd_ali_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ali5451_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_ali_ids,
        .probe = snd_ali_probe,
@@ -2305,15 +2305,4 @@ static struct pci_driver driver = {
 #endif
 };                                
 
-static int __init alsa_card_ali_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ali_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ali_init)
-module_exit(alsa_card_ali_exit)
+module_pci_driver(ali5451_driver);
index 8196e229b2dfbeb4801413b9f87bc7f3e06177aa..59d65388faf53d890d7ecd9603ac0d772a7a0e48 100644 (file)
@@ -852,7 +852,7 @@ static int __devinit snd_als300_probe(struct pci_dev *pci,
        return 0;
 }
 
-static struct pci_driver driver = {
+static struct pci_driver als300_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_als300_ids,
        .probe = snd_als300_probe,
@@ -863,15 +863,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_als300_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als300_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als300_init)
-module_exit(alsa_card_als300_exit)
+module_pci_driver(als300_driver);
index 3269b8011ea9692daa0068df3be024d7848fc96c..7d7f2598c7487c076cec8480f2a35c365bb5b946 100644 (file)
@@ -1036,7 +1036,7 @@ static int snd_als4000_resume(struct pci_dev *pci)
 #endif /* CONFIG_PM */
 
 
-static struct pci_driver driver = {
+static struct pci_driver als4000_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_als4000_ids,
        .probe = snd_card_als4000_probe,
@@ -1047,15 +1047,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_als4000_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_als4000_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_als4000_init)
-module_exit(alsa_card_als4000_exit)
+module_pci_driver(als4000_driver);
index 590682f115ef20f6ecf9e52423049fd28de6f06c..156a94f8a1234aa6cbd8af5f8dd2e08a322b1172 100644 (file)
@@ -1700,7 +1700,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver atiixp_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_atiixp_ids,
        .probe = snd_atiixp_probe,
@@ -1711,16 +1711,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-
-static int __init alsa_card_atiixp_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_driver);
index 524d35f312321145cc08461d78d9b498e4907d9f..30a4fd96ce739c937f52ecbe89d8ed3c2113248b 100644 (file)
@@ -1331,7 +1331,7 @@ static void __devexit snd_atiixp_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver atiixp_modem_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_atiixp_ids,
        .probe = snd_atiixp_probe,
@@ -1342,16 +1342,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-
-static int __init alsa_card_atiixp_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_atiixp_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_atiixp_init)
-module_exit(alsa_card_atiixp_exit)
+module_pci_driver(atiixp_modem_driver);
index f13ad536b2d59fe19ab9826b5abc0c301d5133cf..ffc376f9f4e46f03d61aa808c48423d93e70748e 100644 (file)
@@ -375,24 +375,11 @@ static void __devexit snd_vortex_remove(struct pci_dev *pci)
 }
 
 // pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver vortex_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_vortex_ids,
        .probe = snd_vortex_probe,
        .remove = __devexit_p(snd_vortex_remove),
 };
 
-// initialization of the module
-static int __init alsa_card_vortex_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_vortex_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vortex_init)
-module_exit(alsa_card_vortex_exit)
+module_pci_driver(vortex_driver);
index 1c5231931462926f5919c57814b034f8474951d7..0f804741825f7ef9db7f18f5bb0bd358188bf3c6 100644 (file)
@@ -112,8 +112,6 @@ struct aw2 {
 /*********************************
  * FUNCTION DECLARATIONS
  ********************************/
-static int __init alsa_card_aw2_init(void);
-static void __exit alsa_card_aw2_exit(void);
 static int snd_aw2_dev_free(struct snd_device *device);
 static int __devinit snd_aw2_create(struct snd_card *card,
                                    struct pci_dev *pci, struct aw2 **rchip);
@@ -171,13 +169,15 @@ static DEFINE_PCI_DEVICE_TABLE(snd_aw2_ids) = {
 MODULE_DEVICE_TABLE(pci, snd_aw2_ids);
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver aw2_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_aw2_ids,
        .probe = snd_aw2_probe,
        .remove = __devexit_p(snd_aw2_remove),
 };
 
+module_pci_driver(aw2_driver);
+
 /* operators for playback PCM alsa interface */
 static struct snd_pcm_ops snd_aw2_playback_ops = {
        .open = snd_aw2_pcm_playback_open,
@@ -217,23 +217,6 @@ static struct snd_kcontrol_new aw2_control __devinitdata = {
  * FUNCTION IMPLEMENTATIONS
  ********************************/
 
-/* initialization of the module */
-static int __init alsa_card_aw2_init(void)
-{
-       snd_printdd(KERN_DEBUG "aw2: Load aw2 module\n");
-       return pci_register_driver(&driver);
-}
-
-/* clean up the module */
-static void __exit alsa_card_aw2_exit(void)
-{
-       snd_printdd(KERN_DEBUG "aw2: Unload aw2 module\n");
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_aw2_init);
-module_exit(alsa_card_aw2_exit);
-
 /* component-destructor */
 static int snd_aw2_dev_free(struct snd_device *device)
 {
index 496f14c1a731e78071d30c814f6f0e08c143d85e..f0b4d7493af523083faa11422e5428d4b466bb7b 100644 (file)
@@ -2862,7 +2862,7 @@ snd_azf3328_resume(struct pci_dev *pci)
 #endif /* CONFIG_PM */
 
 
-static struct pci_driver driver = {
+static struct pci_driver azf3328_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_azf3328_ids,
        .probe = snd_azf3328_probe,
@@ -2873,23 +2873,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init
-alsa_card_azf3328_init(void)
-{
-       int err;
-       snd_azf3328_dbgcallenter();
-       err = pci_register_driver(&driver);
-       snd_azf3328_dbgcallleave();
-       return err;
-}
-
-static void __exit
-alsa_card_azf3328_exit(void)
-{
-       snd_azf3328_dbgcallenter();
-       pci_unregister_driver(&driver);
-       snd_azf3328_dbgcallleave();
-}
-
-module_init(alsa_card_azf3328_init)
-module_exit(alsa_card_azf3328_exit)
+module_pci_driver(azf3328_driver);
index 62d6163fc9d9c0bdfa2ef49ffbb230c2ea82386a..b6a95eeca095fe922d8418c4fd05ee5bcc7fe337 100644 (file)
@@ -836,8 +836,6 @@ static struct {
        {0x7063, 0x2000}, /* pcHDTV HD-2000 TV */
 };
 
-static struct pci_driver driver;
-
 /* return the id of the card, or a negative value if it's blacklisted */
 static int __devinit snd_bt87x_detect_card(struct pci_dev *pci)
 {
@@ -964,24 +962,11 @@ static DEFINE_PCI_DEVICE_TABLE(snd_bt87x_default_ids) = {
        { }
 };
 
-static struct pci_driver driver = {
+static struct pci_driver bt87x_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_bt87x_ids,
        .probe = snd_bt87x_probe,
        .remove = __devexit_p(snd_bt87x_remove),
 };
 
-static int __init alsa_card_bt87x_init(void)
-{
-       if (load_all)
-               driver.id_table = snd_bt87x_default_ids;
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_bt87x_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_bt87x_init)
-module_exit(alsa_card_bt87x_exit)
+module_pci_driver(bt87x_driver);
index 08d6ebfe5a610d6b3c7a1951d43d9f44ca219838..e76d68a7081f7ec5795a4b0b6dc40b9e1564bd91 100644 (file)
@@ -1932,7 +1932,7 @@ static DEFINE_PCI_DEVICE_TABLE(snd_ca0106_ids) = {
 MODULE_DEVICE_TABLE(pci, snd_ca0106_ids);
 
 // pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver ca0106_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_ca0106_ids,
        .probe = snd_ca0106_probe,
@@ -1943,17 +1943,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-// initialization of the module
-static int __init alsa_card_ca0106_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_ca0106_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ca0106_init)
-module_exit(alsa_card_ca0106_exit)
+module_pci_driver(ca0106_driver);
index 19b06269adc22df7b54bd1ad941e4a4796745423..3815bd4c67790d009776856c9d5e5cf4a0de34cb 100644 (file)
@@ -3398,7 +3398,7 @@ static int snd_cmipci_resume(struct pci_dev *pci)
 }
 #endif /* CONFIG_PM */
 
-static struct pci_driver driver = {
+static struct pci_driver cmipci_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cmipci_ids,
        .probe = snd_cmipci_probe,
@@ -3409,15 +3409,4 @@ static struct pci_driver driver = {
 #endif
 };
        
-static int __init alsa_card_cmipci_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cmipci_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cmipci_init)
-module_exit(alsa_card_cmipci_exit)
+module_pci_driver(cmipci_driver);
index a9f368f60df6f3b7b271ce6f8e08f6a80259da35..33506ee569bd48c62411a1b014fbd5a28d69e26c 100644 (file)
@@ -2084,7 +2084,7 @@ static int cs4281_resume(struct pci_dev *pci)
 }
 #endif /* CONFIG_PM */
 
-static struct pci_driver driver = {
+static struct pci_driver cs4281_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cs4281_ids,
        .probe = snd_cs4281_probe,
@@ -2095,15 +2095,4 @@ static struct pci_driver driver = {
 #endif
 };
        
-static int __init alsa_card_cs4281_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs4281_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs4281_init)
-module_exit(alsa_card_cs4281_exit)
+module_pci_driver(cs4281_driver);
index 819d79d0586de2909f308fe1094047a7b0830820..6cc7404e0e8f62991120eb5a86f835017c84cdf3 100644 (file)
@@ -161,7 +161,7 @@ static void __devexit snd_card_cs46xx_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cs46xx_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cs46xx_ids,
        .probe = snd_card_cs46xx_probe,
@@ -172,15 +172,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_cs46xx_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs46xx_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs46xx_init)
-module_exit(alsa_card_cs46xx_exit)
+module_pci_driver(cs46xx_driver);
index c47cabff2bfa2b6cbfd92755a8f5658f43ba90b5..f1e4229993af198a23afb4f33a93f1153251e9b5 100644 (file)
@@ -291,23 +291,11 @@ static int __devinit snd_cs5530_probe(struct pci_dev *pci,
        return 0;
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cs5530_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cs5530_ids,
        .probe = snd_cs5530_probe,
        .remove = __devexit_p(snd_cs5530_remove),
 };
 
-static int __init alsa_card_cs5530_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5530_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5530_init)
-module_exit(alsa_card_cs5530_exit)
-
+module_pci_driver(cs5530_driver);
index a2fb2173e980ea3697341446867d7d52a2a8350f..2c9697cf0a1a7c2f3e4dc650b9f505522e69afa2 100644 (file)
@@ -394,7 +394,7 @@ static void __devexit snd_cs5535audio_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver cs5535audio_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_cs5535audio_ids,
        .probe = snd_cs5535audio_probe,
@@ -405,18 +405,7 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_cs5535audio_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_cs5535audio_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_cs5535audio_init)
-module_exit(alsa_card_cs5535audio_exit)
+module_pci_driver(cs5535audio_driver);
 
 MODULE_AUTHOR("Jaya Kumar");
 MODULE_LICENSE("GPL");
index 15d95d2bacee67ff9c1c17c86a01e7aee6a44b75..75aa2c3384108c414d89d169a4a4bf8a0b8cbf7b 100644 (file)
@@ -154,15 +154,4 @@ static struct pci_driver ct_driver = {
 #endif
 };
 
-static int __init ct_card_init(void)
-{
-       return pci_register_driver(&ct_driver);
-}
-
-static void __exit ct_card_exit(void)
-{
-       pci_unregister_driver(&ct_driver);
-}
-
-module_init(ct_card_init)
-module_exit(ct_card_exit)
+module_pci_driver(ct_driver);
index 595c11f904bbf72e5cded83a807f8e8c7f9c3acd..0f8eda1dafdbb8ac11f5e570cda74c5b2373463d 100644 (file)
@@ -2328,7 +2328,7 @@ static void __devexit snd_echo_remove(struct pci_dev *pci)
 ******************************************************************************/
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver echo_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_echo_ids,
        .probe = snd_echo_probe,
@@ -2339,22 +2339,4 @@ static struct pci_driver driver = {
 #endif /* CONFIG_PM */
 };
 
-
-
-/* initialization of the module */
-static int __init alsa_card_echo_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-
-
-/* clean up the module */
-static void __exit alsa_card_echo_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-
-module_init(alsa_card_echo_init)
-module_exit(alsa_card_echo_exit)
+module_pci_driver(echo_driver);
index 790c65d980c8550c828dde2baa0be1c40be40adf..7fdbbe4d9965385be9f12aa1db0fb011b1de946b 100644 (file)
@@ -263,7 +263,7 @@ static int snd_emu10k1_resume(struct pci_dev *pci)
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver emu10k1_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_emu10k1_ids,
        .probe = snd_card_emu10k1_probe,
@@ -274,15 +274,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_emu10k1_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_emu10k1_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1_init)
-module_exit(alsa_card_emu10k1_exit)
+module_pci_driver(emu10k1_driver);
index 47a651cb6e842d78c3cdc42bc3884de129a36baa..5c8978b2c4d9b1486afb26566a40b384e48106b0 100644 (file)
@@ -1612,24 +1612,11 @@ static DEFINE_PCI_DEVICE_TABLE(snd_emu10k1x_ids) = {
 MODULE_DEVICE_TABLE(pci, snd_emu10k1x_ids);
 
 // pci_driver definition
-static struct pci_driver driver = {
+static struct pci_driver emu10k1x_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_emu10k1x_ids,
        .probe = snd_emu10k1x_probe,
        .remove = __devexit_p(snd_emu10k1x_remove),
 };
 
-// initialization of the module
-static int __init alsa_card_emu10k1x_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-// clean up the module
-static void __exit alsa_card_emu10k1x_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_emu10k1x_init)
-module_exit(alsa_card_emu10k1x_exit)
+module_pci_driver(emu10k1x_driver);
index 47a245e84190c08279075b5f14433fa1906a48a7..3821c81d1c993a24961aa4d416d70c8bbc6cd480 100644 (file)
@@ -2488,7 +2488,7 @@ static void __devexit snd_audiopci_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ens137x_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_audiopci_ids,
        .probe = snd_audiopci_probe,
@@ -2499,15 +2499,4 @@ static struct pci_driver driver = {
 #endif
 };
        
-static int __init alsa_card_ens137x_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ens137x_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ens137x_init)
-module_exit(alsa_card_ens137x_exit)
+module_pci_driver(ens137x_driver);
index 53eb76b41108a07da237bf33af48f2cfe1da0d92..82c8d8c5c52ae724657c9bd7212fddc333d4cd5e 100644 (file)
@@ -1882,7 +1882,7 @@ static void __devexit snd_es1938_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver es1938_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_es1938_ids,
        .probe = snd_es1938_probe,
@@ -1893,15 +1893,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_es1938_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1938_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1938_init)
-module_exit(alsa_card_es1938_exit)
+module_pci_driver(es1938_driver);
index a8faae1c85e46787b8e240c3cd80fdc189da931d..67f47d891959661c326902cf5fa7803d39a09be0 100644 (file)
@@ -2898,7 +2898,7 @@ static void __devexit snd_es1968_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver es1968_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_es1968_ids,
        .probe = snd_es1968_probe,
@@ -2909,15 +2909,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_es1968_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_es1968_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_es1968_init)
-module_exit(alsa_card_es1968_exit)
+module_pci_driver(es1968_driver);
index a416ea8af3e9b3c39bef71cb92832f425217380c..f696623227503763d9996589034244314f39568c 100644 (file)
@@ -1416,7 +1416,7 @@ static int snd_fm801_resume(struct pci_dev *pci)
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver fm801_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_fm801_ids,
        .probe = snd_card_fm801_probe,
@@ -1427,15 +1427,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_fm801_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_fm801_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_fm801_init)
-module_exit(alsa_card_fm801_exit)
+module_pci_driver(fm801_driver);
index ace157cc3d15c7e5b560910ca1adcfb2162d093a..bd4149f1aaf45f1f1300563c4aa469d483c2530f 100644 (file)
@@ -1,6 +1,6 @@
 snd-hda-intel-objs := hda_intel.o
 
-snd-hda-codec-y := hda_codec.o hda_jack.o
+snd-hda-codec-y := hda_codec.o hda_jack.o hda_auto_parser.o
 snd-hda-codec-$(CONFIG_SND_HDA_GENERIC) += hda_generic.o
 snd-hda-codec-$(CONFIG_PROC_FS) += hda_proc.o
 snd-hda-codec-$(CONFIG_SND_HDA_HWDEP) += hda_hwdep.o
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
new file mode 100644 (file)
index 0000000..6e9ef3e
--- /dev/null
@@ -0,0 +1,760 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver 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.
+ */
+
+#include <linux/slab.h>
+#include <linux/export.h>
+#include <sound/core.h>
+#include "hda_codec.h"
+#include "hda_local.h"
+#include "hda_auto_parser.h"
+
+#define SFX    "hda_codec: "
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
+{
+       for (; *list; list++)
+               if (*list == nid)
+                       return 1;
+       return 0;
+}
+
+
+/*
+ * Sort an associated group of pins according to their sequence numbers.
+ */
+static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
+                                 int num_pins)
+{
+       int i, j;
+       short seq;
+       hda_nid_t nid;
+
+       for (i = 0; i < num_pins; i++) {
+               for (j = i + 1; j < num_pins; j++) {
+                       if (sequences[i] > sequences[j]) {
+                               seq = sequences[i];
+                               sequences[i] = sequences[j];
+                               sequences[j] = seq;
+                               nid = pins[i];
+                               pins[i] = pins[j];
+                               pins[j] = nid;
+                       }
+               }
+       }
+}
+
+
+/* add the found input-pin to the cfg->inputs[] table */
+static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
+                                  int type)
+{
+       if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
+               cfg->inputs[cfg->num_inputs].pin = nid;
+               cfg->inputs[cfg->num_inputs].type = type;
+               cfg->num_inputs++;
+       }
+}
+
+/* sort inputs in the order of AUTO_PIN_* type */
+static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
+{
+       int i, j;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               for (j = i + 1; j < cfg->num_inputs; j++) {
+                       if (cfg->inputs[i].type > cfg->inputs[j].type) {
+                               struct auto_pin_cfg_item tmp;
+                               tmp = cfg->inputs[i];
+                               cfg->inputs[i] = cfg->inputs[j];
+                               cfg->inputs[j] = tmp;
+                       }
+               }
+       }
+}
+
+/* Reorder the surround channels
+ * ALSA sequence is front/surr/clfe/side
+ * HDA sequence is:
+ *    4-ch: front/surr  =>  OK as it is
+ *    6-ch: front/clfe/surr
+ *    8-ch: front/clfe/rear/side|fc
+ */
+static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
+{
+       hda_nid_t nid;
+
+       switch (nums) {
+       case 3:
+       case 4:
+               nid = pins[1];
+               pins[1] = pins[2];
+               pins[2] = nid;
+               break;
+       }
+}
+
+/*
+ * Parse all pin widgets and store the useful pin nids to cfg
+ *
+ * The number of line-outs or any primary output is stored in line_outs,
+ * and the corresponding output pins are assigned to line_out_pins[],
+ * in the order of front, rear, CLFE, side, ...
+ *
+ * If more extra outputs (speaker and headphone) are found, the pins are
+ * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
+ * is detected, one of speaker of HP pins is assigned as the primary
+ * output, i.e. to line_out_pins[0].  So, line_outs is always positive
+ * if any analog output exists.
+ *
+ * The analog input pins are assigned to inputs array.
+ * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
+ * respectively.
+ */
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+                            struct auto_pin_cfg *cfg,
+                            const hda_nid_t *ignore_nids,
+                            unsigned int cond_flags)
+{
+       hda_nid_t nid, end_nid;
+       short seq, assoc_line_out;
+       short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
+       short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
+       short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
+       int i;
+
+       memset(cfg, 0, sizeof(*cfg));
+
+       memset(sequences_line_out, 0, sizeof(sequences_line_out));
+       memset(sequences_speaker, 0, sizeof(sequences_speaker));
+       memset(sequences_hp, 0, sizeof(sequences_hp));
+       assoc_line_out = 0;
+
+       codec->ignore_misc_bit = true;
+       end_nid = codec->start_nid + codec->num_nodes;
+       for (nid = codec->start_nid; nid < end_nid; nid++) {
+               unsigned int wid_caps = get_wcaps(codec, nid);
+               unsigned int wid_type = get_wcaps_type(wid_caps);
+               unsigned int def_conf;
+               short assoc, loc, conn, dev;
+
+               /* read all default configuration for pin complex */
+               if (wid_type != AC_WID_PIN)
+                       continue;
+               /* ignore the given nids (e.g. pc-beep returns error) */
+               if (ignore_nids && is_in_nid_list(nid, ignore_nids))
+                       continue;
+
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
+                     AC_DEFCFG_MISC_NO_PRESENCE))
+                       codec->ignore_misc_bit = false;
+               conn = get_defcfg_connect(def_conf);
+               if (conn == AC_JACK_PORT_NONE)
+                       continue;
+               loc = get_defcfg_location(def_conf);
+               dev = get_defcfg_device(def_conf);
+
+               /* workaround for buggy BIOS setups */
+               if (dev == AC_JACK_LINE_OUT) {
+                       if (conn == AC_JACK_PORT_FIXED)
+                               dev = AC_JACK_SPEAKER;
+               }
+
+               switch (dev) {
+               case AC_JACK_LINE_OUT:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+
+                       if (!(wid_caps & AC_WCAP_STEREO))
+                               if (!cfg->mono_out_pin)
+                                       cfg->mono_out_pin = nid;
+                       if (!assoc)
+                               continue;
+                       if (!assoc_line_out)
+                               assoc_line_out = assoc;
+                       else if (assoc_line_out != assoc)
+                               continue;
+                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
+                               continue;
+                       cfg->line_out_pins[cfg->line_outs] = nid;
+                       sequences_line_out[cfg->line_outs] = seq;
+                       cfg->line_outs++;
+                       break;
+               case AC_JACK_SPEAKER:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+                       if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
+                               continue;
+                       cfg->speaker_pins[cfg->speaker_outs] = nid;
+                       sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
+                       cfg->speaker_outs++;
+                       break;
+               case AC_JACK_HP_OUT:
+                       seq = get_defcfg_sequence(def_conf);
+                       assoc = get_defcfg_association(def_conf);
+                       if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
+                               continue;
+                       cfg->hp_pins[cfg->hp_outs] = nid;
+                       sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
+                       cfg->hp_outs++;
+                       break;
+               case AC_JACK_MIC_IN:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
+                       break;
+               case AC_JACK_LINE_IN:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
+                       break;
+               case AC_JACK_CD:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
+                       break;
+               case AC_JACK_AUX:
+                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
+                       break;
+               case AC_JACK_SPDIF_OUT:
+               case AC_JACK_DIG_OTHER_OUT:
+                       if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
+                               continue;
+                       cfg->dig_out_pins[cfg->dig_outs] = nid;
+                       cfg->dig_out_type[cfg->dig_outs] =
+                               (loc == AC_JACK_LOC_HDMI) ?
+                               HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
+                       cfg->dig_outs++;
+                       break;
+               case AC_JACK_SPDIF_IN:
+               case AC_JACK_DIG_OTHER_IN:
+                       cfg->dig_in_pin = nid;
+                       if (loc == AC_JACK_LOC_HDMI)
+                               cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
+                       else
+                               cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
+                       break;
+               }
+       }
+
+       /* FIX-UP:
+        * If no line-out is defined but multiple HPs are found,
+        * some of them might be the real line-outs.
+        */
+       if (!cfg->line_outs && cfg->hp_outs > 1 &&
+           !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
+               int i = 0;
+               while (i < cfg->hp_outs) {
+                       /* The real HPs should have the sequence 0x0f */
+                       if ((sequences_hp[i] & 0x0f) == 0x0f) {
+                               i++;
+                               continue;
+                       }
+                       /* Move it to the line-out table */
+                       cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
+                       sequences_line_out[cfg->line_outs] = sequences_hp[i];
+                       cfg->line_outs++;
+                       cfg->hp_outs--;
+                       memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
+                               sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
+                       memmove(sequences_hp + i, sequences_hp + i + 1,
+                               sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
+               }
+               memset(cfg->hp_pins + cfg->hp_outs, 0,
+                      sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
+               if (!cfg->hp_outs)
+                       cfg->line_out_type = AUTO_PIN_HP_OUT;
+
+       }
+
+       /* sort by sequence */
+       sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
+                             cfg->line_outs);
+       sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
+                             cfg->speaker_outs);
+       sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
+                             cfg->hp_outs);
+
+       /*
+        * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
+        * as a primary output
+        */
+       if (!cfg->line_outs &&
+           !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
+               if (cfg->speaker_outs) {
+                       cfg->line_outs = cfg->speaker_outs;
+                       memcpy(cfg->line_out_pins, cfg->speaker_pins,
+                              sizeof(cfg->speaker_pins));
+                       cfg->speaker_outs = 0;
+                       memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
+                       cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
+               } else if (cfg->hp_outs) {
+                       cfg->line_outs = cfg->hp_outs;
+                       memcpy(cfg->line_out_pins, cfg->hp_pins,
+                              sizeof(cfg->hp_pins));
+                       cfg->hp_outs = 0;
+                       memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
+                       cfg->line_out_type = AUTO_PIN_HP_OUT;
+               }
+       }
+
+       reorder_outputs(cfg->line_outs, cfg->line_out_pins);
+       reorder_outputs(cfg->hp_outs, cfg->hp_pins);
+       reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
+
+       sort_autocfg_input_pins(cfg);
+
+       /*
+        * debug prints of the parsed results
+        */
+       snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
+                  cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
+                  cfg->line_out_pins[2], cfg->line_out_pins[3],
+                  cfg->line_out_pins[4],
+                  cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
+                  (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
+                   "speaker" : "line"));
+       snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  cfg->speaker_outs, cfg->speaker_pins[0],
+                  cfg->speaker_pins[1], cfg->speaker_pins[2],
+                  cfg->speaker_pins[3], cfg->speaker_pins[4]);
+       snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
+                  cfg->hp_outs, cfg->hp_pins[0],
+                  cfg->hp_pins[1], cfg->hp_pins[2],
+                  cfg->hp_pins[3], cfg->hp_pins[4]);
+       snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
+       if (cfg->dig_outs)
+               snd_printd("   dig-out=0x%x/0x%x\n",
+                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
+       snd_printd("   inputs:");
+       for (i = 0; i < cfg->num_inputs; i++) {
+               snd_printd(" %s=0x%x",
+                           hda_get_autocfg_input_label(codec, cfg, i),
+                           cfg->inputs[i].pin);
+       }
+       snd_printd("\n");
+       if (cfg->dig_in_pin)
+               snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
+
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf)
+{
+       unsigned int loc = get_defcfg_location(def_conf);
+       unsigned int conn = get_defcfg_connect(def_conf);
+       if (conn == AC_JACK_PORT_NONE)
+               return INPUT_PIN_ATTR_UNUSED;
+       /* Windows may claim the internal mic to be BOTH, too */
+       if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
+               return INPUT_PIN_ATTR_INT;
+       if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
+               return INPUT_PIN_ATTR_INT;
+       if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
+               return INPUT_PIN_ATTR_DOCK;
+       if (loc == AC_JACK_LOC_REAR)
+               return INPUT_PIN_ATTR_REAR;
+       if (loc == AC_JACK_LOC_FRONT)
+               return INPUT_PIN_ATTR_FRONT;
+       return INPUT_PIN_ATTR_NORMAL;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
+
+/**
+ * hda_get_input_pin_label - Give a label for the given input pin
+ *
+ * When check_location is true, the function checks the pin location
+ * for mic and line-in pins, and set an appropriate prefix like "Front",
+ * "Rear", "Internal".
+ */
+
+static const char *hda_get_input_pin_label(struct hda_codec *codec,
+                                          hda_nid_t pin, bool check_location)
+{
+       unsigned int def_conf;
+       static const char * const mic_names[] = {
+               "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
+       };
+       int attr;
+
+       def_conf = snd_hda_codec_get_pincfg(codec, pin);
+
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_MIC_IN:
+               if (!check_location)
+                       return "Mic";
+               attr = snd_hda_get_input_pin_attr(def_conf);
+               if (!attr)
+                       return "None";
+               return mic_names[attr - 1];
+       case AC_JACK_LINE_IN:
+               if (!check_location)
+                       return "Line";
+               attr = snd_hda_get_input_pin_attr(def_conf);
+               if (!attr)
+                       return "None";
+               if (attr == INPUT_PIN_ATTR_DOCK)
+                       return "Dock Line";
+               return "Line";
+       case AC_JACK_AUX:
+               return "Aux";
+       case AC_JACK_CD:
+               return "CD";
+       case AC_JACK_SPDIF_IN:
+               return "SPDIF In";
+       case AC_JACK_DIG_OTHER_IN:
+               return "Digital In";
+       default:
+               return "Misc";
+       }
+}
+
+/* Check whether the location prefix needs to be added to the label.
+ * If all mic-jacks are in the same location (e.g. rear panel), we don't
+ * have to put "Front" prefix to each label.  In such a case, returns false.
+ */
+static int check_mic_location_need(struct hda_codec *codec,
+                                  const struct auto_pin_cfg *cfg,
+                                  int input)
+{
+       unsigned int defc;
+       int i, attr, attr2;
+
+       defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
+       attr = snd_hda_get_input_pin_attr(defc);
+       /* for internal or docking mics, we need locations */
+       if (attr <= INPUT_PIN_ATTR_NORMAL)
+               return 1;
+
+       attr = 0;
+       for (i = 0; i < cfg->num_inputs; i++) {
+               defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
+               attr2 = snd_hda_get_input_pin_attr(defc);
+               if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
+                       if (attr && attr != attr2)
+                               return 1; /* different locations found */
+                       attr = attr2;
+               }
+       }
+       return 0;
+}
+
+/**
+ * hda_get_autocfg_input_label - Get a label for the given input
+ *
+ * Get a label for the given input pin defined by the autocfg item.
+ * Unlike hda_get_input_pin_label(), this function checks all inputs
+ * defined in autocfg and avoids the redundant mic/line prefix as much as
+ * possible.
+ */
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+                                       const struct auto_pin_cfg *cfg,
+                                       int input)
+{
+       int type = cfg->inputs[input].type;
+       int has_multiple_pins = 0;
+
+       if ((input > 0 && cfg->inputs[input - 1].type == type) ||
+           (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
+               has_multiple_pins = 1;
+       if (has_multiple_pins && type == AUTO_PIN_MIC)
+               has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+       return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
+                                      has_multiple_pins);
+}
+EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
+
+/* return the position of NID in the list, or -1 if not found */
+static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
+{
+       int i;
+       for (i = 0; i < nums; i++)
+               if (list[i] == nid)
+                       return i;
+       return -1;
+}
+
+/* get a unique suffix or an index number */
+static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
+                                   int num_pins, int *indexp)
+{
+       static const char * const channel_sfx[] = {
+               " Front", " Surround", " CLFE", " Side"
+       };
+       int i;
+
+       i = find_idx_in_nid_list(nid, pins, num_pins);
+       if (i < 0)
+               return NULL;
+       if (num_pins == 1)
+               return "";
+       if (num_pins > ARRAY_SIZE(channel_sfx)) {
+               if (indexp)
+                       *indexp = i;
+               return "";
+       }
+       return channel_sfx[i];
+}
+
+static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
+                              const struct auto_pin_cfg *cfg,
+                              const char *name, char *label, int maxlen,
+                              int *indexp)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       int attr = snd_hda_get_input_pin_attr(def_conf);
+       const char *pfx = "", *sfx = "";
+
+       /* handle as a speaker if it's a fixed line-out */
+       if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
+               name = "Speaker";
+       /* check the location */
+       switch (attr) {
+       case INPUT_PIN_ATTR_DOCK:
+               pfx = "Dock ";
+               break;
+       case INPUT_PIN_ATTR_FRONT:
+               pfx = "Front ";
+               break;
+       }
+       if (cfg) {
+               /* try to give a unique suffix if needed */
+               sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
+                                      indexp);
+               if (!sfx)
+                       sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
+                                              indexp);
+               if (!sfx) {
+                       /* don't add channel suffix for Headphone controls */
+                       int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
+                                                      cfg->hp_outs);
+                       if (idx >= 0)
+                               *indexp = idx;
+                       sfx = "";
+               }
+       }
+       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
+       return 1;
+}
+
+/**
+ * snd_hda_get_pin_label - Get a label for the given I/O pin
+ *
+ * Get a label for the given pin.  This function works for both input and
+ * output pins.  When @cfg is given as non-NULL, the function tries to get
+ * an optimized label using hda_get_autocfg_input_label().
+ *
+ * This function tries to give a unique label string for the pin as much as
+ * possible.  For example, when the multiple line-outs are present, it adds
+ * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
+ * If no unique name with a suffix is available and @indexp is non-NULL, the
+ * index number is stored in the pointer.
+ */
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+                         const struct auto_pin_cfg *cfg,
+                         char *label, int maxlen, int *indexp)
+{
+       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
+       const char *name = NULL;
+       int i;
+
+       if (indexp)
+               *indexp = 0;
+       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
+               return 0;
+
+       switch (get_defcfg_device(def_conf)) {
+       case AC_JACK_LINE_OUT:
+               return fill_audio_out_name(codec, nid, cfg, "Line Out",
+                                          label, maxlen, indexp);
+       case AC_JACK_SPEAKER:
+               return fill_audio_out_name(codec, nid, cfg, "Speaker",
+                                          label, maxlen, indexp);
+       case AC_JACK_HP_OUT:
+               return fill_audio_out_name(codec, nid, cfg, "Headphone",
+                                          label, maxlen, indexp);
+       case AC_JACK_SPDIF_OUT:
+       case AC_JACK_DIG_OTHER_OUT:
+               if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
+                       name = "HDMI";
+               else
+                       name = "SPDIF";
+               if (cfg && indexp) {
+                       i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
+                                                cfg->dig_outs);
+                       if (i >= 0)
+                               *indexp = i;
+               }
+               break;
+       default:
+               if (cfg) {
+                       for (i = 0; i < cfg->num_inputs; i++) {
+                               if (cfg->inputs[i].pin != nid)
+                                       continue;
+                               name = hda_get_autocfg_input_label(codec, cfg, i);
+                               if (name)
+                                       break;
+                       }
+               }
+               if (!name)
+                       name = hda_get_input_pin_label(codec, nid, true);
+               break;
+       }
+       if (!name)
+               return 0;
+       strlcpy(label, name, maxlen);
+       return 1;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+                         const struct hda_verb *list)
+{
+       const struct hda_verb **v;
+       snd_array_init(&spec->verbs, sizeof(struct hda_verb *), 8);
+       v = snd_array_new(&spec->verbs);
+       if (!v)
+               return -ENOMEM;
+       *v = list;
+       return 0;
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_add_verbs);
+
+void snd_hda_gen_apply_verbs(struct hda_codec *codec)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int i;
+       for (i = 0; i < spec->verbs.used; i++) {
+               struct hda_verb **v = snd_array_elem(&spec->verbs, i);
+               snd_hda_sequence_write(codec, *v);
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_gen_apply_verbs);
+
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+                          const struct hda_pintbl *cfg)
+{
+       for (; cfg->nid; cfg++)
+               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_pincfgs);
+
+void snd_hda_apply_fixup(struct hda_codec *codec, int action)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       int id = spec->fixup_id;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       const char *modelname = spec->fixup_name;
+#endif
+       int depth = 0;
+
+       if (!spec->fixup_list)
+               return;
+
+       while (id >= 0) {
+               const struct hda_fixup *fix = spec->fixup_list + id;
+
+               switch (fix->type) {
+               case HDA_FIXUP_PINS:
+                       if (action != HDA_FIXUP_ACT_PRE_PROBE || !fix->v.pins)
+                               break;
+                       snd_printdd(KERN_INFO SFX
+                                   "%s: Apply pincfg for %s\n",
+                                   codec->chip_name, modelname);
+                       snd_hda_apply_pincfgs(codec, fix->v.pins);
+                       break;
+               case HDA_FIXUP_VERBS:
+                       if (action != HDA_FIXUP_ACT_PROBE || !fix->v.verbs)
+                               break;
+                       snd_printdd(KERN_INFO SFX
+                                   "%s: Apply fix-verbs for %s\n",
+                                   codec->chip_name, modelname);
+                       snd_hda_gen_add_verbs(codec->spec, fix->v.verbs);
+                       break;
+               case HDA_FIXUP_FUNC:
+                       if (!fix->v.func)
+                               break;
+                       snd_printdd(KERN_INFO SFX
+                                   "%s: Apply fix-func for %s\n",
+                                   codec->chip_name, modelname);
+                       fix->v.func(codec, fix, action);
+                       break;
+               default:
+                       snd_printk(KERN_ERR SFX
+                                  "%s: Invalid fixup type %d\n",
+                                  codec->chip_name, fix->type);
+                       break;
+               }
+               if (!fix->chained)
+                       break;
+               if (++depth > 10)
+                       break;
+               id = fix->chain_id;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_apply_fixup);
+
+void snd_hda_pick_fixup(struct hda_codec *codec,
+                       const struct hda_model_fixup *models,
+                       const struct snd_pci_quirk *quirk,
+                       const struct hda_fixup *fixlist)
+{
+       struct hda_gen_spec *spec = codec->spec;
+       const struct snd_pci_quirk *q;
+       int id = -1;
+       const char *name = NULL;
+
+       /* when model=nofixup is given, don't pick up any fixups */
+       if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
+               spec->fixup_list = NULL;
+               spec->fixup_id = -1;
+               return;
+       }
+
+       if (codec->modelname && models) {
+               while (models->name) {
+                       if (!strcmp(codec->modelname, models->name)) {
+                               id = models->id;
+                               name = models->name;
+                               break;
+                       }
+                       models++;
+               }
+       }
+       if (id < 0) {
+               q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
+               if (q) {
+                       id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       name = q->name;
+#endif
+               }
+       }
+       if (id < 0) {
+               for (q = quirk; q->subvendor; q++) {
+                       unsigned int vendorid =
+                               q->subdevice | (q->subvendor << 16);
+                       if (vendorid == codec->subsystem_id) {
+                               id = q->value;
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                               name = q->name;
+#endif
+                               break;
+                       }
+               }
+       }
+
+       spec->fixup_id = id;
+       if (id >= 0) {
+               spec->fixup_list = fixlist;
+               spec->fixup_name = name;
+       }
+}
+EXPORT_SYMBOL_HDA(snd_hda_pick_fixup);
diff --git a/sound/pci/hda/hda_auto_parser.h b/sound/pci/hda/hda_auto_parser.h
new file mode 100644 (file)
index 0000000..2a7889d
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * BIOS auto-parser helper functions for HD-audio
+ *
+ * Copyright (c) 2012 Takashi Iwai <tiwai@suse.de>
+ *
+ * This driver 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.
+ */
+
+#ifndef __SOUND_HDA_AUTO_PARSER_H
+#define __SOUND_HDA_AUTO_PARSER_H
+
+/*
+ * Helper for automatic pin configuration
+ */
+
+enum {
+       AUTO_PIN_MIC,
+       AUTO_PIN_LINE_IN,
+       AUTO_PIN_CD,
+       AUTO_PIN_AUX,
+       AUTO_PIN_LAST
+};
+
+enum {
+       AUTO_PIN_LINE_OUT,
+       AUTO_PIN_SPEAKER_OUT,
+       AUTO_PIN_HP_OUT
+};
+
+#define AUTO_CFG_MAX_OUTS      HDA_MAX_OUTS
+#define AUTO_CFG_MAX_INS       8
+
+struct auto_pin_cfg_item {
+       hda_nid_t pin;
+       int type;
+};
+
+struct auto_pin_cfg;
+const char *hda_get_autocfg_input_label(struct hda_codec *codec,
+                                       const struct auto_pin_cfg *cfg,
+                                       int input);
+int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
+                         const struct auto_pin_cfg *cfg,
+                         char *label, int maxlen, int *indexp);
+
+enum {
+       INPUT_PIN_ATTR_UNUSED,  /* pin not connected */
+       INPUT_PIN_ATTR_INT,     /* internal mic/line-in */
+       INPUT_PIN_ATTR_DOCK,    /* docking mic/line-in */
+       INPUT_PIN_ATTR_NORMAL,  /* mic/line-in jack */
+       INPUT_PIN_ATTR_FRONT,   /* mic/line-in jack in front */
+       INPUT_PIN_ATTR_REAR,    /* mic/line-in jack in rear */
+};
+
+int snd_hda_get_input_pin_attr(unsigned int def_conf);
+
+struct auto_pin_cfg {
+       int line_outs;
+       /* sorted in the order of Front/Surr/CLFE/Side */
+       hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
+       int speaker_outs;
+       hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
+       int hp_outs;
+       int line_out_type;      /* AUTO_PIN_XXX_OUT */
+       hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
+       int num_inputs;
+       struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
+       int dig_outs;
+       hda_nid_t dig_out_pins[2];
+       hda_nid_t dig_in_pin;
+       hda_nid_t mono_out_pin;
+       int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
+       int dig_in_type; /* HDA_PCM_TYPE_XXX */
+};
+
+/* bit-flags for snd_hda_parse_pin_def_config() behavior */
+#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
+#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
+
+int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
+                            struct auto_pin_cfg *cfg,
+                            const hda_nid_t *ignore_nids,
+                            unsigned int cond_flags);
+
+/* older function */
+#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
+       snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
+
+/*
+ */
+
+struct hda_gen_spec {
+       /* fix-up list */
+       int fixup_id;
+       const struct hda_fixup *fixup_list;
+       const char *fixup_name;
+
+       /* additional init verbs */
+       struct snd_array verbs;
+};
+
+
+/*
+ * Fix-up pin default configurations and add default verbs
+ */
+
+struct hda_pintbl {
+       hda_nid_t nid;
+       u32 val;
+};
+
+struct hda_model_fixup {
+       const int id;
+       const char *name;
+};
+
+struct hda_fixup {
+       int type;
+       bool chained;
+       int chain_id;
+       union {
+               const struct hda_pintbl *pins;
+               const struct hda_verb *verbs;
+               void (*func)(struct hda_codec *codec,
+                            const struct hda_fixup *fix,
+                            int action);
+       } v;
+};
+
+/* fixup types */
+enum {
+       HDA_FIXUP_INVALID,
+       HDA_FIXUP_PINS,
+       HDA_FIXUP_VERBS,
+       HDA_FIXUP_FUNC,
+};
+
+/* fixup action definitions */
+enum {
+       HDA_FIXUP_ACT_PRE_PROBE,
+       HDA_FIXUP_ACT_PROBE,
+       HDA_FIXUP_ACT_INIT,
+       HDA_FIXUP_ACT_BUILD,
+};
+
+int snd_hda_gen_add_verbs(struct hda_gen_spec *spec,
+                         const struct hda_verb *list);
+void snd_hda_gen_apply_verbs(struct hda_codec *codec);
+void snd_hda_apply_pincfgs(struct hda_codec *codec,
+                          const struct hda_pintbl *cfg);
+void snd_hda_apply_fixup(struct hda_codec *codec, int action);
+void snd_hda_pick_fixup(struct hda_codec *codec,
+                       const struct hda_model_fixup *models,
+                       const struct snd_pci_quirk *quirk,
+                       const struct hda_fixup *fixlist);
+
+#endif /* __SOUND_HDA_AUTO_PARSER_H */
index 841475cc13b657a2627e107354e4b70251fb3296..eb09a3348325358b0a741d3408d48cc4e5e8fffd 100644 (file)
@@ -334,78 +334,67 @@ static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
        return NULL;
 }
 
+/* read the connection and add to the cache */
+static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+       hda_nid_t list[HDA_MAX_CONNECTIONS];
+       int len;
+
+       len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
+       if (len < 0)
+               return len;
+       return snd_hda_override_conn_list(codec, nid, len, list);
+}
+
 /**
- * snd_hda_get_conn_list - get connection list
+ * snd_hda_get_connections - copy connection list
  * @codec: the HDA codec
  * @nid: NID to parse
- * @listp: the pointer to store NID list
+ * @conn_list: connection list array; when NULL, checks only the size
+ * @max_conns: max. number of connections to store
  *
  * Parses the connection list of the given widget and stores the list
  * of NIDs.
  *
  * Returns the number of connections, or a negative error code.
  */
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-                         const hda_nid_t **listp)
+int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
+                           hda_nid_t *conn_list, int max_conns)
 {
        struct snd_array *array = &codec->conn_lists;
-       int len, err;
-       hda_nid_t list[HDA_MAX_CONNECTIONS];
+       int len;
        hda_nid_t *p;
        bool added = false;
 
  again:
+       mutex_lock(&codec->hash_mutex);
+       len = -1;
        /* if the connection-list is already cached, read it */
        p = lookup_conn_list(array, nid);
        if (p) {
-               if (listp)
-                       *listp = p + 2;
-               return p[1];
+               len = p[1];
+               if (conn_list && len > max_conns) {
+                       snd_printk(KERN_ERR "hda_codec: "
+                                  "Too many connections %d for NID 0x%x\n",
+                                  len, nid);
+                       mutex_unlock(&codec->hash_mutex);
+                       return -EINVAL;
+               }
+               if (conn_list && len)
+                       memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
        }
+       mutex_unlock(&codec->hash_mutex);
+       if (len >= 0)
+               return len;
        if (snd_BUG_ON(added))
                return -EINVAL;
 
-       /* read the connection and add to the cache */
-       len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
+       len = read_and_add_raw_conns(codec, nid);
        if (len < 0)
                return len;
-       err = snd_hda_override_conn_list(codec, nid, len, list);
-       if (err < 0)
-               return err;
        added = true;
        goto again;
 }
-EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
-
-/**
- * snd_hda_get_connections - copy connection list
- * @codec: the HDA codec
- * @nid: NID to parse
- * @conn_list: connection list array
- * @max_conns: max. number of connections to store
- *
- * Parses the connection list of the given widget and stores the list
- * of NIDs.
- *
- * Returns the number of connections, or a negative error code.
- */
-int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
-                            hda_nid_t *conn_list, int max_conns)
-{
-       const hda_nid_t *list;
-       int len = snd_hda_get_conn_list(codec, nid, &list);
-
-       if (len <= 0)
-               return len;
-       if (len > max_conns) {
-               snd_printk(KERN_ERR "hda_codec: "
-                          "Too many connections %d for NID 0x%x\n",
-                          len, nid);
-               return -EINVAL;
-       }
-       memcpy(conn_list, list, len * sizeof(hda_nid_t));
-       return len;
-}
 EXPORT_SYMBOL_HDA(snd_hda_get_connections);
 
 /**
@@ -543,6 +532,7 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
        hda_nid_t *p;
        int i, old_used;
 
+       mutex_lock(&codec->hash_mutex);
        p = lookup_conn_list(array, nid);
        if (p)
                *p = -1; /* invalidate the old entry */
@@ -553,10 +543,12 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
        for (i = 0; i < len; i++)
                if (!add_conn_list(array, list[i]))
                        goto error_add;
+       mutex_unlock(&codec->hash_mutex);
        return 0;
 
  error_add:
        array->used = old_used;
+       mutex_unlock(&codec->hash_mutex);
        return -ENOMEM;
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
@@ -1255,6 +1247,7 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        codec->addr = codec_addr;
        mutex_init(&codec->spdif_mutex);
        mutex_init(&codec->control_mutex);
+       mutex_init(&codec->hash_mutex);
        init_hda_cache(&codec->amp_cache, sizeof(struct hda_amp_info));
        init_hda_cache(&codec->cmd_cache, sizeof(struct hda_cache_head));
        snd_array_init(&codec->mixers, sizeof(struct hda_nid_item), 32);
@@ -1264,15 +1257,9 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        snd_array_init(&codec->cvt_setups, sizeof(struct hda_cvt_setup), 8);
        snd_array_init(&codec->conn_lists, sizeof(hda_nid_t), 64);
        snd_array_init(&codec->spdif_out, sizeof(struct hda_spdif_out), 16);
-       if (codec->bus->modelname) {
-               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
-               if (!codec->modelname) {
-                       snd_hda_codec_free(codec);
-                       return -ENODEV;
-               }
-       }
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
+       spin_lock_init(&codec->power_lock);
        INIT_DELAYED_WORK(&codec->power_work, hda_power_work);
        /* snd_hda_codec_new() marks the codec as power-up, and leave it as is.
         * the caller has to power down appropriatley after initialization
@@ -1281,6 +1268,14 @@ int /*__devinit*/ snd_hda_codec_new(struct hda_bus *bus,
        hda_keep_power_on(codec);
 #endif
 
+       if (codec->bus->modelname) {
+               codec->modelname = kstrdup(codec->bus->modelname, GFP_KERNEL);
+               if (!codec->modelname) {
+                       snd_hda_codec_free(codec);
+                       return -ENODEV;
+               }
+       }
+
        list_add_tail(&codec->list, &bus->codec_list);
        bus->caddr_tbl[codec_addr] = codec;
 
@@ -1603,6 +1598,60 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
        return (struct hda_amp_info *)get_alloc_hash(&codec->amp_cache, key);
 }
 
+/* overwrite the value with the key in the caps hash */
+static int write_caps_hash(struct hda_codec *codec, u32 key, unsigned int val)
+{
+       struct hda_amp_info *info;
+
+       mutex_lock(&codec->hash_mutex);
+       info = get_alloc_amp_hash(codec, key);
+       if (!info) {
+               mutex_unlock(&codec->hash_mutex);
+               return -EINVAL;
+       }
+       info->amp_caps = val;
+       info->head.val |= INFO_AMP_CAPS;
+       mutex_unlock(&codec->hash_mutex);
+       return 0;
+}
+
+/* query the value from the caps hash; if not found, fetch the current
+ * value from the given function and store in the hash
+ */
+static unsigned int
+query_caps_hash(struct hda_codec *codec, hda_nid_t nid, int dir, u32 key,
+               unsigned int (*func)(struct hda_codec *, hda_nid_t, int))
+{
+       struct hda_amp_info *info;
+       unsigned int val;
+
+       mutex_lock(&codec->hash_mutex);
+       info = get_alloc_amp_hash(codec, key);
+       if (!info) {
+               mutex_unlock(&codec->hash_mutex);
+               return 0;
+       }
+       if (!(info->head.val & INFO_AMP_CAPS)) {
+               mutex_unlock(&codec->hash_mutex); /* for reentrance */
+               val = func(codec, nid, dir);
+               write_caps_hash(codec, key, val);
+       } else {
+               val = info->amp_caps;
+               mutex_unlock(&codec->hash_mutex);
+       }
+       return val;
+}
+
+static unsigned int read_amp_cap(struct hda_codec *codec, hda_nid_t nid,
+                                int direction)
+{
+       if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
+               nid = codec->afg;
+       return snd_hda_param_read(codec, nid,
+                                 direction == HDA_OUTPUT ?
+                                 AC_PAR_AMP_OUT_CAP : AC_PAR_AMP_IN_CAP);
+}
+
 /**
  * query_amp_caps - query AMP capabilities
  * @codec: the HD-auio codec
@@ -1617,22 +1666,9 @@ get_alloc_amp_hash(struct hda_codec *codec, u32 key)
  */
 u32 query_amp_caps(struct hda_codec *codec, hda_nid_t nid, int direction)
 {
-       struct hda_amp_info *info;
-
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, 0));
-       if (!info)
-               return 0;
-       if (!(info->head.val & INFO_AMP_CAPS)) {
-               if (!(get_wcaps(codec, nid) & AC_WCAP_AMP_OVRD))
-                       nid = codec->afg;
-               info->amp_caps = snd_hda_param_read(codec, nid,
-                                                   direction == HDA_OUTPUT ?
-                                                   AC_PAR_AMP_OUT_CAP :
-                                                   AC_PAR_AMP_IN_CAP);
-               if (info->amp_caps)
-                       info->head.val |= INFO_AMP_CAPS;
-       }
-       return info->amp_caps;
+       return query_caps_hash(codec, nid, direction,
+                              HDA_HASH_KEY(nid, direction, 0),
+                              read_amp_cap);
 }
 EXPORT_SYMBOL_HDA(query_amp_caps);
 
@@ -1652,34 +1688,12 @@ EXPORT_SYMBOL_HDA(query_amp_caps);
 int snd_hda_override_amp_caps(struct hda_codec *codec, hda_nid_t nid, int dir,
                              unsigned int caps)
 {
-       struct hda_amp_info *info;
-
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, dir, 0));
-       if (!info)
-               return -EINVAL;
-       info->amp_caps = caps;
-       info->head.val |= INFO_AMP_CAPS;
-       return 0;
+       return write_caps_hash(codec, HDA_HASH_KEY(nid, dir, 0), caps);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_amp_caps);
 
-static unsigned int
-query_caps_hash(struct hda_codec *codec, hda_nid_t nid, u32 key,
-               unsigned int (*func)(struct hda_codec *, hda_nid_t))
-{
-       struct hda_amp_info *info;
-
-       info = get_alloc_amp_hash(codec, key);
-       if (!info)
-               return 0;
-       if (!info->head.val) {
-               info->head.val |= INFO_AMP_CAPS;
-               info->amp_caps = func(codec, nid);
-       }
-       return info->amp_caps;
-}
-
-static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid,
+                                int dir)
 {
        return snd_hda_param_read(codec, nid, AC_PAR_PIN_CAP);
 }
@@ -1697,7 +1711,7 @@ static unsigned int read_pin_cap(struct hda_codec *codec, hda_nid_t nid)
  */
 u32 snd_hda_query_pin_caps(struct hda_codec *codec, hda_nid_t nid)
 {
-       return query_caps_hash(codec, nid, HDA_HASH_PINCAP_KEY(nid),
+       return query_caps_hash(codec, nid, 0, HDA_HASH_PINCAP_KEY(nid),
                               read_pin_cap);
 }
 EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
@@ -1715,41 +1729,47 @@ EXPORT_SYMBOL_HDA(snd_hda_query_pin_caps);
 int snd_hda_override_pin_caps(struct hda_codec *codec, hda_nid_t nid,
                              unsigned int caps)
 {
-       struct hda_amp_info *info;
-       info = get_alloc_amp_hash(codec, HDA_HASH_PINCAP_KEY(nid));
-       if (!info)
-               return -ENOMEM;
-       info->amp_caps = caps;
-       info->head.val |= INFO_AMP_CAPS;
-       return 0;
+       return write_caps_hash(codec, HDA_HASH_PINCAP_KEY(nid), caps);
 }
 EXPORT_SYMBOL_HDA(snd_hda_override_pin_caps);
 
-/*
- * read the current volume to info
- * if the cache exists, read the cache value.
+/* read or sync the hash value with the current value;
+ * call within hash_mutex
  */
-static unsigned int get_vol_mute(struct hda_codec *codec,
-                                struct hda_amp_info *info, hda_nid_t nid,
-                                int ch, int direction, int index)
+static struct hda_amp_info *
+update_amp_hash(struct hda_codec *codec, hda_nid_t nid, int ch,
+               int direction, int index)
 {
-       u32 val, parm;
-
-       if (info->head.val & INFO_AMP_VOL(ch))
-               return info->vol[ch];
+       struct hda_amp_info *info;
+       unsigned int parm, val = 0;
+       bool val_read = false;
 
-       parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
-       parm |= direction == HDA_OUTPUT ? AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
-       parm |= index;
-       val = snd_hda_codec_read(codec, nid, 0,
+ retry:
+       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
+       if (!info)
+               return NULL;
+       if (!(info->head.val & INFO_AMP_VOL(ch))) {
+               if (!val_read) {
+                       mutex_unlock(&codec->hash_mutex);
+                       parm = ch ? AC_AMP_GET_RIGHT : AC_AMP_GET_LEFT;
+                       parm |= direction == HDA_OUTPUT ?
+                               AC_AMP_GET_OUTPUT : AC_AMP_GET_INPUT;
+                       parm |= index;
+                       val = snd_hda_codec_read(codec, nid, 0,
                                 AC_VERB_GET_AMP_GAIN_MUTE, parm);
-       info->vol[ch] = val & 0xff;
-       info->head.val |= INFO_AMP_VOL(ch);
-       return info->vol[ch];
+                       val &= 0xff;
+                       val_read = true;
+                       mutex_lock(&codec->hash_mutex);
+                       goto retry;
+               }
+               info->vol[ch] = val;
+               info->head.val |= INFO_AMP_VOL(ch);
+       }
+       return info;
 }
 
 /*
- * write the current volume in info to the h/w and update the cache
+ * write the current volume in info to the h/w
  */
 static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
                         hda_nid_t nid, int ch, int direction, int index,
@@ -1766,7 +1786,6 @@ static void put_vol_mute(struct hda_codec *codec, struct hda_amp_info *info,
        else
                parm |= val;
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, parm);
-       info->vol[ch] = val;
 }
 
 /**
@@ -1783,10 +1802,14 @@ int snd_hda_codec_amp_read(struct hda_codec *codec, hda_nid_t nid, int ch,
                           int direction, int index)
 {
        struct hda_amp_info *info;
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, index));
-       if (!info)
-               return 0;
-       return get_vol_mute(codec, info, nid, ch, direction, index);
+       unsigned int val = 0;
+
+       mutex_lock(&codec->hash_mutex);
+       info = update_amp_hash(codec, nid, ch, direction, index);
+       if (info)
+               val = info->vol[ch];
+       mutex_unlock(&codec->hash_mutex);
+       return val;
 }
 EXPORT_SYMBOL_HDA(snd_hda_codec_amp_read);
 
@@ -1808,15 +1831,23 @@ int snd_hda_codec_amp_update(struct hda_codec *codec, hda_nid_t nid, int ch,
 {
        struct hda_amp_info *info;
 
-       info = get_alloc_amp_hash(codec, HDA_HASH_KEY(nid, direction, idx));
-       if (!info)
-               return 0;
        if (snd_BUG_ON(mask & ~0xff))
                mask &= 0xff;
        val &= mask;
-       val |= get_vol_mute(codec, info, nid, ch, direction, idx) & ~mask;
-       if (info->vol[ch] == val)
+
+       mutex_lock(&codec->hash_mutex);
+       info = update_amp_hash(codec, nid, ch, direction, idx);
+       if (!info) {
+               mutex_unlock(&codec->hash_mutex);
+               return 0;
+       }
+       val |= info->vol[ch] & ~mask;
+       if (info->vol[ch] == val) {
+               mutex_unlock(&codec->hash_mutex);
                return 0;
+       }
+       info->vol[ch] = val;
+       mutex_unlock(&codec->hash_mutex);
        put_vol_mute(codec, info, nid, ch, direction, idx, val);
        return 1;
 }
@@ -2263,7 +2294,10 @@ int snd_hda_codec_reset(struct hda_codec *codec)
        /* OK, let it free */
 
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-       cancel_delayed_work(&codec->power_work);
+       cancel_delayed_work_sync(&codec->power_work);
+       codec->power_on = 0;
+       codec->power_transition = 0;
+       codec->power_jiffies = jiffies;
        flush_workqueue(codec->bus->workq);
 #endif
        snd_hda_ctls_clear(codec);
@@ -2859,12 +2893,15 @@ static int snd_hda_spdif_default_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
 
+       mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        ucontrol->value.iec958.status[0] = spdif->status & 0xff;
        ucontrol->value.iec958.status[1] = (spdif->status >> 8) & 0xff;
        ucontrol->value.iec958.status[2] = (spdif->status >> 16) & 0xff;
        ucontrol->value.iec958.status[3] = (spdif->status >> 24) & 0xff;
+       mutex_unlock(&codec->spdif_mutex);
 
        return 0;
 }
@@ -2950,12 +2987,14 @@ static int snd_hda_spdif_default_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
-       hda_nid_t nid = spdif->nid;
+       struct hda_spdif_out *spdif;
+       hda_nid_t nid;
        unsigned short val;
        int change;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
+       nid = spdif->nid;
        spdif->status = ucontrol->value.iec958.status[0] |
                ((unsigned int)ucontrol->value.iec958.status[1] << 8) |
                ((unsigned int)ucontrol->value.iec958.status[2] << 16) |
@@ -2977,9 +3016,12 @@ static int snd_hda_spdif_out_switch_get(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
 
+       mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        ucontrol->value.integer.value[0] = spdif->ctls & AC_DIG1_ENABLE;
+       mutex_unlock(&codec->spdif_mutex);
        return 0;
 }
 
@@ -2999,12 +3041,14 @@ static int snd_hda_spdif_out_switch_put(struct snd_kcontrol *kcontrol,
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        int idx = kcontrol->private_value;
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
-       hda_nid_t nid = spdif->nid;
+       struct hda_spdif_out *spdif;
+       hda_nid_t nid;
        unsigned short val;
        int change;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
+       nid = spdif->nid;
        val = spdif->ctls & ~AC_DIG1_ENABLE;
        if (ucontrol->value.integer.value[0])
                val |= AC_DIG1_ENABLE;
@@ -3092,6 +3136,9 @@ int snd_hda_create_spdif_out_ctls(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_create_spdif_out_ctls);
 
+/* get the hda_spdif_out entry from the given NID
+ * call within spdif_mutex lock
+ */
 struct hda_spdif_out *snd_hda_spdif_out_of_nid(struct hda_codec *codec,
                                               hda_nid_t nid)
 {
@@ -3108,9 +3155,10 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_out_of_nid);
 
 void snd_hda_spdif_ctls_unassign(struct hda_codec *codec, int idx)
 {
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        spdif->nid = (u16)-1;
        mutex_unlock(&codec->spdif_mutex);
 }
@@ -3118,10 +3166,11 @@ EXPORT_SYMBOL_HDA(snd_hda_spdif_ctls_unassign);
 
 void snd_hda_spdif_ctls_assign(struct hda_codec *codec, int idx, hda_nid_t nid)
 {
-       struct hda_spdif_out *spdif = snd_array_elem(&codec->spdif_out, idx);
+       struct hda_spdif_out *spdif;
        unsigned short val;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_array_elem(&codec->spdif_out, idx);
        if (spdif->nid != nid) {
                spdif->nid = nid;
                val = spdif->ctls;
@@ -3486,11 +3535,14 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D3);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
-       snd_hda_update_power_acct(codec);
        cancel_delayed_work(&codec->power_work);
+       spin_lock(&codec->power_lock);
+       snd_hda_update_power_acct(codec);
+       trace_hda_power_down(codec);
        codec->power_on = 0;
        codec->power_transition = 0;
        codec->power_jiffies = jiffies;
+       spin_unlock(&codec->power_lock);
 #endif
 }
 
@@ -3499,6 +3551,10 @@ static void hda_call_codec_suspend(struct hda_codec *codec)
  */
 static void hda_call_codec_resume(struct hda_codec *codec)
 {
+       /* set as if powered on for avoiding re-entering the resume
+        * in the resume / power-save sequence
+        */
+       hda_keep_power_on(codec);
        hda_set_power_state(codec,
                            codec->afg ? codec->afg : codec->mfg,
                            AC_PWRST_D0);
@@ -3514,6 +3570,7 @@ static void hda_call_codec_resume(struct hda_codec *codec)
                snd_hda_codec_resume_amp(codec);
                snd_hda_codec_resume_cache(codec);
        }
+       snd_hda_power_down(codec); /* flag down before returning */
 }
 #endif /* CONFIG_PM */
 
@@ -3665,7 +3722,8 @@ unsigned int snd_hda_calc_stream_format(unsigned int rate,
 }
 EXPORT_SYMBOL_HDA(snd_hda_calc_stream_format);
 
-static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid,
+                                 int dir)
 {
        unsigned int val = 0;
        if (nid != codec->afg &&
@@ -3680,11 +3738,12 @@ static unsigned int get_pcm_param(struct hda_codec *codec, hda_nid_t nid)
 
 static unsigned int query_pcm_param(struct hda_codec *codec, hda_nid_t nid)
 {
-       return query_caps_hash(codec, nid, HDA_HASH_PARPCM_KEY(nid),
+       return query_caps_hash(codec, nid, 0, HDA_HASH_PARPCM_KEY(nid),
                               get_pcm_param);
 }
 
-static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
+static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid,
+                                    int dir)
 {
        unsigned int streams = snd_hda_param_read(codec, nid, AC_PAR_STREAM);
        if (!streams || streams == -1)
@@ -3696,7 +3755,7 @@ static unsigned int get_stream_param(struct hda_codec *codec, hda_nid_t nid)
 
 static unsigned int query_stream_param(struct hda_codec *codec, hda_nid_t nid)
 {
-       return query_caps_hash(codec, nid, HDA_HASH_PARSTR_KEY(nid),
+       return query_caps_hash(codec, nid, 0, HDA_HASH_PARSTR_KEY(nid),
                               get_stream_param);
 }
 
@@ -3775,11 +3834,13 @@ int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                        bps = 20;
                        }
                }
+#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */
                if (streams & AC_SUPFMT_FLOAT32) {
                        formats |= SNDRV_PCM_FMTBIT_FLOAT_LE;
                        if (!bps)
                                bps = 32;
                }
+#endif
                if (streams == AC_SUPFMT_AC3) {
                        /* should be exclusive */
                        /* temporary hack: we have still no proper support
@@ -4283,12 +4344,18 @@ static void hda_power_work(struct work_struct *work)
                container_of(work, struct hda_codec, power_work.work);
        struct hda_bus *bus = codec->bus;
 
+       spin_lock(&codec->power_lock);
+       if (codec->power_transition > 0) { /* during power-up sequence? */
+               spin_unlock(&codec->power_lock);
+               return;
+       }
        if (!codec->power_on || codec->power_count) {
                codec->power_transition = 0;
+               spin_unlock(&codec->power_lock);
                return;
        }
+       spin_unlock(&codec->power_lock);
 
-       trace_hda_power_down(codec);
        hda_call_codec_suspend(codec);
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
@@ -4296,9 +4363,11 @@ static void hda_power_work(struct work_struct *work)
 
 static void hda_keep_power_on(struct hda_codec *codec)
 {
+       spin_lock(&codec->power_lock);
        codec->power_count++;
        codec->power_on = 1;
        codec->power_jiffies = jiffies;
+       spin_unlock(&codec->power_lock);
 }
 
 /* update the power on/off account with the current jiffies */
@@ -4323,19 +4392,31 @@ void snd_hda_power_up(struct hda_codec *codec)
 {
        struct hda_bus *bus = codec->bus;
 
+       spin_lock(&codec->power_lock);
        codec->power_count++;
-       if (codec->power_on || codec->power_transition)
+       if (codec->power_on || codec->power_transition > 0) {
+               spin_unlock(&codec->power_lock);
                return;
+       }
+       spin_unlock(&codec->power_lock);
 
+       cancel_delayed_work_sync(&codec->power_work);
+
+       spin_lock(&codec->power_lock);
        trace_hda_power_up(codec);
        snd_hda_update_power_acct(codec);
        codec->power_on = 1;
        codec->power_jiffies = jiffies;
+       codec->power_transition = 1; /* avoid reentrance */
+       spin_unlock(&codec->power_lock);
+
        if (bus->ops.pm_notify)
                bus->ops.pm_notify(bus);
        hda_call_codec_resume(codec);
-       cancel_delayed_work(&codec->power_work);
+
+       spin_lock(&codec->power_lock);
        codec->power_transition = 0;
+       spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_up);
 
@@ -4351,14 +4432,18 @@ EXPORT_SYMBOL_HDA(snd_hda_power_up);
  */
 void snd_hda_power_down(struct hda_codec *codec)
 {
+       spin_lock(&codec->power_lock);
        --codec->power_count;
-       if (!codec->power_on || codec->power_count || codec->power_transition)
+       if (!codec->power_on || codec->power_count || codec->power_transition) {
+               spin_unlock(&codec->power_lock);
                return;
+       }
        if (power_save(codec)) {
-               codec->power_transition = 1; /* avoid reentrance */
+               codec->power_transition = -1; /* avoid reentrance */
                queue_delayed_work(codec->bus->workq, &codec->power_work,
                                msecs_to_jiffies(power_save(codec) * 1000));
        }
+       spin_unlock(&codec->power_lock);
 }
 EXPORT_SYMBOL_HDA(snd_hda_power_down);
 
@@ -4710,11 +4795,11 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
 {
        const hda_nid_t *nids = mout->dac_nids;
        int chs = substream->runtime->channels;
-       struct hda_spdif_out *spdif =
-                       snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
+       struct hda_spdif_out *spdif;
        int i;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
        if (mout->dig_out_nid && mout->share_spdif &&
            mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
                if (chs == 2 &&
@@ -4795,601 +4880,58 @@ int snd_hda_multi_out_analog_cleanup(struct hda_codec *codec,
 }
 EXPORT_SYMBOL_HDA(snd_hda_multi_out_analog_cleanup);
 
-/*
- * Helper for automatic pin configuration
- */
-
-static int is_in_nid_list(hda_nid_t nid, const hda_nid_t *list)
-{
-       for (; *list; list++)
-               if (*list == nid)
-                       return 1;
-       return 0;
-}
-
-
-/*
- * Sort an associated group of pins according to their sequence numbers.
- */
-static void sort_pins_by_sequence(hda_nid_t *pins, short *sequences,
-                                 int num_pins)
-{
-       int i, j;
-       short seq;
-       hda_nid_t nid;
-
-       for (i = 0; i < num_pins; i++) {
-               for (j = i + 1; j < num_pins; j++) {
-                       if (sequences[i] > sequences[j]) {
-                               seq = sequences[i];
-                               sequences[i] = sequences[j];
-                               sequences[j] = seq;
-                               nid = pins[i];
-                               pins[i] = pins[j];
-                               pins[j] = nid;
-                       }
-               }
-       }
-}
-
-
-/* add the found input-pin to the cfg->inputs[] table */
-static void add_auto_cfg_input_pin(struct auto_pin_cfg *cfg, hda_nid_t nid,
-                                  int type)
-{
-       if (cfg->num_inputs < AUTO_CFG_MAX_INS) {
-               cfg->inputs[cfg->num_inputs].pin = nid;
-               cfg->inputs[cfg->num_inputs].type = type;
-               cfg->num_inputs++;
-       }
-}
-
-/* sort inputs in the order of AUTO_PIN_* type */
-static void sort_autocfg_input_pins(struct auto_pin_cfg *cfg)
-{
-       int i, j;
-
-       for (i = 0; i < cfg->num_inputs; i++) {
-               for (j = i + 1; j < cfg->num_inputs; j++) {
-                       if (cfg->inputs[i].type > cfg->inputs[j].type) {
-                               struct auto_pin_cfg_item tmp;
-                               tmp = cfg->inputs[i];
-                               cfg->inputs[i] = cfg->inputs[j];
-                               cfg->inputs[j] = tmp;
-                       }
-               }
-       }
-}
-
-/* Reorder the surround channels
- * ALSA sequence is front/surr/clfe/side
- * HDA sequence is:
- *    4-ch: front/surr  =>  OK as it is
- *    6-ch: front/clfe/surr
- *    8-ch: front/clfe/rear/side|fc
- */
-static void reorder_outputs(unsigned int nums, hda_nid_t *pins)
-{
-       hda_nid_t nid;
-
-       switch (nums) {
-       case 3:
-       case 4:
-               nid = pins[1];
-               pins[1] = pins[2];
-               pins[2] = nid;
-               break;
-       }
-}
-
-/*
- * Parse all pin widgets and store the useful pin nids to cfg
- *
- * The number of line-outs or any primary output is stored in line_outs,
- * and the corresponding output pins are assigned to line_out_pins[],
- * in the order of front, rear, CLFE, side, ...
- *
- * If more extra outputs (speaker and headphone) are found, the pins are
- * assisnged to hp_pins[] and speaker_pins[], respectively.  If no line-out jack
- * is detected, one of speaker of HP pins is assigned as the primary
- * output, i.e. to line_out_pins[0].  So, line_outs is always positive
- * if any analog output exists.
- *
- * The analog input pins are assigned to inputs array.
- * The digital input/output pins are assigned to dig_in_pin and dig_out_pin,
- * respectively.
- */
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
-                            struct auto_pin_cfg *cfg,
-                            const hda_nid_t *ignore_nids,
-                            unsigned int cond_flags)
-{
-       hda_nid_t nid, end_nid;
-       short seq, assoc_line_out;
-       short sequences_line_out[ARRAY_SIZE(cfg->line_out_pins)];
-       short sequences_speaker[ARRAY_SIZE(cfg->speaker_pins)];
-       short sequences_hp[ARRAY_SIZE(cfg->hp_pins)];
-       int i;
-
-       memset(cfg, 0, sizeof(*cfg));
-
-       memset(sequences_line_out, 0, sizeof(sequences_line_out));
-       memset(sequences_speaker, 0, sizeof(sequences_speaker));
-       memset(sequences_hp, 0, sizeof(sequences_hp));
-       assoc_line_out = 0;
-
-       codec->ignore_misc_bit = true;
-       end_nid = codec->start_nid + codec->num_nodes;
-       for (nid = codec->start_nid; nid < end_nid; nid++) {
-               unsigned int wid_caps = get_wcaps(codec, nid);
-               unsigned int wid_type = get_wcaps_type(wid_caps);
-               unsigned int def_conf;
-               short assoc, loc, conn, dev;
-
-               /* read all default configuration for pin complex */
-               if (wid_type != AC_WID_PIN)
-                       continue;
-               /* ignore the given nids (e.g. pc-beep returns error) */
-               if (ignore_nids && is_in_nid_list(nid, ignore_nids))
-                       continue;
-
-               def_conf = snd_hda_codec_get_pincfg(codec, nid);
-               if (!(get_defcfg_misc(snd_hda_codec_get_pincfg(codec, nid)) &
-                     AC_DEFCFG_MISC_NO_PRESENCE))
-                       codec->ignore_misc_bit = false;
-               conn = get_defcfg_connect(def_conf);
-               if (conn == AC_JACK_PORT_NONE)
-                       continue;
-               loc = get_defcfg_location(def_conf);
-               dev = get_defcfg_device(def_conf);
-
-               /* workaround for buggy BIOS setups */
-               if (dev == AC_JACK_LINE_OUT) {
-                       if (conn == AC_JACK_PORT_FIXED)
-                               dev = AC_JACK_SPEAKER;
-               }
-
-               switch (dev) {
-               case AC_JACK_LINE_OUT:
-                       seq = get_defcfg_sequence(def_conf);
-                       assoc = get_defcfg_association(def_conf);
-
-                       if (!(wid_caps & AC_WCAP_STEREO))
-                               if (!cfg->mono_out_pin)
-                                       cfg->mono_out_pin = nid;
-                       if (!assoc)
-                               continue;
-                       if (!assoc_line_out)
-                               assoc_line_out = assoc;
-                       else if (assoc_line_out != assoc)
-                               continue;
-                       if (cfg->line_outs >= ARRAY_SIZE(cfg->line_out_pins))
-                               continue;
-                       cfg->line_out_pins[cfg->line_outs] = nid;
-                       sequences_line_out[cfg->line_outs] = seq;
-                       cfg->line_outs++;
-                       break;
-               case AC_JACK_SPEAKER:
-                       seq = get_defcfg_sequence(def_conf);
-                       assoc = get_defcfg_association(def_conf);
-                       if (cfg->speaker_outs >= ARRAY_SIZE(cfg->speaker_pins))
-                               continue;
-                       cfg->speaker_pins[cfg->speaker_outs] = nid;
-                       sequences_speaker[cfg->speaker_outs] = (assoc << 4) | seq;
-                       cfg->speaker_outs++;
-                       break;
-               case AC_JACK_HP_OUT:
-                       seq = get_defcfg_sequence(def_conf);
-                       assoc = get_defcfg_association(def_conf);
-                       if (cfg->hp_outs >= ARRAY_SIZE(cfg->hp_pins))
-                               continue;
-                       cfg->hp_pins[cfg->hp_outs] = nid;
-                       sequences_hp[cfg->hp_outs] = (assoc << 4) | seq;
-                       cfg->hp_outs++;
-                       break;
-               case AC_JACK_MIC_IN:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_MIC);
-                       break;
-               case AC_JACK_LINE_IN:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_LINE_IN);
-                       break;
-               case AC_JACK_CD:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_CD);
-                       break;
-               case AC_JACK_AUX:
-                       add_auto_cfg_input_pin(cfg, nid, AUTO_PIN_AUX);
-                       break;
-               case AC_JACK_SPDIF_OUT:
-               case AC_JACK_DIG_OTHER_OUT:
-                       if (cfg->dig_outs >= ARRAY_SIZE(cfg->dig_out_pins))
-                               continue;
-                       cfg->dig_out_pins[cfg->dig_outs] = nid;
-                       cfg->dig_out_type[cfg->dig_outs] =
-                               (loc == AC_JACK_LOC_HDMI) ?
-                               HDA_PCM_TYPE_HDMI : HDA_PCM_TYPE_SPDIF;
-                       cfg->dig_outs++;
-                       break;
-               case AC_JACK_SPDIF_IN:
-               case AC_JACK_DIG_OTHER_IN:
-                       cfg->dig_in_pin = nid;
-                       if (loc == AC_JACK_LOC_HDMI)
-                               cfg->dig_in_type = HDA_PCM_TYPE_HDMI;
-                       else
-                               cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
-                       break;
-               }
-       }
-
-       /* FIX-UP:
-        * If no line-out is defined but multiple HPs are found,
-        * some of them might be the real line-outs.
-        */
-       if (!cfg->line_outs && cfg->hp_outs > 1 &&
-           !(cond_flags & HDA_PINCFG_NO_HP_FIXUP)) {
-               int i = 0;
-               while (i < cfg->hp_outs) {
-                       /* The real HPs should have the sequence 0x0f */
-                       if ((sequences_hp[i] & 0x0f) == 0x0f) {
-                               i++;
-                               continue;
-                       }
-                       /* Move it to the line-out table */
-                       cfg->line_out_pins[cfg->line_outs] = cfg->hp_pins[i];
-                       sequences_line_out[cfg->line_outs] = sequences_hp[i];
-                       cfg->line_outs++;
-                       cfg->hp_outs--;
-                       memmove(cfg->hp_pins + i, cfg->hp_pins + i + 1,
-                               sizeof(cfg->hp_pins[0]) * (cfg->hp_outs - i));
-                       memmove(sequences_hp + i, sequences_hp + i + 1,
-                               sizeof(sequences_hp[0]) * (cfg->hp_outs - i));
-               }
-               memset(cfg->hp_pins + cfg->hp_outs, 0,
-                      sizeof(hda_nid_t) * (AUTO_CFG_MAX_OUTS - cfg->hp_outs));
-               if (!cfg->hp_outs)
-                       cfg->line_out_type = AUTO_PIN_HP_OUT;
-
-       }
-
-       /* sort by sequence */
-       sort_pins_by_sequence(cfg->line_out_pins, sequences_line_out,
-                             cfg->line_outs);
-       sort_pins_by_sequence(cfg->speaker_pins, sequences_speaker,
-                             cfg->speaker_outs);
-       sort_pins_by_sequence(cfg->hp_pins, sequences_hp,
-                             cfg->hp_outs);
-
-       /*
-        * FIX-UP: if no line-outs are detected, try to use speaker or HP pin
-        * as a primary output
-        */
-       if (!cfg->line_outs &&
-           !(cond_flags & HDA_PINCFG_NO_LO_FIXUP)) {
-               if (cfg->speaker_outs) {
-                       cfg->line_outs = cfg->speaker_outs;
-                       memcpy(cfg->line_out_pins, cfg->speaker_pins,
-                              sizeof(cfg->speaker_pins));
-                       cfg->speaker_outs = 0;
-                       memset(cfg->speaker_pins, 0, sizeof(cfg->speaker_pins));
-                       cfg->line_out_type = AUTO_PIN_SPEAKER_OUT;
-               } else if (cfg->hp_outs) {
-                       cfg->line_outs = cfg->hp_outs;
-                       memcpy(cfg->line_out_pins, cfg->hp_pins,
-                              sizeof(cfg->hp_pins));
-                       cfg->hp_outs = 0;
-                       memset(cfg->hp_pins, 0, sizeof(cfg->hp_pins));
-                       cfg->line_out_type = AUTO_PIN_HP_OUT;
-               }
-       }
-
-       reorder_outputs(cfg->line_outs, cfg->line_out_pins);
-       reorder_outputs(cfg->hp_outs, cfg->hp_pins);
-       reorder_outputs(cfg->speaker_outs, cfg->speaker_pins);
-
-       sort_autocfg_input_pins(cfg);
-
-       /*
-        * debug prints of the parsed results
-        */
-       snd_printd("autoconfig: line_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x) type:%s\n",
-                  cfg->line_outs, cfg->line_out_pins[0], cfg->line_out_pins[1],
-                  cfg->line_out_pins[2], cfg->line_out_pins[3],
-                  cfg->line_out_pins[4],
-                  cfg->line_out_type == AUTO_PIN_HP_OUT ? "hp" :
-                  (cfg->line_out_type == AUTO_PIN_SPEAKER_OUT ?
-                   "speaker" : "line"));
-       snd_printd("   speaker_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-                  cfg->speaker_outs, cfg->speaker_pins[0],
-                  cfg->speaker_pins[1], cfg->speaker_pins[2],
-                  cfg->speaker_pins[3], cfg->speaker_pins[4]);
-       snd_printd("   hp_outs=%d (0x%x/0x%x/0x%x/0x%x/0x%x)\n",
-                  cfg->hp_outs, cfg->hp_pins[0],
-                  cfg->hp_pins[1], cfg->hp_pins[2],
-                  cfg->hp_pins[3], cfg->hp_pins[4]);
-       snd_printd("   mono: mono_out=0x%x\n", cfg->mono_out_pin);
-       if (cfg->dig_outs)
-               snd_printd("   dig-out=0x%x/0x%x\n",
-                          cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
-       snd_printd("   inputs:");
-       for (i = 0; i < cfg->num_inputs; i++) {
-               snd_printd(" %s=0x%x",
-                           hda_get_autocfg_input_label(codec, cfg, i),
-                           cfg->inputs[i].pin);
-       }
-       snd_printd("\n");
-       if (cfg->dig_in_pin)
-               snd_printd("   dig-in=0x%x\n", cfg->dig_in_pin);
-
-       return 0;
-}
-EXPORT_SYMBOL_HDA(snd_hda_parse_pin_defcfg);
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf)
-{
-       unsigned int loc = get_defcfg_location(def_conf);
-       unsigned int conn = get_defcfg_connect(def_conf);
-       if (conn == AC_JACK_PORT_NONE)
-               return INPUT_PIN_ATTR_UNUSED;
-       /* Windows may claim the internal mic to be BOTH, too */
-       if (conn == AC_JACK_PORT_FIXED || conn == AC_JACK_PORT_BOTH)
-               return INPUT_PIN_ATTR_INT;
-       if ((loc & 0x30) == AC_JACK_LOC_INTERNAL)
-               return INPUT_PIN_ATTR_INT;
-       if ((loc & 0x30) == AC_JACK_LOC_SEPARATE)
-               return INPUT_PIN_ATTR_DOCK;
-       if (loc == AC_JACK_LOC_REAR)
-               return INPUT_PIN_ATTR_REAR;
-       if (loc == AC_JACK_LOC_FRONT)
-               return INPUT_PIN_ATTR_FRONT;
-       return INPUT_PIN_ATTR_NORMAL;
-}
-EXPORT_SYMBOL_HDA(snd_hda_get_input_pin_attr);
-
-/**
- * hda_get_input_pin_label - Give a label for the given input pin
- *
- * When check_location is true, the function checks the pin location
- * for mic and line-in pins, and set an appropriate prefix like "Front",
- * "Rear", "Internal".
- */
-
-static const char *hda_get_input_pin_label(struct hda_codec *codec,
-                                          hda_nid_t pin, bool check_location)
-{
-       unsigned int def_conf;
-       static const char * const mic_names[] = {
-               "Internal Mic", "Dock Mic", "Mic", "Front Mic", "Rear Mic",
-       };
-       int attr;
-
-       def_conf = snd_hda_codec_get_pincfg(codec, pin);
-
-       switch (get_defcfg_device(def_conf)) {
-       case AC_JACK_MIC_IN:
-               if (!check_location)
-                       return "Mic";
-               attr = snd_hda_get_input_pin_attr(def_conf);
-               if (!attr)
-                       return "None";
-               return mic_names[attr - 1];
-       case AC_JACK_LINE_IN:
-               if (!check_location)
-                       return "Line";
-               attr = snd_hda_get_input_pin_attr(def_conf);
-               if (!attr)
-                       return "None";
-               if (attr == INPUT_PIN_ATTR_DOCK)
-                       return "Dock Line";
-               return "Line";
-       case AC_JACK_AUX:
-               return "Aux";
-       case AC_JACK_CD:
-               return "CD";
-       case AC_JACK_SPDIF_IN:
-               return "SPDIF In";
-       case AC_JACK_DIG_OTHER_IN:
-               return "Digital In";
-       default:
-               return "Misc";
-       }
-}
-
-/* Check whether the location prefix needs to be added to the label.
- * If all mic-jacks are in the same location (e.g. rear panel), we don't
- * have to put "Front" prefix to each label.  In such a case, returns false.
- */
-static int check_mic_location_need(struct hda_codec *codec,
-                                  const struct auto_pin_cfg *cfg,
-                                  int input)
-{
-       unsigned int defc;
-       int i, attr, attr2;
-
-       defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[input].pin);
-       attr = snd_hda_get_input_pin_attr(defc);
-       /* for internal or docking mics, we need locations */
-       if (attr <= INPUT_PIN_ATTR_NORMAL)
-               return 1;
-
-       attr = 0;
-       for (i = 0; i < cfg->num_inputs; i++) {
-               defc = snd_hda_codec_get_pincfg(codec, cfg->inputs[i].pin);
-               attr2 = snd_hda_get_input_pin_attr(defc);
-               if (attr2 >= INPUT_PIN_ATTR_NORMAL) {
-                       if (attr && attr != attr2)
-                               return 1; /* different locations found */
-                       attr = attr2;
-               }
-       }
-       return 0;
-}
-
 /**
- * hda_get_autocfg_input_label - Get a label for the given input
+ * snd_hda_get_default_vref - Get the default (mic) VREF pin bits
  *
- * Get a label for the given input pin defined by the autocfg item.
- * Unlike hda_get_input_pin_label(), this function checks all inputs
- * defined in autocfg and avoids the redundant mic/line prefix as much as
- * possible.
- */
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
-                                       const struct auto_pin_cfg *cfg,
-                                       int input)
-{
-       int type = cfg->inputs[input].type;
-       int has_multiple_pins = 0;
-
-       if ((input > 0 && cfg->inputs[input - 1].type == type) ||
-           (input < cfg->num_inputs - 1 && cfg->inputs[input + 1].type == type))
-               has_multiple_pins = 1;
-       if (has_multiple_pins && type == AUTO_PIN_MIC)
-               has_multiple_pins &= check_mic_location_need(codec, cfg, input);
-       return hda_get_input_pin_label(codec, cfg->inputs[input].pin,
-                                      has_multiple_pins);
-}
-EXPORT_SYMBOL_HDA(hda_get_autocfg_input_label);
-
-/* return the position of NID in the list, or -1 if not found */
-static int find_idx_in_nid_list(hda_nid_t nid, const hda_nid_t *list, int nums)
-{
-       int i;
-       for (i = 0; i < nums; i++)
-               if (list[i] == nid)
-                       return i;
-       return -1;
-}
-
-/* get a unique suffix or an index number */
-static const char *check_output_sfx(hda_nid_t nid, const hda_nid_t *pins,
-                                   int num_pins, int *indexp)
-{
-       static const char * const channel_sfx[] = {
-               " Front", " Surround", " CLFE", " Side"
-       };
-       int i;
-
-       i = find_idx_in_nid_list(nid, pins, num_pins);
-       if (i < 0)
-               return NULL;
-       if (num_pins == 1)
-               return "";
-       if (num_pins > ARRAY_SIZE(channel_sfx)) {
-               if (indexp)
-                       *indexp = i;
-               return "";
-       }
-       return channel_sfx[i];
-}
-
-static int fill_audio_out_name(struct hda_codec *codec, hda_nid_t nid,
-                              const struct auto_pin_cfg *cfg,
-                              const char *name, char *label, int maxlen,
-                              int *indexp)
-{
-       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       int attr = snd_hda_get_input_pin_attr(def_conf);
-       const char *pfx = "", *sfx = "";
-
-       /* handle as a speaker if it's a fixed line-out */
-       if (!strcmp(name, "Line Out") && attr == INPUT_PIN_ATTR_INT)
-               name = "Speaker";
-       /* check the location */
-       switch (attr) {
-       case INPUT_PIN_ATTR_DOCK:
-               pfx = "Dock ";
-               break;
-       case INPUT_PIN_ATTR_FRONT:
-               pfx = "Front ";
-               break;
-       }
-       if (cfg) {
-               /* try to give a unique suffix if needed */
-               sfx = check_output_sfx(nid, cfg->line_out_pins, cfg->line_outs,
-                                      indexp);
-               if (!sfx)
-                       sfx = check_output_sfx(nid, cfg->speaker_pins, cfg->speaker_outs,
-                                              indexp);
-               if (!sfx) {
-                       /* don't add channel suffix for Headphone controls */
-                       int idx = find_idx_in_nid_list(nid, cfg->hp_pins,
-                                                      cfg->hp_outs);
-                       if (idx >= 0)
-                               *indexp = idx;
-                       sfx = "";
+ * Guess the suitable VREF pin bits to be set as the pin-control value.
+ * Note: the function doesn't set the AC_PINCTL_IN_EN bit.
+ */
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin)
+{
+       unsigned int pincap;
+       unsigned int oldval;
+       oldval = snd_hda_codec_read(codec, pin, 0,
+                                   AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
+       pincap = snd_hda_query_pin_caps(codec, pin);
+       pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
+       /* Exception: if the default pin setup is vref50, we give it priority */
+       if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
+               return AC_PINCTL_VREF_80;
+       else if (pincap & AC_PINCAP_VREF_50)
+               return AC_PINCTL_VREF_50;
+       else if (pincap & AC_PINCAP_VREF_100)
+               return AC_PINCTL_VREF_100;
+       else if (pincap & AC_PINCAP_VREF_GRD)
+               return AC_PINCTL_VREF_GRD;
+       return AC_PINCTL_VREF_HIZ;
+}
+EXPORT_SYMBOL_HDA(snd_hda_get_default_vref);
+
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int val, bool cached)
+{
+       if (val) {
+               unsigned int cap = snd_hda_query_pin_caps(codec, pin);
+               if (cap && (val & AC_PINCTL_OUT_EN)) {
+                       if (!(cap & AC_PINCAP_OUT))
+                               val &= ~(AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN);
+                       else if ((val & AC_PINCTL_HP_EN) &&
+                                !(cap & AC_PINCAP_HP_DRV))
+                               val &= ~AC_PINCTL_HP_EN;
                }
-       }
-       snprintf(label, maxlen, "%s%s%s", pfx, name, sfx);
-       return 1;
-}
-
-/**
- * snd_hda_get_pin_label - Get a label for the given I/O pin
- *
- * Get a label for the given pin.  This function works for both input and
- * output pins.  When @cfg is given as non-NULL, the function tries to get
- * an optimized label using hda_get_autocfg_input_label().
- *
- * This function tries to give a unique label string for the pin as much as
- * possible.  For example, when the multiple line-outs are present, it adds
- * the channel suffix like "Front", "Surround", etc (only when @cfg is given).
- * If no unique name with a suffix is available and @indexp is non-NULL, the
- * index number is stored in the pointer.
- */
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
-                         const struct auto_pin_cfg *cfg,
-                         char *label, int maxlen, int *indexp)
-{
-       unsigned int def_conf = snd_hda_codec_get_pincfg(codec, nid);
-       const char *name = NULL;
-       int i;
-
-       if (indexp)
-               *indexp = 0;
-       if (get_defcfg_connect(def_conf) == AC_JACK_PORT_NONE)
-               return 0;
-
-       switch (get_defcfg_device(def_conf)) {
-       case AC_JACK_LINE_OUT:
-               return fill_audio_out_name(codec, nid, cfg, "Line Out",
-                                          label, maxlen, indexp);
-       case AC_JACK_SPEAKER:
-               return fill_audio_out_name(codec, nid, cfg, "Speaker",
-                                          label, maxlen, indexp);
-       case AC_JACK_HP_OUT:
-               return fill_audio_out_name(codec, nid, cfg, "Headphone",
-                                          label, maxlen, indexp);
-       case AC_JACK_SPDIF_OUT:
-       case AC_JACK_DIG_OTHER_OUT:
-               if (get_defcfg_location(def_conf) == AC_JACK_LOC_HDMI)
-                       name = "HDMI";
-               else
-                       name = "SPDIF";
-               if (cfg && indexp) {
-                       i = find_idx_in_nid_list(nid, cfg->dig_out_pins,
-                                                cfg->dig_outs);
-                       if (i >= 0)
-                               *indexp = i;
-               }
-               break;
-       default:
-               if (cfg) {
-                       for (i = 0; i < cfg->num_inputs; i++) {
-                               if (cfg->inputs[i].pin != nid)
-                                       continue;
-                               name = hda_get_autocfg_input_label(codec, cfg, i);
-                               if (name)
-                                       break;
-                       }
+               if (cap && (val & AC_PINCTL_IN_EN)) {
+                       if (!(cap & AC_PINCAP_IN))
+                               val &= ~(AC_PINCTL_IN_EN | AC_PINCTL_VREFEN);
                }
-               if (!name)
-                       name = hda_get_input_pin_label(codec, nid, true);
-               break;
        }
-       if (!name)
-               return 0;
-       strlcpy(label, name, maxlen);
-       return 1;
+       if (cached)
+               return snd_hda_codec_update_cache(codec, pin, 0,
+                               AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       else
+               return snd_hda_codec_write(codec, pin, 0,
+                                          AC_VERB_SET_PIN_WIDGET_CONTROL, val);
 }
-EXPORT_SYMBOL_HDA(snd_hda_get_pin_label);
+EXPORT_SYMBOL_HDA(_snd_hda_set_pin_ctl);
 
 /**
  * snd_hda_add_imux_item - Add an item to input_mux
@@ -5444,8 +4986,6 @@ int snd_hda_suspend(struct hda_bus *bus)
        list_for_each_entry(codec, &bus->codec_list, list) {
                if (hda_codec_is_power_on(codec))
                        hda_call_codec_suspend(codec);
-               if (codec->patch_ops.post_suspend)
-                       codec->patch_ops.post_suspend(codec);
        }
        return 0;
 }
@@ -5465,10 +5005,7 @@ int snd_hda_resume(struct hda_bus *bus)
        struct hda_codec *codec;
 
        list_for_each_entry(codec, &bus->codec_list, list) {
-               if (codec->patch_ops.pre_resume)
-                       codec->patch_ops.pre_resume(codec);
-               if (snd_hda_codec_needs_resume(codec))
-                       hda_call_codec_resume(codec);
+               hda_call_codec_resume(codec);
        }
        return 0;
 }
index 56b4f74c0b13a101e16e9433f76341ef7a5a7c0e..54b52819fb47acf5ef8b2342643a1ece7d9a8c57 100644 (file)
@@ -704,8 +704,6 @@ struct hda_codec_ops {
                                unsigned int power_state);
 #ifdef CONFIG_PM
        int (*suspend)(struct hda_codec *codec, pm_message_t state);
-       int (*post_suspend)(struct hda_codec *codec);
-       int (*pre_resume)(struct hda_codec *codec);
        int (*resume)(struct hda_codec *codec);
 #endif
 #ifdef CONFIG_SND_HDA_POWER_SAVE
@@ -829,6 +827,7 @@ struct hda_codec {
 
        struct mutex spdif_mutex;
        struct mutex control_mutex;
+       struct mutex hash_mutex;
        struct snd_array spdif_out;
        unsigned int spdif_in_enable;   /* SPDIF input enable? */
        const hda_nid_t *slave_dig_outs; /* optional digital out slave widgets */
@@ -861,12 +860,13 @@ struct hda_codec {
        unsigned int no_jack_detect:1;  /* Machine has no jack-detection */
 #ifdef CONFIG_SND_HDA_POWER_SAVE
        unsigned int power_on :1;       /* current (global) power-state */
-       unsigned int power_transition :1; /* power-state in transition */
+       int power_transition;   /* power-state in transition */
        int power_count;        /* current (global) power refcount */
        struct delayed_work power_work; /* delayed task for powerdown */
        unsigned long power_on_acct;
        unsigned long power_off_acct;
        unsigned long power_jiffies;
+       spinlock_t power_lock;
 #endif
 
        /* codec-specific additional proc output */
@@ -911,10 +911,13 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
                          hda_nid_t *start_id);
 int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns);
+static inline int
+snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_get_connections(codec, nid, NULL, 0);
+}
 int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
                            hda_nid_t *conn_list, int max_conns);
-int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
-                         const hda_nid_t **listp);
 int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
                          const hda_nid_t *list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
@@ -1051,12 +1054,10 @@ const char *snd_hda_get_jack_location(u32 cfg);
 #ifdef CONFIG_SND_HDA_POWER_SAVE
 void snd_hda_power_up(struct hda_codec *codec);
 void snd_hda_power_down(struct hda_codec *codec);
-#define snd_hda_codec_needs_resume(codec) codec->power_count
 void snd_hda_update_power_acct(struct hda_codec *codec);
 #else
 static inline void snd_hda_power_up(struct hda_codec *codec) {}
 static inline void snd_hda_power_down(struct hda_codec *codec) {}
-#define snd_hda_codec_needs_resume(codec) 1
 #endif
 
 #ifdef CONFIG_SND_HDA_PATCH_LOADER
index 1f350522bed436812b794c2544f39674ec4a868d..4ab8102f87ea63ccb83ccbb6c97df2d17fa13eb2 100644 (file)
@@ -497,6 +497,7 @@ enum {
        AZX_DRIVER_NVIDIA,
        AZX_DRIVER_TERA,
        AZX_DRIVER_CTX,
+       AZX_DRIVER_CTHDA,
        AZX_DRIVER_GENERIC,
        AZX_NUM_DRIVERS, /* keep this as last entry */
 };
@@ -518,6 +519,7 @@ enum {
 #define AZX_DCAPS_OLD_SSYNC    (1 << 20)       /* Old SSYNC reg for ICH */
 #define AZX_DCAPS_BUFSIZE      (1 << 21)       /* no buffer size alignment */
 #define AZX_DCAPS_ALIGN_BUFSIZE        (1 << 22)       /* buffer size alignment */
+#define AZX_DCAPS_4K_BDLE_BOUNDARY (1 << 23)   /* BDLE in 4k boundary */
 
 /* quirks for ATI SB / AMD Hudson */
 #define AZX_DCAPS_PRESET_ATI_SB \
@@ -533,6 +535,9 @@ enum {
        (AZX_DCAPS_NVIDIA_SNOOP | AZX_DCAPS_RIRB_DELAY | AZX_DCAPS_NO_MSI |\
         AZX_DCAPS_ALIGN_BUFSIZE)
 
+#define AZX_DCAPS_PRESET_CTHDA \
+       (AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_4K_BDLE_BOUNDARY)
+
 static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_ICH] = "HDA Intel",
        [AZX_DRIVER_PCH] = "HDA Intel PCH",
@@ -546,6 +551,7 @@ static char *driver_short_names[] __devinitdata = {
        [AZX_DRIVER_NVIDIA] = "HDA NVidia",
        [AZX_DRIVER_TERA] = "HDA Teradici", 
        [AZX_DRIVER_CTX] = "HDA Creative", 
+       [AZX_DRIVER_CTHDA] = "HDA Creative",
        [AZX_DRIVER_GENERIC] = "HD-Audio Generic",
 };
 
@@ -1285,7 +1291,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 /*
  * set up a BDL entry
  */
-static int setup_bdle(struct snd_pcm_substream *substream,
+static int setup_bdle(struct azx *chip,
+                     struct snd_pcm_substream *substream,
                      struct azx_dev *azx_dev, u32 **bdlp,
                      int ofs, int size, int with_ioc)
 {
@@ -1304,6 +1311,12 @@ static int setup_bdle(struct snd_pcm_substream *substream,
                bdl[1] = cpu_to_le32(upper_32_bits(addr));
                /* program the size field of the BDL entry */
                chunk = snd_pcm_sgbuf_get_chunk_size(substream, ofs, size);
+               /* one BDLE cannot cross 4K boundary on CTHDA chips */
+               if (chip->driver_caps & AZX_DCAPS_4K_BDLE_BOUNDARY) {
+                       u32 remain = 0x1000 - (ofs & 0xfff);
+                       if (chunk > remain)
+                               chunk = remain;
+               }
                bdl[2] = cpu_to_le32(chunk);
                /* program the IOC to enable interrupt
                 * only when the whole fragment is processed
@@ -1356,7 +1369,7 @@ static int azx_setup_periods(struct azx *chip,
                                   bdl_pos_adj[chip->dev_index]);
                        pos_adj = 0;
                } else {
-                       ofs = setup_bdle(substream, azx_dev,
+                       ofs = setup_bdle(chip, substream, azx_dev,
                                         &bdl, ofs, pos_adj,
                                         !substream->runtime->no_period_wakeup);
                        if (ofs < 0)
@@ -1366,10 +1379,10 @@ static int azx_setup_periods(struct azx *chip,
                pos_adj = 0;
        for (i = 0; i < periods; i++) {
                if (i == periods - 1 && pos_adj)
-                       ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+                       ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
                                         period_bytes - pos_adj, 0);
                else
-                       ofs = setup_bdle(substream, azx_dev, &bdl, ofs,
+                       ofs = setup_bdle(chip, substream, azx_dev, &bdl, ofs,
                                         period_bytes,
                                         !substream->runtime->no_period_wakeup);
                if (ofs < 0)
@@ -2353,17 +2366,6 @@ static void azx_power_notify(struct hda_bus *bus)
  * power management
  */
 
-static int snd_hda_codecs_inuse(struct hda_bus *bus)
-{
-       struct hda_codec *codec;
-
-       list_for_each_entry(codec, &bus->codec_list, list) {
-               if (snd_hda_codec_needs_resume(codec))
-                       return 1;
-       }
-       return 0;
-}
-
 static int azx_suspend(struct pci_dev *pci, pm_message_t state)
 {
        struct snd_card *card = pci_get_drvdata(pci);
@@ -2410,8 +2412,7 @@ static int azx_resume(struct pci_dev *pci)
                return -EIO;
        azx_init_pci(chip);
 
-       if (snd_hda_codecs_inuse(chip->bus))
-               azx_init_chip(chip, 1);
+       azx_init_chip(chip, 1);
 
        snd_hda_resume(chip->bus);
        snd_power_change_state(card, SNDRV_CTL_POWER_D0);
@@ -2565,6 +2566,8 @@ static struct snd_pci_quirk probe_mask_list[] __devinitdata = {
        /* forced codec slots */
        SND_PCI_QUIRK(0x1043, 0x1262, "ASUS W5Fm", 0x103),
        SND_PCI_QUIRK(0x1046, 0x1262, "ASUS W5F", 0x103),
+       /* WinFast VP200 H (Teradici) user reported broken communication */
+       SND_PCI_QUIRK(0x3a21, 0x040d, "WinFast VP200 H", 0x101),
        {}
 };
 
@@ -3130,6 +3133,11 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
          .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
          AZX_DCAPS_RIRB_PRE_DELAY | AZX_DCAPS_POSFIX_LPIB },
 #endif
+       /* CTHDA chips */
+       { PCI_DEVICE(0x1102, 0x0010),
+         .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
+       { PCI_DEVICE(0x1102, 0x0012),
+         .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
        /* Vortex86MX */
        { PCI_DEVICE(0x17f3, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
        /* VMware HDAudio */
@@ -3148,7 +3156,7 @@ static DEFINE_PCI_DEVICE_TABLE(azx_ids) = {
 MODULE_DEVICE_TABLE(pci, azx_ids);
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver azx_driver = {
        .name = KBUILD_MODNAME,
        .id_table = azx_ids,
        .probe = azx_probe,
@@ -3159,15 +3167,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_azx_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_azx_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_azx_init)
-module_exit(alsa_card_azx_exit)
+module_pci_driver(azx_driver);
index d68948499fbc15d7342ba0c9a63675d769b95a92..2dd1c113a4c1b62248843849a5604ceb972f9f89 100644 (file)
@@ -17,6 +17,7 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 
 bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid)
index c66655cf413a1bc074f62ac91e0ed6e3907e342d..8ae52465ec5df807efde3dad54c3a11fc00ca5aa 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __SOUND_HDA_JACK_H
 #define __SOUND_HDA_JACK_H
 
+struct auto_pin_cfg;
+
 struct hda_jack_tbl {
        hda_nid_t nid;
        unsigned char action;           /* event action (0 = none) */
index 0ec9248165bca9c7f7f7d47aaffe4abc863b600c..9a096a8e0fc5b8cb01ce7620956c9bd5848eec4e 100644 (file)
@@ -262,6 +262,8 @@ int snd_hda_input_mux_put(struct hda_codec *codec,
                          const struct hda_input_mux *imux,
                          struct snd_ctl_elem_value *ucontrol, hda_nid_t nid,
                          unsigned int *cur_val);
+int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
+                         int index, int *type_index_ret);
 
 /*
  * Channel mode helper
@@ -393,72 +395,7 @@ struct hda_bus_unsolicited {
        struct hda_bus *bus;
 };
 
-/*
- * Helper for automatic pin configuration
- */
-
-enum {
-       AUTO_PIN_MIC,
-       AUTO_PIN_LINE_IN,
-       AUTO_PIN_CD,
-       AUTO_PIN_AUX,
-       AUTO_PIN_LAST
-};
-
-enum {
-       AUTO_PIN_LINE_OUT,
-       AUTO_PIN_SPEAKER_OUT,
-       AUTO_PIN_HP_OUT
-};
-
-#define AUTO_CFG_MAX_OUTS      HDA_MAX_OUTS
-#define AUTO_CFG_MAX_INS       8
-
-struct auto_pin_cfg_item {
-       hda_nid_t pin;
-       int type;
-};
-
-struct auto_pin_cfg;
-const char *hda_get_autocfg_input_label(struct hda_codec *codec,
-                                       const struct auto_pin_cfg *cfg,
-                                       int input);
-int snd_hda_get_pin_label(struct hda_codec *codec, hda_nid_t nid,
-                         const struct auto_pin_cfg *cfg,
-                         char *label, int maxlen, int *indexp);
-int snd_hda_add_imux_item(struct hda_input_mux *imux, const char *label,
-                         int index, int *type_index_ret);
-
-enum {
-       INPUT_PIN_ATTR_UNUSED,  /* pin not connected */
-       INPUT_PIN_ATTR_INT,     /* internal mic/line-in */
-       INPUT_PIN_ATTR_DOCK,    /* docking mic/line-in */
-       INPUT_PIN_ATTR_NORMAL,  /* mic/line-in jack */
-       INPUT_PIN_ATTR_FRONT,   /* mic/line-in jack in front */
-       INPUT_PIN_ATTR_REAR,    /* mic/line-in jack in rear */
-};
-
-int snd_hda_get_input_pin_attr(unsigned int def_conf);
-
-struct auto_pin_cfg {
-       int line_outs;
-       /* sorted in the order of Front/Surr/CLFE/Side */
-       hda_nid_t line_out_pins[AUTO_CFG_MAX_OUTS];
-       int speaker_outs;
-       hda_nid_t speaker_pins[AUTO_CFG_MAX_OUTS];
-       int hp_outs;
-       int line_out_type;      /* AUTO_PIN_XXX_OUT */
-       hda_nid_t hp_pins[AUTO_CFG_MAX_OUTS];
-       int num_inputs;
-       struct auto_pin_cfg_item inputs[AUTO_CFG_MAX_INS];
-       int dig_outs;
-       hda_nid_t dig_out_pins[2];
-       hda_nid_t dig_in_pin;
-       hda_nid_t mono_out_pin;
-       int dig_out_type[2]; /* HDA_PCM_TYPE_XXX */
-       int dig_in_type; /* HDA_PCM_TYPE_XXX */
-};
-
+/* helper macros to retrieve pin default-config values */
 #define get_defcfg_connect(cfg) \
        ((cfg & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT)
 #define get_defcfg_association(cfg) \
@@ -472,19 +409,6 @@ struct auto_pin_cfg {
 #define get_defcfg_misc(cfg) \
        ((cfg & AC_DEFCFG_MISC) >> AC_DEFCFG_MISC_SHIFT)
 
-/* bit-flags for snd_hda_parse_pin_def_config() behavior */
-#define HDA_PINCFG_NO_HP_FIXUP (1 << 0) /* no HP-split */
-#define HDA_PINCFG_NO_LO_FIXUP (1 << 1) /* don't take other outs as LO */
-
-int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
-                            struct auto_pin_cfg *cfg,
-                            const hda_nid_t *ignore_nids,
-                            unsigned int cond_flags);
-
-/* older function */
-#define snd_hda_parse_pin_def_config(codec, cfg, ignore) \
-       snd_hda_parse_pin_defcfg(codec, cfg, ignore, 0)
-
 /* amp values */
 #define AMP_IN_MUTE(idx)       (0x7080 | ((idx)<<8))
 #define AMP_IN_UNMUTE(idx)     (0x7000 | ((idx)<<8))
@@ -502,6 +426,46 @@ int snd_hda_parse_pin_defcfg(struct hda_codec *codec,
 #define PIN_HP                 (AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN)
 #define PIN_HP_AMP             (AC_PINCTL_HP_EN)
 
+unsigned int snd_hda_get_default_vref(struct hda_codec *codec, hda_nid_t pin);
+int _snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin,
+                        unsigned int val, bool cached);
+
+/**
+ * _snd_hda_set_pin_ctl - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * This function sets the pin-control value to the given pin, but
+ * filters out the invalid pin-control bits when the pin has no such
+ * capabilities.  For example, when PIN_HP is passed but the pin has no
+ * HP-drive capability, the HP bit is omitted.
+ *
+ * The function doesn't check the input VREF capability bits, though.
+ * Use snd_hda_get_default_vref() to guess the right value.
+ * Also, this function is only for analog pins, not for HDMI pins.
+ */
+static inline int
+snd_hda_set_pin_ctl(struct hda_codec *codec, hda_nid_t pin, unsigned int val)
+{
+       return _snd_hda_set_pin_ctl(codec, pin, val, false);
+}
+
+/**
+ * snd_hda_set_pin_ctl_cache - Set a pin-control value safely
+ * @codec: the codec instance
+ * @pin: the pin NID to set the control
+ * @val: the pin-control value (AC_PINCTL_* bits)
+ *
+ * Just like snd_hda_set_pin_ctl() but write to cache as well.
+ */
+static inline int
+snd_hda_set_pin_ctl_cache(struct hda_codec *codec, hda_nid_t pin,
+                         unsigned int val)
+{
+       return _snd_hda_set_pin_ctl(codec, pin, val, true);
+}
+
 /*
  * get widget capabilities
  */
index 7143393927da34ddeac2c86ce6538c669e85d16a..d8b2d6dee986b928ae0ffe98509a167e07ba639f 100644 (file)
@@ -28,6 +28,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -1742,9 +1743,7 @@ static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
        if (! ad198x_eapd_put(kcontrol, ucontrol))
                return 0;
        /* change speaker pin appropriately */
-       snd_hda_codec_write(codec, 0x05, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           spec->cur_eapd ? PIN_OUT : 0);
+       snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
        /* toggle HP mute appropriately */
        snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
                                 HDA_AMP_MUTE,
@@ -3103,7 +3102,7 @@ static void ad1988_auto_set_output_and_unmute(struct hda_codec *codec,
                                              int dac_idx)
 {
        /* set as output */
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+       snd_hda_set_pin_ctl(codec, nid, pin_type);
        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
        switch (nid) {
        case 0x11: /* port-A - DAC 03 */
@@ -3157,6 +3156,7 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
        for (i = 0; i < cfg->num_inputs; i++) {
                hda_nid_t nid = cfg->inputs[i].pin;
                int type = cfg->inputs[i].type;
+               int val;
                switch (nid) {
                case 0x15: /* port-C */
                        snd_hda_codec_write(codec, 0x33, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
@@ -3165,8 +3165,10 @@ static void ad1988_auto_init_analog_input(struct hda_codec *codec)
                        snd_hda_codec_write(codec, 0x34, 0, AC_VERB_SET_CONNECT_SEL, 0x0);
                        break;
                }
-               snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   type == AUTO_PIN_MIC ? PIN_VREF80 : PIN_IN);
+               val = PIN_IN;
+               if (type == AUTO_PIN_MIC)
+                       val |= snd_hda_get_default_vref(codec, nid);
+               snd_hda_set_pin_ctl(codec, nid, val);
                if (nid != AD1988_PIN_CD_NID)
                        snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
                                            AMP_OUT_MUTE);
index 09ccfabb4a17c38a608d04d05b4a5979e6dc7ed7..19ae14f739cbbd7ad0d5c6bcff8cf945db97ff1b 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 
 /*
  */
@@ -341,8 +342,7 @@ static int ca0110_build_pcms(struct hda_codec *codec)
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               snd_hda_set_pin_ctl(codec, pin, PIN_HP);
                if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -356,8 +356,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80);
+               snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+                                   snd_hda_get_default_vref(codec, pin));
                if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
index 21d91d580da8b9e29091dca8ad5d91440b38797a..d0d3540e39e7746b1c42074257fe8194b928e09b 100644 (file)
@@ -30,6 +30,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 
 #define WIDGET_CHIP_CTRL      0x15
 #define WIDGET_DSP_CTRL       0x16
@@ -239,8 +240,7 @@ enum get_set {
 static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               snd_hda_set_pin_ctl(codec, pin, PIN_HP);
                if (get_wcaps(codec, pin) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -254,9 +254,8 @@ static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac)
 static void init_input(struct hda_codec *codec, hda_nid_t pin, hda_nid_t adc)
 {
        if (pin) {
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   PIN_VREF80);
+               snd_hda_set_pin_ctl(codec, pin, PIN_IN |
+                                   snd_hda_get_default_vref(codec, pin));
                if (get_wcaps(codec, pin) & AC_WCAP_IN_AMP)
                        snd_hda_codec_write(codec, pin, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
index c83ccdba1e5afc1dca9715a870eedf68a8961ee8..9647ed4d7929175452ea77eee81677342264104f 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 #include <sound/tlv.h>
 
@@ -933,8 +934,7 @@ static void cs_automute(struct hda_codec *codec)
                        pin_ctl = 0;
 
                nid = cfg->speaker_pins[i];
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl);
+               snd_hda_set_pin_ctl(codec, nid, pin_ctl);
        }
        if (spec->gpio_eapd_hp) {
                unsigned int gpio = hp_present ?
@@ -948,16 +948,14 @@ static void cs_automute(struct hda_codec *codec)
                /* mute HPs if spdif jack (SENSE_B) is present */
                for (i = 0; i < cfg->hp_outs; i++) {
                        nid = cfg->hp_pins[i];
-                       snd_hda_codec_write(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       snd_hda_set_pin_ctl(codec, nid,
                                (spdif_present && spec->sense_b) ? 0 : PIN_HP);
                }
 
                /* SPDIF TX on/off */
                if (cfg->dig_outs) {
                        nid = cfg->dig_out_pins[0];
-                       snd_hda_codec_write(codec, nid, 0,
-                               AC_VERB_SET_PIN_WIDGET_CONTROL,
+                       snd_hda_set_pin_ctl(codec, nid,
                                spdif_present ? PIN_OUT : 0);
 
                }
@@ -1024,13 +1022,11 @@ static void init_output(struct hda_codec *codec)
 
        /* set appropriate pin controls */
        for (i = 0; i < cfg->line_outs; i++)
-               snd_hda_codec_write(codec, cfg->line_out_pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               snd_hda_set_pin_ctl(codec, cfg->line_out_pins[i], PIN_OUT);
        /* HP */
        for (i = 0; i < cfg->hp_outs; i++) {
                hda_nid_t nid = cfg->hp_pins[i];
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP);
+               snd_hda_set_pin_ctl(codec, nid, PIN_HP);
                if (!cfg->speaker_outs)
                        continue;
                if (get_wcaps(codec, nid) & AC_WCAP_UNSOL_CAP) {
@@ -1041,8 +1037,7 @@ static void init_output(struct hda_codec *codec)
 
        /* Speaker */
        for (i = 0; i < cfg->speaker_outs; i++)
-               snd_hda_codec_write(codec, cfg->speaker_pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               snd_hda_set_pin_ctl(codec, cfg->speaker_pins[i], PIN_OUT);
 
        /* SPDIF is enabled on presence detect for CS421x */
        if (spec->hp_detect || spec->spdif_detect)
@@ -1063,14 +1058,9 @@ static void init_input(struct hda_codec *codec)
                        continue;
                /* set appropriate pin control and mute first */
                ctl = PIN_IN;
-               if (cfg->inputs[i].type == AUTO_PIN_MIC) {
-                       unsigned int caps = snd_hda_query_pin_caps(codec, pin);
-                       caps >>= AC_PINCAP_VREF_SHIFT;
-                       if (caps & AC_PINCAP_VREF_80)
-                               ctl = PIN_VREF80;
-               }
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+               if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                       ctl |= snd_hda_get_default_vref(codec, pin);
+               snd_hda_set_pin_ctl(codec, pin, ctl);
                snd_hda_codec_write(codec, spec->adc_nid[i], 0,
                                    AC_VERB_SET_AMP_GAIN_MUTE,
                                    AMP_IN_MUTE(spec->adc_idx[i]));
index b6767b4ced4478c5d1d7a0eb671d528284ef8e34..c8fdaaefe7029a403e81e03dcac96cee721ecca3 100644 (file)
@@ -29,6 +29,7 @@
 #include <sound/core.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #define NUM_PINS       11
 
 
index d906c5b74cf0e7b047a4e702c71ba8b6d0e5d5de..3acb5824ad39e95e354491c2336a445cd796745e 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -66,6 +67,7 @@ struct imux_info {
 };
 
 struct conexant_spec {
+       struct hda_gen_spec gen;
 
        const struct snd_kcontrol_new *mixers[5];
        int num_mixers;
@@ -141,6 +143,7 @@ struct conexant_spec {
        unsigned int hp_laptop:1;
        unsigned int asus:1;
        unsigned int pin_eapd_ctrls:1;
+       unsigned int fixup_stereo_dmic:1;
 
        unsigned int adc_switching:1;
 
@@ -1601,17 +1604,13 @@ static void cxt5051_update_speaker(struct hda_codec *codec)
        unsigned int pinctl;
        /* headphone pin */
        pinctl = (spec->hp_present && spec->cur_eapd) ? PIN_HP : 0;
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pinctl);
+       snd_hda_set_pin_ctl(codec, 0x16, pinctl);
        /* speaker pin */
        pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pinctl);
+       snd_hda_set_pin_ctl(codec, 0x1a, pinctl);
        /* on ideapad there is an additional speaker (subwoofer) to mute */
        if (spec->ideapad)
-               snd_hda_codec_write(codec, 0x1b, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   pinctl);
+               snd_hda_set_pin_ctl(codec, 0x1b, pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -1996,8 +1995,7 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
 
        /* Port A (HP) */
        pinctl = (hp_port_a_present(spec) && spec->cur_eapd) ? PIN_HP : 0;
-       snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pinctl);
+       snd_hda_set_pin_ctl(codec, 0x19, pinctl);
 
        /* Port D (HP/LO) */
        pinctl = spec->cur_eapd ? spec->port_d_mode : 0;
@@ -2010,13 +2008,11 @@ static void cxt5066_update_speaker(struct hda_codec *codec)
                if (!hp_port_d_present(spec))
                        pinctl = 0;
        }
-       snd_hda_codec_write(codec, 0x1c, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pinctl);
+       snd_hda_set_pin_ctl(codec, 0x1c, pinctl);
 
        /* CLASS_D AMP */
        pinctl = (!spec->hp_present && spec->cur_eapd) ? PIN_OUT : 0;
-       snd_hda_codec_write(codec, 0x1f, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                       pinctl);
+       snd_hda_set_pin_ctl(codec, 0x1f, pinctl);
 }
 
 /* turn on/off EAPD (+ mute HP) as a master switch */
@@ -2047,8 +2043,7 @@ static int cxt5066_set_olpc_dc_bias(struct hda_codec *codec)
        /* Even though port F is the DC input, the bias is controlled on port B.
         * we also leave that port as an active input (but unselected) in DC mode
         * just in case that is necessary to make the bias setting take effect. */
-       return snd_hda_codec_write_cache(codec, 0x1a, 0,
-               AC_VERB_SET_PIN_WIDGET_CONTROL,
+       return snd_hda_set_pin_ctl_cache(codec, 0x1a,
                cxt5066_olpc_dc_bias.items[spec->dc_input_bias].index);
 }
 
@@ -2081,14 +2076,14 @@ static void cxt5066_olpc_select_mic(struct hda_codec *codec)
        }
 
        /* disable DC (port F) */
-       snd_hda_codec_write(codec, 0x1e, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+       snd_hda_set_pin_ctl(codec, 0x1e, 0);
 
        /* external mic, port B */
-       snd_hda_codec_write(codec, 0x1a, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_set_pin_ctl(codec, 0x1a,
                spec->ext_mic_present ? CXT5066_OLPC_EXT_MIC_BIAS : 0);
 
        /* internal mic, port C */
-       snd_hda_codec_write(codec, 0x1b, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+       snd_hda_set_pin_ctl(codec, 0x1b,
                spec->ext_mic_present ? 0 : PIN_VREF80);
 }
 
@@ -3357,9 +3352,7 @@ static void do_automute(struct hda_codec *codec, int num_pins,
        struct conexant_spec *spec = codec->spec;
        int i;
        for (i = 0; i < num_pins; i++)
-               snd_hda_codec_write(codec, pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   on ? PIN_OUT : 0);
+               snd_hda_set_pin_ctl(codec, pins[i], on ? PIN_OUT : 0);
        if (spec->pin_eapd_ctrls)
                cx_auto_turn_eapd(codec, num_pins, pins, on);
 }
@@ -3976,8 +3969,7 @@ static void cx_auto_init_output(struct hda_codec *codec)
                if (snd_hda_query_pin_caps(codec, cfg->hp_pins[i]) &
                    AC_PINCAP_HP_DRV)
                        val |= AC_PINCTL_HP_EN;
-               snd_hda_codec_write(codec, cfg->hp_pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+               snd_hda_set_pin_ctl(codec, cfg->hp_pins[i], val);
        }
        mute_outputs(codec, cfg->hp_outs, cfg->hp_pins);
        mute_outputs(codec, cfg->line_outs, cfg->line_out_pins);
@@ -4030,13 +4022,11 @@ static void cx_auto_init_input(struct hda_codec *codec)
        }
 
        for (i = 0; i < cfg->num_inputs; i++) {
-               unsigned int type;
+               hda_nid_t pin = cfg->inputs[i].pin;
+               unsigned int type = PIN_IN;
                if (cfg->inputs[i].type == AUTO_PIN_MIC)
-                       type = PIN_VREF80;
-               else
-                       type = PIN_IN;
-               snd_hda_codec_write(codec, cfg->inputs[i].pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, type);
+                       type |= snd_hda_get_default_vref(codec, pin);
+               snd_hda_set_pin_ctl(codec, pin, type);
        }
 
        if (spec->auto_mic) {
@@ -4063,11 +4053,9 @@ static void cx_auto_init_digital(struct hda_codec *codec)
        struct auto_pin_cfg *cfg = &spec->autocfg;
 
        if (spec->multiout.dig_out_nid)
-               snd_hda_codec_write(codec, cfg->dig_out_pins[0], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               snd_hda_set_pin_ctl(codec, cfg->dig_out_pins[0], PIN_OUT);
        if (spec->dig_in_nid)
-               snd_hda_codec_write(codec, cfg->dig_in_pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+               snd_hda_set_pin_ctl(codec, cfg->dig_in_pin, PIN_IN);
 }
 
 static int cx_auto_init(struct hda_codec *codec)
@@ -4084,9 +4072,9 @@ static int cx_auto_init(struct hda_codec *codec)
 
 static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
                              const char *dir, int cidx,
-                             hda_nid_t nid, int hda_dir, int amp_idx)
+                             hda_nid_t nid, int hda_dir, int amp_idx, int chs)
 {
-       static char name[32];
+       static char name[44];
        static struct snd_kcontrol_new knew[] = {
                HDA_CODEC_VOLUME(name, 0, 0, 0),
                HDA_CODEC_MUTE(name, 0, 0, 0),
@@ -4096,7 +4084,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
 
        for (i = 0; i < 2; i++) {
                struct snd_kcontrol *kctl;
-               knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, 3, amp_idx,
+               knew[i].private_value = HDA_COMPOSE_AMP_VAL(nid, chs, amp_idx,
                                                            hda_dir);
                knew[i].subdevice = HDA_SUBDEV_AMP_FLAG;
                knew[i].index = cidx;
@@ -4115,7 +4103,7 @@ static int cx_auto_add_volume_idx(struct hda_codec *codec, const char *basename,
 }
 
 #define cx_auto_add_volume(codec, str, dir, cidx, nid, hda_dir)                \
-       cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0)
+       cx_auto_add_volume_idx(codec, str, dir, cidx, nid, hda_dir, 0, 3)
 
 #define cx_auto_add_pb_volume(codec, nid, str, idx)                    \
        cx_auto_add_volume(codec, str, " Playback", idx, nid, HDA_OUTPUT)
@@ -4185,6 +4173,36 @@ static int cx_auto_build_output_controls(struct hda_codec *codec)
        return 0;
 }
 
+/* Returns zero if this is a normal stereo channel, and non-zero if it should
+   be split in two independent channels.
+   dest_label must be at least 44 characters. */
+static int cx_auto_get_rightch_label(struct hda_codec *codec, const char *label,
+                                    char *dest_label, int nid)
+{
+       struct conexant_spec *spec = codec->spec;
+       int i;
+
+       if (!spec->fixup_stereo_dmic)
+               return 0;
+
+       for (i = 0; i < AUTO_CFG_MAX_INS; i++) {
+               int def_conf;
+               if (spec->autocfg.inputs[i].pin != nid)
+                       continue;
+
+               if (spec->autocfg.inputs[i].type != AUTO_PIN_MIC)
+                       return 0;
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT)
+                       return 0;
+
+               /* Finally found the inverted internal mic! */
+               snprintf(dest_label, 44, "Inverted %s", label);
+               return 1;
+       }
+       return 0;
+}
+
 static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
                                      const char *label, const char *pfx,
                                      int cidx)
@@ -4193,14 +4211,25 @@ static int cx_auto_add_capture_volume(struct hda_codec *codec, hda_nid_t nid,
        int i;
 
        for (i = 0; i < spec->num_adc_nids; i++) {
+               char rightch_label[44];
                hda_nid_t adc_nid = spec->adc_nids[i];
                int idx = get_input_connection(codec, adc_nid, nid);
                if (idx < 0)
                        continue;
                if (codec->single_adc_amp)
                        idx = 0;
+
+               if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+                       /* Make two independent kcontrols for left and right */
+                       int err = cx_auto_add_volume_idx(codec, label, pfx,
+                                             cidx, adc_nid, HDA_INPUT, idx, 1);
+                       if (err < 0)
+                               return err;
+                       return cx_auto_add_volume_idx(codec, rightch_label, pfx,
+                                                     cidx, adc_nid, HDA_INPUT, idx, 2);
+               }
                return cx_auto_add_volume_idx(codec, label, pfx,
-                                             cidx, adc_nid, HDA_INPUT, idx);
+                                             cidx, adc_nid, HDA_INPUT, idx, 3);
        }
        return 0;
 }
@@ -4213,9 +4242,19 @@ static int cx_auto_add_boost_volume(struct hda_codec *codec, int idx,
        int i, con;
 
        nid = spec->imux_info[idx].pin;
-       if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP)
+       if (get_wcaps(codec, nid) & AC_WCAP_IN_AMP) {
+               char rightch_label[44];
+               if (cx_auto_get_rightch_label(codec, label, rightch_label, nid)) {
+                       int err = cx_auto_add_volume_idx(codec, label, " Boost",
+                                                        cidx, nid, HDA_INPUT, 0, 1);
+                       if (err < 0)
+                               return err;
+                       return cx_auto_add_volume_idx(codec, rightch_label, " Boost",
+                                                     cidx, nid, HDA_INPUT, 0, 2);
+               }
                return cx_auto_add_volume(codec, label, " Boost", cidx,
                                          nid, HDA_INPUT);
+       }
        con = __select_input_connection(codec, spec->imux_info[idx].adc, nid,
                                        &mux, false, 0);
        if (con < 0)
@@ -4370,37 +4409,21 @@ static const struct hda_codec_ops cx_auto_patch_ops = {
 /*
  * pin fix-up
  */
-struct cxt_pincfg {
-       hda_nid_t nid;
-       u32 val;
-};
-
-static void apply_pincfg(struct hda_codec *codec, const struct cxt_pincfg *cfg)
-{
-       for (; cfg->nid; cfg++)
-               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-
-}
-
-static void apply_pin_fixup(struct hda_codec *codec,
-                           const struct snd_pci_quirk *quirk,
-                           const struct cxt_pincfg **table)
-{
-       quirk = snd_pci_quirk_lookup(codec->bus->pci, quirk);
-       if (quirk) {
-               snd_printdd(KERN_INFO "hda_codec: applying pincfg for %s\n",
-                           quirk->name);
-               apply_pincfg(codec, table[quirk->value]);
-       }
-}
-
 enum {
        CXT_PINCFG_LENOVO_X200,
        CXT_PINCFG_LENOVO_TP410,
+       CXT_FIXUP_STEREO_DMIC,
 };
 
+static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
+                                 const struct hda_fixup *fix, int action)
+{
+       struct conexant_spec *spec = codec->spec;
+       spec->fixup_stereo_dmic = 1;
+}
+
 /* ThinkPad X200 & co with cxt5051 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
        { 0x16, 0x042140ff }, /* HP (seq# overridden) */
        { 0x17, 0x21a11000 }, /* dock-mic */
        { 0x19, 0x2121103f }, /* dock-HP */
@@ -4409,16 +4432,26 @@ static const struct cxt_pincfg cxt_pincfg_lenovo_x200[] = {
 };
 
 /* ThinkPad 410/420/510/520, X201 & co with cxt5066 */
-static const struct cxt_pincfg cxt_pincfg_lenovo_tp410[] = {
+static const struct hda_pintbl cxt_pincfg_lenovo_tp410[] = {
        { 0x19, 0x042110ff }, /* HP (seq# overridden) */
        { 0x1a, 0x21a190f0 }, /* dock-mic */
        { 0x1c, 0x212140ff }, /* dock-HP */
        {}
 };
 
-static const struct cxt_pincfg *cxt_pincfg_tbl[] = {
-       [CXT_PINCFG_LENOVO_X200] = cxt_pincfg_lenovo_x200,
-       [CXT_PINCFG_LENOVO_TP410] = cxt_pincfg_lenovo_tp410,
+static const struct hda_fixup cxt_fixups[] = {
+       [CXT_PINCFG_LENOVO_X200] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = cxt_pincfg_lenovo_x200,
+       },
+       [CXT_PINCFG_LENOVO_TP410] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = cxt_pincfg_lenovo_tp410,
+       },
+       [CXT_FIXUP_STEREO_DMIC] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cxt_fixup_stereo_dmic,
+       },
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -4432,6 +4465,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x21ce, "Lenovo T420", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520", CXT_PINCFG_LENOVO_TP410),
+       SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC),
        {}
 };
 
@@ -4471,13 +4505,16 @@ static int patch_conexant_auto(struct hda_codec *codec)
        case 0x14f15051:
                add_cx5051_fake_mutes(codec);
                codec->pin_amp_workaround = 1;
-               apply_pin_fixup(codec, cxt5051_fixups, cxt_pincfg_tbl);
+               snd_hda_pick_fixup(codec, NULL, cxt5051_fixups, cxt_fixups);
                break;
        default:
                codec->pin_amp_workaround = 1;
-               apply_pin_fixup(codec, cxt5066_fixups, cxt_pincfg_tbl);
+               snd_hda_pick_fixup(codec, NULL, cxt5066_fixups, cxt_fixups);
+               break;
        }
 
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
        /* Show mute-led control only on HP laptops
         * This is a sort of white-list: on HP laptops, EAPD corresponds
         * only to the mute-LED without actualy amp function.  Meanwhile,
@@ -4556,6 +4593,12 @@ static const struct hda_codec_preset snd_hda_preset_conexant[] = {
          .patch = patch_conexant_auto },
        { .id = 0x14f150b9, .name = "CX20665",
          .patch = patch_conexant_auto },
+       { .id = 0x14f1510f, .name = "CX20751/2",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15110, .name = "CX20751/2",
+         .patch = patch_conexant_auto },
+       { .id = 0x14f15111, .name = "CX20753/4",
+         .patch = patch_conexant_auto },
        {} /* terminator */
 };
 
@@ -4576,6 +4619,9 @@ MODULE_ALIAS("snd-hda-codec-id:14f150ab");
 MODULE_ALIAS("snd-hda-codec-id:14f150ac");
 MODULE_ALIAS("snd-hda-codec-id:14f150b8");
 MODULE_ALIAS("snd-hda-codec-id:14f150b9");
+MODULE_ALIAS("snd-hda-codec-id:14f1510f");
+MODULE_ALIAS("snd-hda-codec-id:14f15110");
+MODULE_ALIAS("snd-hda-codec-id:14f15111");
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
index 83f345f3c961b05313a17e10d9c46d53ace138c7..ad319d4dc32f7e5d0d5265020a0f700c10bb08ab 100644 (file)
@@ -1592,10 +1592,10 @@ static int nvhdmi_8ch_7x_pcm_prepare(struct hda_pcm_stream *hinfo,
        unsigned int dataDCC2, channel_id;
        int i;
        struct hdmi_spec *spec = codec->spec;
-       struct hda_spdif_out *spdif =
-               snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
+       struct hda_spdif_out *spdif;
 
        mutex_lock(&codec->spdif_mutex);
+       spdif = snd_hda_spdif_out_of_nid(codec, spec->cvts[0].cvt_nid);
 
        chs = substream->runtime->channels;
 
index 7810913d07a032f303815966f4c19c26cc22c5a1..3cb1f7128b5f04a25a30bfdf2be788996ac1f3b2 100644 (file)
@@ -32,6 +32,7 @@
 #include <sound/jack.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -66,8 +67,6 @@ struct alc_customize_define {
        unsigned int  fixup:1; /* Means that this sku is set by driver, not read from hw */
 };
 
-struct alc_fixup;
-
 struct alc_multi_io {
        hda_nid_t pin;          /* multi-io widget pin NID */
        hda_nid_t dac;          /* DAC to be connected */
@@ -82,19 +81,33 @@ enum {
 
 #define MAX_VOL_NIDS   0x40
 
+/* make compatible with old code */
+#define alc_apply_pincfgs      snd_hda_apply_pincfgs
+#define alc_apply_fixup                snd_hda_apply_fixup
+#define alc_pick_fixup         snd_hda_pick_fixup
+#define alc_fixup              hda_fixup
+#define alc_pincfg             hda_pintbl
+#define alc_model_fixup                hda_model_fixup
+
+#define ALC_FIXUP_PINS HDA_FIXUP_PINS
+#define ALC_FIXUP_VERBS        HDA_FIXUP_VERBS
+#define ALC_FIXUP_FUNC HDA_FIXUP_FUNC
+
+#define ALC_FIXUP_ACT_PRE_PROBE        HDA_FIXUP_ACT_PRE_PROBE
+#define ALC_FIXUP_ACT_PROBE    HDA_FIXUP_ACT_PROBE
+#define ALC_FIXUP_ACT_INIT     HDA_FIXUP_ACT_INIT
+#define ALC_FIXUP_ACT_BUILD    HDA_FIXUP_ACT_BUILD
+
+
 struct alc_spec {
+       struct hda_gen_spec gen;
+
        /* codec parameterization */
        const struct snd_kcontrol_new *mixers[5];       /* mixer arrays */
        unsigned int num_mixers;
        const struct snd_kcontrol_new *cap_mixer;       /* capture mixer */
        unsigned int beep_amp;  /* beep amp value, set via set_beep_amp() */
 
-       const struct hda_verb *init_verbs[10];  /* initialization verbs
-                                                * don't forget NULL
-                                                * termination!
-                                                */
-       unsigned int num_init_verbs;
-
        char stream_name_analog[32];    /* analog PCM stream */
        const struct hda_pcm_stream *stream_analog_playback;
        const struct hda_pcm_stream *stream_analog_capture;
@@ -210,11 +223,6 @@ struct alc_spec {
        unsigned int pll_coef_idx, pll_coef_bit;
        unsigned int coef0;
 
-       /* fix-up list */
-       int fixup_id;
-       const struct alc_fixup *fixup_list;
-       const char *fixup_name;
-
        /* multi-io */
        int multi_ios;
        struct alc_multi_io multi_io[4];
@@ -319,13 +327,16 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
 
        /* for shared I/O, change the pin-control accordingly */
        if (spec->shared_mic_hp) {
+               unsigned int val;
+               hda_nid_t pin = spec->autocfg.inputs[1].pin;
                /* NOTE: this assumes that there are only two inputs, the
                 * first is the real internal mic and the second is HP jack.
                 */
-               snd_hda_codec_write(codec, spec->autocfg.inputs[1].pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   spec->cur_mux[adc_idx] ?
-                                   PIN_VREF80 : PIN_HP);
+               if (spec->cur_mux[adc_idx])
+                       val = snd_hda_get_default_vref(codec, pin) | PIN_IN;
+               else
+                       val = PIN_HP;
+               snd_hda_set_pin_ctl(codec, pin, val);
                spec->automute_speaker = !spec->cur_mux[adc_idx];
                call_update_outputs(codec);
        }
@@ -338,7 +349,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
        nid = get_capsrc(spec, adc_idx);
 
        /* no selection? */
-       num_conns = snd_hda_get_conn_list(codec, nid, NULL);
+       num_conns = snd_hda_get_num_conns(codec, nid);
        if (num_conns <= 1)
                return 1;
 
@@ -376,25 +387,9 @@ static void alc_set_input_pin(struct hda_codec *codec, hda_nid_t nid,
                              int auto_pin_type)
 {
        unsigned int val = PIN_IN;
-
-       if (auto_pin_type == AUTO_PIN_MIC) {
-               unsigned int pincap;
-               unsigned int oldval;
-               oldval = snd_hda_codec_read(codec, nid, 0,
-                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
-               pincap = snd_hda_query_pin_caps(codec, nid);
-               pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-               /* if the default pin setup is vref50, we give it priority */
-               if ((pincap & AC_PINCAP_VREF_80) && oldval != PIN_VREF50)
-                       val = PIN_VREF80;
-               else if (pincap & AC_PINCAP_VREF_50)
-                       val = PIN_VREF50;
-               else if (pincap & AC_PINCAP_VREF_100)
-                       val = PIN_VREF100;
-               else if (pincap & AC_PINCAP_VREF_GRD)
-                       val = PIN_VREFGRD;
-       }
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       if (auto_pin_type == AUTO_PIN_MIC)
+               val |= snd_hda_get_default_vref(codec, nid);
+       snd_hda_set_pin_ctl(codec, nid, val);
 }
 
 /*
@@ -409,13 +404,6 @@ static void add_mixer(struct alc_spec *spec, const struct snd_kcontrol_new *mix)
        spec->mixers[spec->num_mixers++] = mix;
 }
 
-static void add_verb(struct alc_spec *spec, const struct hda_verb *verb)
-{
-       if (snd_BUG_ON(spec->num_init_verbs >= ARRAY_SIZE(spec->init_verbs)))
-               return;
-       spec->init_verbs[spec->num_init_verbs++] = verb;
-}
-
 /*
  * GPIO setup tables, used in initialization
  */
@@ -517,9 +505,7 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
                        } else
                                val = 0;
                        val |= pin_bits;
-                       snd_hda_codec_write(codec, nid, 0,
-                                           AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                           val);
+                       snd_hda_set_pin_ctl(codec, nid, val);
                        break;
                case ALC_AUTOMUTE_AMP:
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
@@ -1200,6 +1186,16 @@ static void alc_auto_check_switches(struct hda_codec *codec)
  */
 #define ALC_FIXUP_SKU_IGNORE (2)
 
+static void alc_fixup_sku_ignore(struct hda_codec *codec,
+                                const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+               spec->cdefine.fixup = 1;
+               spec->cdefine.sku_cfg = ALC_FIXUP_SKU_IGNORE;
+       }
+}
+
 static int alc_auto_parse_customize_define(struct hda_codec *codec)
 {
        unsigned int ass, tmp, i;
@@ -1402,178 +1398,6 @@ static void alc_ssid_check(struct hda_codec *codec, const hda_nid_t *ports)
        }
 }
 
-/*
- * Fix-up pin default configurations and add default verbs
- */
-
-struct alc_pincfg {
-       hda_nid_t nid;
-       u32 val;
-};
-
-struct alc_model_fixup {
-       const int id;
-       const char *name;
-};
-
-struct alc_fixup {
-       int type;
-       bool chained;
-       int chain_id;
-       union {
-               unsigned int sku;
-               const struct alc_pincfg *pins;
-               const struct hda_verb *verbs;
-               void (*func)(struct hda_codec *codec,
-                            const struct alc_fixup *fix,
-                            int action);
-       } v;
-};
-
-enum {
-       ALC_FIXUP_INVALID,
-       ALC_FIXUP_SKU,
-       ALC_FIXUP_PINS,
-       ALC_FIXUP_VERBS,
-       ALC_FIXUP_FUNC,
-};
-
-enum {
-       ALC_FIXUP_ACT_PRE_PROBE,
-       ALC_FIXUP_ACT_PROBE,
-       ALC_FIXUP_ACT_INIT,
-       ALC_FIXUP_ACT_BUILD,
-};
-
-static void alc_apply_pincfgs(struct hda_codec *codec,
-                             const struct alc_pincfg *cfg)
-{
-       for (; cfg->nid; cfg++)
-               snd_hda_codec_set_pincfg(codec, cfg->nid, cfg->val);
-}
-
-static void alc_apply_fixup(struct hda_codec *codec, int action)
-{
-       struct alc_spec *spec = codec->spec;
-       int id = spec->fixup_id;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       const char *modelname = spec->fixup_name;
-#endif
-       int depth = 0;
-
-       if (!spec->fixup_list)
-               return;
-
-       while (id >= 0) {
-               const struct alc_fixup *fix = spec->fixup_list + id;
-               const struct alc_pincfg *cfg;
-
-               switch (fix->type) {
-               case ALC_FIXUP_SKU:
-                       if (action != ALC_FIXUP_ACT_PRE_PROBE || !fix->v.sku)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply sku override for %s\n",
-                                   codec->chip_name, modelname);
-                       spec->cdefine.sku_cfg = fix->v.sku;
-                       spec->cdefine.fixup = 1;
-                       break;
-               case ALC_FIXUP_PINS:
-                       cfg = fix->v.pins;
-                       if (action != ALC_FIXUP_ACT_PRE_PROBE || !cfg)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply pincfg for %s\n",
-                                   codec->chip_name, modelname);
-                       alc_apply_pincfgs(codec, cfg);
-                       break;
-               case ALC_FIXUP_VERBS:
-                       if (action != ALC_FIXUP_ACT_PROBE || !fix->v.verbs)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply fix-verbs for %s\n",
-                                   codec->chip_name, modelname);
-                       add_verb(codec->spec, fix->v.verbs);
-                       break;
-               case ALC_FIXUP_FUNC:
-                       if (!fix->v.func)
-                               break;
-                       snd_printdd(KERN_INFO "hda_codec: %s: "
-                                   "Apply fix-func for %s\n",
-                                   codec->chip_name, modelname);
-                       fix->v.func(codec, fix, action);
-                       break;
-               default:
-                       snd_printk(KERN_ERR "hda_codec: %s: "
-                                  "Invalid fixup type %d\n",
-                                  codec->chip_name, fix->type);
-                       break;
-               }
-               if (!fix->chained)
-                       break;
-               if (++depth > 10)
-                       break;
-               id = fix->chain_id;
-       }
-}
-
-static void alc_pick_fixup(struct hda_codec *codec,
-                          const struct alc_model_fixup *models,
-                          const struct snd_pci_quirk *quirk,
-                          const struct alc_fixup *fixlist)
-{
-       struct alc_spec *spec = codec->spec;
-       const struct snd_pci_quirk *q;
-       int id = -1;
-       const char *name = NULL;
-
-       /* when model=nofixup is given, don't pick up any fixups */
-       if (codec->modelname && !strcmp(codec->modelname, "nofixup")) {
-               spec->fixup_list = NULL;
-               spec->fixup_id = -1;
-               return;
-       }
-
-       if (codec->modelname && models) {
-               while (models->name) {
-                       if (!strcmp(codec->modelname, models->name)) {
-                               id = models->id;
-                               name = models->name;
-                               break;
-                       }
-                       models++;
-               }
-       }
-       if (id < 0) {
-               q = snd_pci_quirk_lookup(codec->bus->pci, quirk);
-               if (q) {
-                       id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                       name = q->name;
-#endif
-               }
-       }
-       if (id < 0) {
-               for (q = quirk; q->subvendor; q++) {
-                       unsigned int vendorid =
-                               q->subdevice | (q->subvendor << 16);
-                       if (vendorid == codec->subsystem_id) {
-                               id = q->value;
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                               name = q->name;
-#endif
-                               break;
-                       }
-               }
-       }
-
-       spec->fixup_id = id;
-       if (id >= 0) {
-               spec->fixup_list = fixlist;
-               spec->fixup_name = name;
-       }
-}
-
 /*
  * COEF access helper functions
  */
@@ -1621,8 +1445,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
                pin = spec->autocfg.dig_out_pins[i];
                if (!pin)
                        continue;
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+               snd_hda_set_pin_ctl(codec, pin, PIN_OUT);
                if (!i)
                        dac = spec->multiout.dig_out_nid;
                else
@@ -1635,9 +1458,7 @@ static void alc_auto_init_digital(struct hda_codec *codec)
        }
        pin = spec->autocfg.dig_in_pin;
        if (pin)
-               snd_hda_codec_write(codec, pin, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   PIN_IN);
+               snd_hda_set_pin_ctl(codec, pin, PIN_IN);
 }
 
 /* parse digital I/Os and set up NIDs in BIOS auto-parse mode */
@@ -2068,7 +1889,6 @@ static void alc_auto_init_std(struct hda_codec *codec);
 static int alc_init(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
-       unsigned int i;
 
        if (spec->init_hook)
                spec->init_hook(codec);
@@ -2076,8 +1896,6 @@ static int alc_init(struct hda_codec *codec)
        alc_fix_pll(codec);
        alc_auto_init_amp(codec, spec->init_amp);
 
-       for (i = 0; i < spec->num_init_verbs; i++)
-               snd_hda_sequence_write(codec, spec->init_verbs[i]);
        alc_init_special_input_src(codec);
        alc_auto_init_std(codec);
 
@@ -2725,7 +2543,6 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
        nid = codec->start_nid;
        for (i = 0; i < codec->num_nodes; i++, nid++) {
                hda_nid_t src;
-               const hda_nid_t *list;
                unsigned int caps = get_wcaps(codec, nid);
                int type = get_wcaps_type(caps);
 
@@ -2743,13 +2560,14 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
                                cap_nids[nums] = src;
                                break;
                        }
-                       n = snd_hda_get_conn_list(codec, src, &list);
+                       n = snd_hda_get_num_conns(codec, src);
                        if (n > 1) {
                                cap_nids[nums] = src;
                                break;
                        } else if (n != 1)
                                break;
-                       src = *list;
+                       if (snd_hda_get_connections(codec, src, &src, 1) != 1)
+                               break;
                }
                if (++nums >= max_nums)
                        break;
@@ -2856,8 +2674,7 @@ static int alc_auto_create_shared_input(struct hda_codec *codec)
 static void alc_set_pin_output(struct hda_codec *codec, hda_nid_t nid,
                               unsigned int pin_type)
 {
-       snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pin_type);
+       snd_hda_set_pin_ctl(codec, nid, pin_type);
        /* unmute pin */
        if (nid_has_mute(codec, nid, HDA_OUTPUT))
                snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_AMP_GAIN_MUTE,
@@ -2891,7 +2708,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
 
        /* mute all loopback inputs */
        if (spec->mixer_nid) {
-               int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL);
+               int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
                for (i = 0; i < nums; i++)
                        snd_hda_codec_write(codec, spec->mixer_nid, 0,
                                            AC_VERB_SET_AMP_GAIN_MUTE,
@@ -3521,7 +3338,7 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
        if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
                type = ALC_CTL_WIDGET_MUTE;
                val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
-       } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) {
+       } else if (snd_hda_get_num_conns(codec, nid) == 1) {
                type = ALC_CTL_WIDGET_MUTE;
                val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
        } else {
@@ -3998,9 +3815,7 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
                        snd_hda_codec_read(codec, nid, 0,
                                           AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
        if (output) {
-               snd_hda_codec_update_cache(codec, nid, 0,
-                                          AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                          PIN_OUT);
+               snd_hda_set_pin_ctl_cache(codec, nid, PIN_OUT);
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
                                                 HDA_AMP_MUTE, 0);
@@ -4009,9 +3824,8 @@ static int alc_set_multi_io(struct hda_codec *codec, int idx, bool output)
                if (get_wcaps(codec, nid) & AC_WCAP_OUT_AMP)
                        snd_hda_codec_amp_stereo(codec, nid, HDA_OUTPUT, 0,
                                                 HDA_AMP_MUTE, HDA_AMP_MUTE);
-               snd_hda_codec_update_cache(codec, nid, 0,
-                                          AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                          spec->multi_io[idx].ctl_in);
+               snd_hda_set_pin_ctl_cache(codec, nid,
+                                         spec->multi_io[idx].ctl_in);
        }
        return 0;
 }
@@ -4084,7 +3898,7 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
        nums = 0;
        for (n = 0; n < spec->num_adc_nids; n++) {
                hda_nid_t cap = spec->private_capsrc_nids[n];
-               int num_conns = snd_hda_get_conn_list(codec, cap, NULL);
+               int num_conns = snd_hda_get_num_conns(codec, cap);
                for (i = 0; i < imux->num_items; i++) {
                        hda_nid_t pin = spec->imux_pins[i];
                        if (pin) {
@@ -4213,7 +4027,7 @@ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
        if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
                snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
                                         HDA_AMP_MUTE, 0);
-       } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) {
+       } else if (snd_hda_get_num_conns(codec, cap) > 1) {
                snd_hda_codec_write_cache(codec, cap, 0,
                                          AC_VERB_SET_CONNECT_SEL, idx);
        }
@@ -4427,6 +4241,25 @@ static int alc_parse_auto_config(struct hda_codec *codec,
        return 1;
 }
 
+/* common preparation job for alc_spec */
+static int alc_alloc_spec(struct hda_codec *codec, hda_nid_t mixer_nid)
+{
+       struct alc_spec *spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       int err;
+
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+       spec->mixer_nid = mixer_nid;
+
+       err = alc_codec_rename_from_preset(codec);
+       if (err < 0) {
+               kfree(spec);
+               return err;
+       }
+       return 0;
+}
+
 static int alc880_parse_auto_config(struct hda_codec *codec)
 {
        static const hda_nid_t alc880_ignore[] = { 0x1d, 0 };
@@ -4808,13 +4641,11 @@ static int patch_alc880(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
        spec->need_dac_fix = 1;
 
        alc_pick_fixup(codec, alc880_fixup_models, alc880_fixup_tbl,
@@ -4890,7 +4721,7 @@ static void alc260_fixup_gpio1_toggle(struct hda_codec *codec,
                spec->autocfg.hp_pins[0] = 0x0f; /* copy it for automute */
                snd_hda_jack_detect_enable(codec, 0x0f, ALC_HP_EVENT);
                spec->unsol_event = alc_sku_unsol_event;
-               add_verb(codec->spec, alc_gpio1_init_verbs);
+               snd_hda_gen_add_verbs(&spec->gen, alc_gpio1_init_verbs);
        }
 }
 
@@ -5001,13 +4832,11 @@ static int patch_alc260(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x07);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x07;
+       spec = codec->spec;
 
        alc_pick_fixup(codec, NULL, alc260_fixup_tbl, alc260_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5171,8 +5000,7 @@ static void alc889_fixup_mbp_vref(struct hda_codec *codec,
                val = snd_hda_codec_read(codec, nids[i], 0,
                                         AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                val |= AC_PINCTL_VREF_80;
-               snd_hda_codec_write(codec, nids[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+               snd_hda_set_pin_ctl(codec, nids[i], val);
                spec->keep_vref_in_automute = 1;
                break;
        }
@@ -5193,8 +5021,7 @@ static void alc889_fixup_imac91_vref(struct hda_codec *codec,
                val = snd_hda_codec_read(codec, nids[i], 0,
                                         AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                val |= AC_PINCTL_VREF_50;
-               snd_hda_codec_write(codec, nids[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+               snd_hda_set_pin_ctl(codec, nids[i], val);
        }
        spec->keep_vref_in_automute = 1;
 }
@@ -5225,8 +5052,8 @@ static const struct alc_fixup alc882_fixups[] = {
                }
        },
        [ALC882_FIXUP_ACER_ASPIRE_7736] = {
-               .type = ALC_FIXUP_SKU,
-               .v.sku = ALC_FIXUP_SKU_IGNORE,
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_sku_ignore,
        },
        [ALC882_FIXUP_ASUS_W90V] = {
                .type = ALC_FIXUP_PINS,
@@ -5476,13 +5303,11 @@ static int patch_alc882(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
        switch (codec->vendor_id) {
        case 0x10ec0882:
@@ -5494,10 +5319,6 @@ static int patch_alc882(struct hda_codec *codec)
                break;
        }
 
-       err = alc_codec_rename_from_preset(codec);
-       if (err < 0)
-               goto error;
-
        alc_pick_fixup(codec, alc882_fixup_models, alc882_fixup_tbl,
                       alc882_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -5621,13 +5442,11 @@ static int patch_alc262(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
 #if 0
        /* pshou 07/11/05  set a zero PCM sample to DAC when FIFO is
@@ -5710,7 +5529,7 @@ static int alc268_parse_auto_config(struct hda_codec *codec)
        if (err > 0) {
                if (!spec->no_analog && spec->autocfg.speaker_pins[0] != 0x1d) {
                        add_mixer(spec, alc268_beep_mixer);
-                       add_verb(spec, alc268_beep_init_verbs);
+                       snd_hda_gen_add_verbs(&spec->gen, alc268_beep_init_verbs);
                }
        }
        return err;
@@ -5723,13 +5542,12 @@ static int patch_alc268(struct hda_codec *codec)
        struct alc_spec *spec;
        int i, has_beep, err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
        /* ALC268 has no aa-loopback mixer */
+       err = alc_alloc_spec(codec, 0);
+       if (err < 0)
+               return err;
+
+       spec = codec->spec;
 
        /* automatic parse from the BIOS config */
        err = alc268_parse_auto_config(codec);
@@ -5946,9 +5764,7 @@ static void alc269_fixup_mic2_mute_hook(void *private_data, int enabled)
 {
        struct hda_codec *codec = private_data;
        unsigned int pinval = enabled ? 0x20 : 0x24;
-       snd_hda_codec_update_cache(codec, 0x19, 0,
-                                  AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                  pinval);
+       snd_hda_set_pin_ctl_cache(codec, 0x19, pinval);
 }
 
 static void alc269_fixup_mic2_mute(struct hda_codec *codec,
@@ -6015,8 +5831,8 @@ static const struct alc_fixup alc269_fixups[] = {
                }
        },
        [ALC269_FIXUP_SKU_IGNORE] = {
-               .type = ALC_FIXUP_SKU,
-               .v.sku = ALC_FIXUP_SKU_IGNORE,
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_sku_ignore,
        },
        [ALC269_FIXUP_ASUS_G73JW] = {
                .type = ALC_FIXUP_PINS,
@@ -6242,19 +6058,13 @@ static void alc269_fill_coef(struct hda_codec *codec)
 static int patch_alc269(struct hda_codec *codec)
 {
        struct alc_spec *spec;
-       int err = 0;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
-       spec->mixer_nid = 0x0b;
+       int err;
 
-       err = alc_codec_rename_from_preset(codec);
+       err = alc_alloc_spec(codec, 0x0b);
        if (err < 0)
-               goto error;
+               return err;
+
+       spec = codec->spec;
 
        if (codec->vendor_id == 0x10ec0269) {
                spec->codec_variant = ALC269_TYPE_ALC269VA;
@@ -6346,8 +6156,7 @@ static void alc861_fixup_asus_amp_vref_0f(struct hda_codec *codec,
        if (!(val & (AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN)))
                val |= AC_PINCTL_IN_EN;
        val |= AC_PINCTL_VREF_50;
-       snd_hda_codec_write(codec, 0x0f, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, val);
+       snd_hda_set_pin_ctl(codec, 0x0f, val);
        spec->keep_vref_in_automute = 1;
 }
 
@@ -6401,13 +6210,11 @@ static int patch_alc861(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x15);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x15;
+       spec = codec->spec;
 
        alc_pick_fixup(codec, NULL, alc861_fixup_tbl, alc861_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6504,13 +6311,11 @@ static int patch_alc861vd(struct hda_codec *codec)
        struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
        alc_pick_fixup(codec, NULL, alc861vd_fixup_tbl, alc861vd_fixups);
        alc_apply_fixup(codec, ALC_FIXUP_ACT_PRE_PROBE);
@@ -6522,7 +6327,7 @@ static int patch_alc861vd(struct hda_codec *codec)
 
        if (codec->vendor_id == 0x10ec0660) {
                /* always turn on EAPD */
-               add_verb(spec, alc660vd_eapd_verbs);
+               snd_hda_gen_add_verbs(&spec->gen, alc660vd_eapd_verbs);
        }
 
        if (!spec->no_analog) {
@@ -6635,8 +6440,8 @@ static const struct alc_fixup alc662_fixups[] = {
                }
        },
        [ALC662_FIXUP_SKU_IGNORE] = {
-               .type = ALC_FIXUP_SKU,
-               .v.sku = ALC_FIXUP_SKU_IGNORE,
+               .type = ALC_FIXUP_FUNC,
+               .v.func = alc_fixup_sku_ignore,
        },
        [ALC662_FIXUP_HP_RP5800] = {
                .type = ALC_FIXUP_PINS,
@@ -6849,25 +6654,19 @@ static const struct alc_model_fixup alc662_fixup_models[] = {
 static int patch_alc662(struct hda_codec *codec)
 {
        struct alc_spec *spec;
-       int err = 0;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (!spec)
-               return -ENOMEM;
+       int err;
 
-       codec->spec = spec;
+       err = alc_alloc_spec(codec, 0x0b);
+       if (err < 0)
+               return err;
 
-       spec->mixer_nid = 0x0b;
+       spec = codec->spec;
 
        /* handle multiple HPs as is */
        spec->parse_flags = HDA_PINCFG_NO_HP_FIXUP;
 
        alc_fix_pll_init(codec, 0x20, 0x04, 15);
 
-       err = alc_codec_rename_from_preset(codec);
-       if (err < 0)
-               goto error;
-
        if ((alc_get_coef0(codec) & (1 << 14)) &&
            codec->bus->pci->subsystem_vendor == 0x1025 &&
            spec->cdefine.platform_type == 1) {
@@ -6930,16 +6729,12 @@ static int alc680_parse_auto_config(struct hda_codec *codec)
  */
 static int patch_alc680(struct hda_codec *codec)
 {
-       struct alc_spec *spec;
        int err;
 
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (spec == NULL)
-               return -ENOMEM;
-
-       codec->spec = spec;
-
        /* ALC680 has no aa-loopback mixer */
+       err = alc_alloc_spec(codec, 0);
+       if (err < 0)
+               return err;
 
        /* automatic parse from the BIOS config */
        err = alc680_parse_auto_config(codec);
index 4742cac26aa9b4058a58220897e45c2e826dc47d..7db8228f1b882c013f4105e535af8ceaae3c1fbd 100644 (file)
@@ -36,6 +36,7 @@
 #include <sound/tlv.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_beep.h"
 #include "hda_jack.h"
 
@@ -221,6 +222,7 @@ struct sigmatel_spec {
        unsigned char aloopback_shift;
 
        /* power management */
+       unsigned int power_map_bits;
        unsigned int num_pwrs;
        const hda_nid_t *pwr_nids;
        const hda_nid_t *dac_list;
@@ -314,6 +316,9 @@ struct sigmatel_spec {
        struct hda_vmaster_mute_hook vmaster_mute;
 };
 
+#define AC_VERB_IDT_SET_POWER_MAP      0x7ec
+#define AC_VERB_IDT_GET_POWER_MAP      0xfec
+
 static const hda_nid_t stac9200_adc_nids[1] = {
         0x03,
 };
@@ -681,8 +686,7 @@ static int stac_vrefout_set(struct hda_codec *codec,
        pinctl &= ~AC_PINCTL_VREFEN;
        pinctl |= (new_vref & AC_PINCTL_VREFEN);
 
-       error = snd_hda_codec_write_cache(codec, nid, 0,
-                                       AC_VERB_SET_PIN_WIDGET_CONTROL, pinctl);
+       error = snd_hda_set_pin_ctl_cache(codec, nid, pinctl);
        if (error < 0)
                return error;
 
@@ -706,8 +710,7 @@ static unsigned int stac92xx_vref_set(struct hda_codec *codec,
        else
                pincfg |= AC_PINCTL_IN_EN;
 
-       error = snd_hda_codec_write_cache(codec, nid, 0,
-                                       AC_VERB_SET_PIN_WIDGET_CONTROL, pincfg);
+       error = snd_hda_set_pin_ctl_cache(codec, nid, pincfg);
        if (error < 0)
                return error;
        else
@@ -2505,27 +2508,10 @@ static int stac92xx_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static unsigned int stac92xx_get_default_vref(struct hda_codec *codec,
-                                       hda_nid_t nid)
-{
-       unsigned int pincap = snd_hda_query_pin_caps(codec, nid);
-       pincap = (pincap & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT;
-       if (pincap & AC_PINCAP_VREF_100)
-               return AC_PINCTL_VREF_100;
-       if (pincap & AC_PINCAP_VREF_80)
-               return AC_PINCTL_VREF_80;
-       if (pincap & AC_PINCAP_VREF_50)
-               return AC_PINCTL_VREF_50;
-       if (pincap & AC_PINCAP_VREF_GRD)
-               return AC_PINCTL_VREF_GRD;
-       return 0;
-}
-
 static void stac92xx_auto_set_pinctl(struct hda_codec *codec, hda_nid_t nid, int pin_type)
 
 {
-       snd_hda_codec_write_cache(codec, nid, 0,
-                                 AC_VERB_SET_PIN_WIDGET_CONTROL, pin_type);
+       snd_hda_set_pin_ctl_cache(codec, nid, pin_type);
 }
 
 #define stac92xx_hp_switch_info                snd_ctl_boolean_mono_info
@@ -2594,7 +2580,7 @@ static int stac92xx_dc_bias_get(struct snd_kcontrol *kcontrol,
        hda_nid_t nid = kcontrol->private_value;
        unsigned int vref = stac92xx_vref_get(codec, nid);
 
-       if (vref == stac92xx_get_default_vref(codec, nid))
+       if (vref == snd_hda_get_default_vref(codec, nid))
                ucontrol->value.enumerated.item[0] = 0;
        else if (vref == AC_PINCTL_VREF_GRD)
                ucontrol->value.enumerated.item[0] = 1;
@@ -2613,7 +2599,7 @@ static int stac92xx_dc_bias_put(struct snd_kcontrol *kcontrol,
        hda_nid_t nid = kcontrol->private_value;
 
        if (ucontrol->value.enumerated.item[0] == 0)
-               new_vref = stac92xx_get_default_vref(codec, nid);
+               new_vref = snd_hda_get_default_vref(codec, nid);
        else if (ucontrol->value.enumerated.item[0] == 1)
                new_vref = AC_PINCTL_VREF_GRD;
        else if (ucontrol->value.enumerated.item[0] == 2)
@@ -2679,7 +2665,7 @@ static int stac92xx_io_switch_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
        else {
                unsigned int pinctl = AC_PINCTL_IN_EN;
                if (io_idx) /* set VREF for mic */
-                       pinctl |= stac92xx_get_default_vref(codec, nid);
+                       pinctl |= snd_hda_get_default_vref(codec, nid);
                stac92xx_auto_set_pinctl(codec, nid, pinctl);
        }
 
@@ -2847,7 +2833,7 @@ static inline int stac92xx_add_jack_mode_control(struct hda_codec *codec,
        char name[22];
 
        if (snd_hda_get_input_pin_attr(def_conf) != INPUT_PIN_ATTR_INT) {
-               if (stac92xx_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
+               if (snd_hda_get_default_vref(codec, nid) == AC_PINCTL_VREF_GRD
                        && nid == spec->line_switch)
                        control = STAC_CTL_WIDGET_IO_SWITCH;
                else if (snd_hda_query_pin_caps(codec, nid)
@@ -4250,13 +4236,6 @@ static void stac_store_hints(struct hda_codec *codec)
        val = snd_hda_get_bool_hint(codec, "eapd_switch");
        if (val >= 0)
                spec->eapd_switch = val;
-       get_int_hint(codec, "gpio_led_polarity", &spec->gpio_led_polarity);
-       if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
-               spec->gpio_mask |= spec->gpio_led;
-               spec->gpio_dir |= spec->gpio_led;
-               if (spec->gpio_led_polarity)
-                       spec->gpio_data |= spec->gpio_led;
-       }
 }
 
 static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
@@ -4354,7 +4333,7 @@ static int stac92xx_init(struct hda_codec *codec)
                unsigned int pinctl, conf;
                if (type == AUTO_PIN_MIC) {
                        /* for mic pins, force to initialize */
-                       pinctl = stac92xx_get_default_vref(codec, nid);
+                       pinctl = snd_hda_get_default_vref(codec, nid);
                        pinctl |= AC_PINCTL_IN_EN;
                        stac92xx_auto_set_pinctl(codec, nid, pinctl);
                } else {
@@ -4390,10 +4369,18 @@ static int stac92xx_init(struct hda_codec *codec)
                hda_nid_t nid = spec->pwr_nids[i];
                int pinctl, def_conf;
 
+               def_conf = snd_hda_codec_get_pincfg(codec, nid);
+               def_conf = get_defcfg_connect(def_conf);
+               if (def_conf == AC_JACK_PORT_NONE) {
+                       /* power off unused ports */
+                       stac_toggle_power_map(codec, nid, 0);
+                       continue;
+               }
                /* power on when no jack detection is available */
                /* or when the VREF is used for controlling LED */
                if (!spec->hp_detect ||
-                   spec->vref_mute_led_nid == nid) {
+                   spec->vref_mute_led_nid == nid ||
+                   !is_jack_detectable(codec, nid)) {
                        stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
@@ -4411,15 +4398,6 @@ static int stac92xx_init(struct hda_codec *codec)
                        stac_toggle_power_map(codec, nid, 1);
                        continue;
                }
-               def_conf = snd_hda_codec_get_pincfg(codec, nid);
-               def_conf = get_defcfg_connect(def_conf);
-               /* skip any ports that don't have jacks since presence
-                * detection is useless */
-               if (def_conf != AC_JACK_PORT_COMPLEX) {
-                       if (def_conf != AC_JACK_PORT_NONE)
-                               stac_toggle_power_map(codec, nid, 1);
-                       continue;
-               }
                if (enable_pin_detect(codec, nid, STAC_PWR_EVENT)) {
                        stac_issue_unsol_event(codec, nid);
                        continue;
@@ -4432,6 +4410,12 @@ static int stac92xx_init(struct hda_codec *codec)
 
        /* sync mute LED */
        snd_hda_sync_vmaster_hook(&spec->vmaster_mute);
+
+       /* sync the power-map */
+       if (spec->num_pwrs)
+               snd_hda_codec_write(codec, codec->afg, 0,
+                                   AC_VERB_IDT_SET_POWER_MAP,
+                                   spec->power_map_bits);
        if (spec->dac_list)
                stac92xx_power_down(codec);
        return 0;
@@ -4460,8 +4444,7 @@ static void stac92xx_shutup_pins(struct hda_codec *codec)
                struct hda_pincfg *pin = snd_array_elem(&codec->init_pins, i);
                def_conf = snd_hda_codec_get_pincfg(codec, pin->nid);
                if (get_defcfg_connect(def_conf) != AC_JACK_PORT_NONE)
-                       snd_hda_codec_write(codec, pin->nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, 0);
+                       snd_hda_set_pin_ctl(codec, pin->nid, 0);
        }
 }
 
@@ -4517,9 +4500,7 @@ static void stac92xx_set_pinctl(struct hda_codec *codec, hda_nid_t nid,
        
        pin_ctl |= flag;
        if (old_ctl != pin_ctl)
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                         pin_ctl);
+               snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl);
 }
 
 static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
@@ -4528,9 +4509,7 @@ static void stac92xx_reset_pinctl(struct hda_codec *codec, hda_nid_t nid,
        unsigned int pin_ctl = snd_hda_codec_read(codec, nid,
                        0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0x00);
        if (pin_ctl & flag)
-               snd_hda_codec_write_cache(codec, nid, 0,
-                                         AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                         pin_ctl & ~flag);
+               snd_hda_set_pin_ctl_cache(codec, nid, pin_ctl & ~flag);
 }
 
 static inline int get_pin_presence(struct hda_codec *codec, hda_nid_t nid)
@@ -4682,14 +4661,18 @@ static void stac_toggle_power_map(struct hda_codec *codec, hda_nid_t nid,
 
        idx = 1 << idx;
 
-       val = snd_hda_codec_read(codec, codec->afg, 0, 0x0fec, 0x0) & 0xff;
+       val = spec->power_map_bits;
        if (enable)
                val &= ~idx;
        else
                val |= idx;
 
        /* power down unused output ports */
-       snd_hda_codec_write(codec, codec->afg, 0, 0x7ec, val);
+       if (val != spec->power_map_bits) {
+               spec->power_map_bits = val;
+               snd_hda_codec_write(codec, codec->afg, 0,
+                                   AC_VERB_IDT_SET_POWER_MAP, val);
+       }
 }
 
 static void stac92xx_pin_sense(struct hda_codec *codec, hda_nid_t nid)
@@ -4866,6 +4849,11 @@ static int find_mute_led_cfg(struct hda_codec *codec, int default_polarity)
        struct sigmatel_spec *spec = codec->spec;
        const struct dmi_device *dev = NULL;
 
+       if (get_int_hint(codec, "gpio_led", &spec->gpio_led)) {
+               get_int_hint(codec, "gpio_led_polarity",
+                            &spec->gpio_led_polarity);
+               return 1;
+       }
        if ((codec->subsystem_id >> 16) == PCI_VENDOR_ID_HP) {
                while ((dev = dmi_find_device(DMI_DEV_TYPE_OEM_STRING,
                                                                NULL, dev))) {
@@ -4952,7 +4940,8 @@ static void stac92hd_proc_hook(struct snd_info_buffer *buffer,
 {
        if (nid == codec->afg)
                snd_iprintf(buffer, "Power-Map: 0x%02x\n", 
-                           snd_hda_codec_read(codec, nid, 0, 0x0fec, 0x0));
+                           snd_hda_codec_read(codec, nid, 0,
+                                              AC_VERB_IDT_GET_POWER_MAP, 0));
 }
 
 static void analog_loop_proc_hook(struct snd_info_buffer *buffer,
@@ -5009,20 +4998,6 @@ static int stac92xx_suspend(struct hda_codec *codec, pm_message_t state)
        return 0;
 }
 
-static int stac92xx_pre_resume(struct hda_codec *codec)
-{
-       struct sigmatel_spec *spec = codec->spec;
-
-       /* sync mute LED */
-       if (spec->vref_mute_led_nid)
-               stac_vrefout_set(codec, spec->vref_mute_led_nid,
-                                spec->vref_led);
-       else if (spec->gpio_led)
-               stac_gpio_set(codec, spec->gpio_mask,
-                             spec->gpio_dir, spec->gpio_data);
-       return 0;
-}
-
 static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
                                unsigned int power_state)
 {
@@ -5046,7 +5021,6 @@ static void stac92xx_set_power_state(struct hda_codec *codec, hda_nid_t fg,
 #else
 #define stac92xx_suspend       NULL
 #define stac92xx_resume                NULL
-#define stac92xx_pre_resume    NULL
 #define stac92xx_set_power_state NULL
 #endif /* CONFIG_PM */
 
@@ -5592,9 +5566,6 @@ again:
                        codec->patch_ops.set_power_state =
                                        stac92xx_set_power_state;
                }
-#ifdef CONFIG_PM
-               codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
        }
 
        err = stac92xx_parse_auto_config(codec);
@@ -5901,9 +5872,6 @@ again:
                        codec->patch_ops.set_power_state =
                                        stac92xx_set_power_state;
                }
-#ifdef CONFIG_PM
-               codec->patch_ops.pre_resume = stac92xx_pre_resume;
-#endif
        }
 
        spec->multiout.dac_nids = spec->dac_nids;
index 06214fdc9486d2c5d1d6c59eca22325330c5c3f3..82b368068e08a57eb2785ca1dea23ca420b152e9 100644 (file)
@@ -54,6 +54,7 @@
 #include <sound/asoundef.h>
 #include "hda_codec.h"
 #include "hda_local.h"
+#include "hda_auto_parser.h"
 #include "hda_jack.h"
 
 /* Pin Widget NID */
@@ -484,7 +485,7 @@ static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
 
        if (!path)
                return;
-       num = snd_hda_get_conn_list(codec, mix_nid, NULL);
+       num = snd_hda_get_num_conns(codec, mix_nid);
        for (i = 0; i < num; i++) {
                if (i == idx)
                        val = AMP_IN_UNMUTE(i);
@@ -532,8 +533,7 @@ static void init_output_pin(struct hda_codec *codec, hda_nid_t pin,
 {
        if (!pin)
                return;
-       snd_hda_codec_write(codec, pin, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           pin_type);
+       snd_hda_set_pin_ctl(codec, pin, pin_type);
        if (snd_hda_query_pin_caps(codec, pin) & AC_PINCAP_EAPD)
                snd_hda_codec_write(codec, pin, 0,
                                    AC_VERB_SET_EAPD_BTLENABLE, 0x02);
@@ -662,12 +662,12 @@ static void via_auto_init_analog_input(struct hda_codec *codec)
                hda_nid_t nid = cfg->inputs[i].pin;
                if (spec->smart51_enabled && is_smart51_pins(codec, nid))
                        ctl = PIN_OUT;
-               else if (cfg->inputs[i].type == AUTO_PIN_MIC)
-                       ctl = PIN_VREF50;
-               else
+               else {
                        ctl = PIN_IN;
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, ctl);
+                       if (cfg->inputs[i].type == AUTO_PIN_MIC)
+                               ctl |= snd_hda_get_default_vref(codec, nid);
+               }
+               snd_hda_set_pin_ctl(codec, nid, ctl);
        }
 
        /* init input-src */
@@ -1006,9 +1006,7 @@ static int via_smart51_put(struct snd_kcontrol *kcontrol,
                                          AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
                parm &= ~(AC_PINCTL_IN_EN | AC_PINCTL_OUT_EN);
                parm |= out_in;
-               snd_hda_codec_write(codec, nid, 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   parm);
+               snd_hda_set_pin_ctl(codec, nid, parm);
                if (out_in == AC_PINCTL_OUT_EN) {
                        mute_aa_path(codec, 1);
                        notify_aa_path_ctls(codec);
@@ -1647,8 +1645,7 @@ static void toggle_output_mutes(struct hda_codec *codec, int num_pins,
                        parm &= ~AC_PINCTL_OUT_EN;
                else
                        parm |= AC_PINCTL_OUT_EN;
-               snd_hda_codec_write(codec, pins[i], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL, parm);
+               snd_hda_set_pin_ctl(codec, pins[i], parm);
        }
 }
 
@@ -1709,8 +1706,7 @@ static void via_gpio_control(struct hda_codec *codec)
 
        if (gpio_data == 0x02) {
                /* unmute line out */
-               snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
+               snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0],
                                    PIN_OUT);
                if (vol_counter & 0x20) {
                        /* decrease volume */
@@ -1728,9 +1724,7 @@ static void via_gpio_control(struct hda_codec *codec)
                }
        } else if (!(gpio_data & 0x02)) {
                /* mute line out */
-               snd_hda_codec_write(codec, spec->autocfg.line_out_pins[0], 0,
-                                   AC_VERB_SET_PIN_WIDGET_CONTROL,
-                                   0);
+               snd_hda_set_pin_ctl(codec, spec->autocfg.line_out_pins[0], 0);
        }
 }
 
@@ -2757,8 +2751,7 @@ static void via_auto_init_dig_in(struct hda_codec *codec)
        struct via_spec *spec = codec->spec;
        if (!spec->dig_in_nid)
                return;
-       snd_hda_codec_write(codec, spec->autocfg.dig_in_pin, 0,
-                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN);
+       snd_hda_set_pin_ctl(codec, spec->autocfg.dig_in_pin, PIN_IN);
 }
 
 /* initialize the unsolicited events */
index 132a86e09d07d69adb6f7e5c7516207205f44c13..5be2e120a14e25a03744b873e690f765eecb9545 100644 (file)
@@ -2803,22 +2803,11 @@ static void __devexit snd_ice1712_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ice1712_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_ice1712_ids,
        .probe = snd_ice1712_probe,
        .remove = __devexit_p(snd_ice1712_remove),
 };
 
-static int __init alsa_card_ice1712_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1712_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1712_init)
-module_exit(alsa_card_ice1712_exit)
+module_pci_driver(ice1712_driver);
index 812d10e43ae0aa270517b9204be80a2c64200ff6..a01a00d1cf4db3ab1184beba254a3f20cb4e3634 100644 (file)
@@ -2873,7 +2873,7 @@ static int snd_vt1724_resume(struct pci_dev *pci)
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver vt1724_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_vt1724_ids,
        .probe = snd_vt1724_probe,
@@ -2884,15 +2884,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_ice1724_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ice1724_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ice1724_init)
-module_exit(alsa_card_ice1724_exit)
+module_pci_driver(vt1724_driver);
index e0a4263baa2028f995b45741745dd703e2191875..f4e2dd4da8cf6e2c5e72b45d88e143a9d19783bb 100644 (file)
@@ -3338,7 +3338,7 @@ static void __devexit snd_intel8x0_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver intel8x0_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_intel8x0_ids,
        .probe = snd_intel8x0_probe,
@@ -3349,16 +3349,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-
-static int __init alsa_card_intel8x0_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0_init)
-module_exit(alsa_card_intel8x0_exit)
+module_pci_driver(intel8x0_driver);
index d689913a61be11ba1a2462ab11b8c4e54aac0735..fc27a6a69e77d8aeb45a9a46fda2d319fa1c19fc 100644 (file)
@@ -1324,7 +1324,7 @@ static void __devexit snd_intel8x0m_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver intel8x0m_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_intel8x0m_ids,
        .probe = snd_intel8x0m_probe,
@@ -1335,16 +1335,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-
-static int __init alsa_card_intel8x0m_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_intel8x0m_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_intel8x0m_init)
-module_exit(alsa_card_intel8x0m_exit)
+module_pci_driver(intel8x0m_driver);
index 8fea45ab5882b241237bdc834205e687b651666b..e69ce5f9c31e036a9f4229d76fe9d8c21d9cefe8 100644 (file)
@@ -2476,22 +2476,11 @@ static void __devexit snd_korg1212_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver korg1212_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_korg1212_ids,
        .probe = snd_korg1212_probe,
        .remove = __devexit_p(snd_korg1212_remove),
 };
 
-static int __init alsa_card_korg1212_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_korg1212_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_korg1212_init)
-module_exit(alsa_card_korg1212_exit)
+module_pci_driver(korg1212_driver);
index 375982736858fed1d27f3a848abce3498d581a25..ac15166bee6889477d5cc4c005adc8979c6b7ee5 100644 (file)
@@ -770,22 +770,11 @@ static DEFINE_PCI_DEVICE_TABLE(lola_ids) = {
 MODULE_DEVICE_TABLE(pci, lola_ids);
 
 /* pci_driver definition */
-static struct pci_driver driver = {
+static struct pci_driver lola_driver = {
        .name = KBUILD_MODNAME,
        .id_table = lola_ids,
        .probe = lola_probe,
        .remove = __devexit_p(lola_remove),
 };
 
-static int __init alsa_card_lola_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_lola_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_lola_init)
-module_exit(alsa_card_lola_exit)
+module_pci_driver(lola_driver);
index d94c0c292bd08c2753f4dabc714bd31c5bbf9239..d1ab43706735fb42d12cc8514b48b9add557b5c2 100644 (file)
@@ -1141,24 +1141,11 @@ static void __devexit snd_lx6464es_remove(struct pci_dev *pci)
 }
 
 
-static struct pci_driver driver = {
+static struct pci_driver lx6464es_driver = {
        .name =     KBUILD_MODNAME,
        .id_table = snd_lx6464es_ids,
        .probe =    snd_lx6464es_probe,
        .remove = __devexit_p(snd_lx6464es_remove),
 };
 
-
-/* module initialization */
-static int __init mod_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit mod_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(mod_init);
-module_exit(mod_exit);
+module_pci_driver(lx6464es_driver);
index 78229b0dad2bcbfcfdcafd4f18c40b775a133498..deef213995869964791ddf53d03198019cfb1db0 100644 (file)
@@ -2837,7 +2837,7 @@ static void __devexit snd_m3_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver m3_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_m3_ids,
        .probe = snd_m3_probe,
@@ -2848,15 +2848,4 @@ static struct pci_driver driver = {
 #endif
 };
        
-static int __init alsa_card_m3_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_m3_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_m3_init)
-module_exit(alsa_card_m3_exit)
+module_pci_driver(m3_driver);
index 487837c01c9ff3ae206e30cbd804e02c5de1725e..0762610c99c0e7046f490d0d71e9a304a9e940c7 100644 (file)
@@ -1380,22 +1380,11 @@ static void __devexit snd_mixart_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver mixart_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_mixart_ids,
        .probe = snd_mixart_probe,
        .remove = __devexit_p(snd_mixart_remove),
 };
 
-static int __init alsa_card_mixart_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_mixart_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_mixart_init)
-module_exit(alsa_card_mixart_exit)
+module_pci_driver(mixart_driver);
index ade2c64bd606f9ba84eab2c84ab0ebb841f7b146..8159b05ee94d4a10a41988597891c8765ba50c2f 100644 (file)
@@ -1742,7 +1742,7 @@ static void __devexit snd_nm256_remove(struct pci_dev *pci)
 }
 
 
-static struct pci_driver driver = {
+static struct pci_driver nm256_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_nm256_ids,
        .probe = snd_nm256_probe,
@@ -1753,16 +1753,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-
-static int __init alsa_card_nm256_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_nm256_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_nm256_init)
-module_exit(alsa_card_nm256_exit)
+module_pci_driver(nm256_driver);
index eab663eef1170dd22f508c6f589a2f17f82f35de..610275bfbaeb2c2bae48f620f1232b7e12f027ed 100644 (file)
@@ -94,6 +94,7 @@ enum {
        MODEL_2CH_OUTPUT,
        MODEL_HG2PCI,
        MODEL_XONAR_DG,
+       MODEL_XONAR_DGX,
 };
 
 static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
@@ -109,6 +110,8 @@ static DEFINE_PCI_DEVICE_TABLE(oxygen_ids) = {
        { OXYGEN_PCI_SUBID(0x1a58, 0x0910), .driver_data = MODEL_CMEDIA_REF },
        /* Asus Xonar DG */
        { OXYGEN_PCI_SUBID(0x1043, 0x8467), .driver_data = MODEL_XONAR_DG },
+       /* Asus Xonar DGX */
+       { OXYGEN_PCI_SUBID(0x1043, 0x8521), .driver_data = MODEL_XONAR_DGX },
        /* PCI 2.0 HD Audio */
        { OXYGEN_PCI_SUBID(0x13f6, 0x8782), .driver_data = MODEL_2CH_OUTPUT },
        /* Kuroutoshikou CMI8787-HG2PCI */
@@ -827,6 +830,11 @@ static int __devinit get_oxygen_model(struct oxygen *chip,
                break;
        case MODEL_XONAR_DG:
                chip->model = model_xonar_dg;
+               chip->model.shortname = "Xonar DG";
+               break;
+       case MODEL_XONAR_DGX:
+               chip->model = model_xonar_dg;
+               chip->model.shortname = "Xonar DGX";
                break;
        }
        if (id->driver_data == MODEL_MERIDIAN ||
@@ -870,15 +878,4 @@ static struct pci_driver oxygen_driver = {
 #endif
 };
 
-static int __init alsa_card_oxygen_init(void)
-{
-       return pci_register_driver(&oxygen_driver);
-}
-
-static void __exit alsa_card_oxygen_exit(void)
-{
-       pci_unregister_driver(&oxygen_driver);
-}
-
-module_init(alsa_card_oxygen_init)
-module_exit(alsa_card_oxygen_exit)
+module_pci_driver(oxygen_driver);
index 3fdee4950174597db4abaf93e11192e95e2d2f65..19962c6d38c3caf3850d5a71d4bb70307d301276 100644 (file)
@@ -100,15 +100,4 @@ static struct pci_driver xonar_driver = {
        .shutdown = oxygen_pci_shutdown,
 };
 
-static int __init alsa_card_xonar_init(void)
-{
-       return pci_register_driver(&xonar_driver);
-}
-
-static void __exit alsa_card_xonar_exit(void)
-{
-       pci_unregister_driver(&xonar_driver);
-}
-
-module_init(alsa_card_xonar_init)
-module_exit(alsa_card_xonar_exit)
+module_pci_driver(xonar_driver);
index 793bdf03d7e07cdc700c93915356896936993806..77acd790ea4796b94c779fd72c1a8be861c4aa9e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * card driver for the Xonar DG
+ * card driver for the Xonar DG/DGX
  *
  * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
  *
@@ -17,8 +17,8 @@
  */
 
 /*
- * Xonar DG
- * --------
+ * Xonar DG/DGX
+ * ------------
  *
  * CMI8788:
  *
@@ -581,7 +581,6 @@ static void dump_cs4245_registers(struct oxygen *chip,
 }
 
 struct oxygen_model model_xonar_dg = {
-       .shortname = "Xonar DG",
        .longname = "C-Media Oxygen HD Audio",
        .chip = "CMI8786",
        .init = dg_init,
index fd1809ab73b4e4c3a934e895a4039a32f8cc2ba6..0435f45e95132d59583a09c9d72a25f19d536408 100644 (file)
@@ -1607,22 +1607,11 @@ static void __devexit pcxhr_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver pcxhr_driver = {
        .name = KBUILD_MODNAME,
        .id_table = pcxhr_ids,
        .probe = pcxhr_probe,
        .remove = __devexit_p(pcxhr_remove),
 };
 
-static int __init pcxhr_module_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit pcxhr_module_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(pcxhr_module_init)
-module_exit(pcxhr_module_exit)
+module_pci_driver(pcxhr_driver);
index 0481d94aac9b9e3027467aa744861d947c5373cc..cbeb3f77350c6e42ff8861c7304a3b5b4957920c 100644 (file)
@@ -1837,8 +1837,7 @@ static int snd_riptide_free(struct snd_riptide *chip)
        }
        if (chip->irq >= 0)
                free_irq(chip->irq, chip);
-       if (chip->fw_entry)
-               release_firmware(chip->fw_entry);
+       release_firmware(chip->fw_entry);
        release_and_free_resource(chip->res_port);
        kfree(chip);
        return 0;
index b4819d5e41dbe04be2f707ead5f7daecb015a281..46b3629dda22d3d9fbd33f281b2dc13c79815f56 100644 (file)
@@ -1984,22 +1984,11 @@ static void __devexit snd_rme32_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver rme32_driver = {
        .name =         KBUILD_MODNAME,
        .id_table =     snd_rme32_ids,
        .probe =        snd_rme32_probe,
        .remove =       __devexit_p(snd_rme32_remove),
 };
 
-static int __init alsa_card_rme32_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme32_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme32_init)
-module_exit(alsa_card_rme32_exit)
+module_pci_driver(rme32_driver);
index ba894158e76ceaa62fc335ae8a30a69d28de1b16..9b98dc406988386b62a6c9f07e31181f98d2bb29 100644 (file)
@@ -2395,22 +2395,11 @@ static void __devexit snd_rme96_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver rme96_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_rme96_ids,
        .probe = snd_rme96_probe,
        .remove = __devexit_p(snd_rme96_remove),
 };
 
-static int __init alsa_card_rme96_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_rme96_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_rme96_init)
-module_exit(alsa_card_rme96_exit)
+module_pci_driver(rme96_driver);
index 0b2aea2ce1729086db78b38b134777935d04fb00..0d6930c4f4b7bc4c2d4cecd0a074cbd30b3305bb 100644 (file)
@@ -5636,22 +5636,11 @@ static void __devexit snd_hdsp_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver hdsp_driver = {
        .name =     KBUILD_MODNAME,
        .id_table = snd_hdsp_ids,
        .probe =    snd_hdsp_probe,
        .remove = __devexit_p(snd_hdsp_remove),
 };
 
-static int __init alsa_card_hdsp_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdsp_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdsp_init)
-module_exit(alsa_card_hdsp_exit)
+module_pci_driver(hdsp_driver);
index bc030a2088da7928d0b9ebc5830fbeb743c59a37..0a5027b94714afb9600b94922f6d89140271492d 100644 (file)
@@ -6918,23 +6918,11 @@ static void __devexit snd_hdspm_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver hdspm_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_hdspm_ids,
        .probe = snd_hdspm_probe,
        .remove = __devexit_p(snd_hdspm_remove),
 };
 
-
-static int __init alsa_card_hdspm_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hdspm_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hdspm_init)
-module_exit(alsa_card_hdspm_exit)
+module_pci_driver(hdspm_driver);
index b737d1619cc764ce00137b71db68709363ae2d0b..a15fc100ab0c320808229729d00c4a55eb1d7da4 100644 (file)
@@ -2631,22 +2631,11 @@ static void __devexit snd_rme9652_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver rme9652_driver = {
        .name     = KBUILD_MODNAME,
        .id_table = snd_rme9652_ids,
        .probe    = snd_rme9652_probe,
        .remove   = __devexit_p(snd_rme9652_remove),
 };
 
-static int __init alsa_card_hammerfall_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_hammerfall_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_hammerfall_init)
-module_exit(alsa_card_hammerfall_exit)
+module_pci_driver(rme9652_driver);
index ff500a87f7694f09578d032965bdc4815c039d1c..1552642765d6eea4896da26bc715024764c49eaa 100644 (file)
@@ -1488,15 +1488,4 @@ static struct pci_driver sis7019_driver = {
 #endif
 };
 
-static int __init sis7019_init(void)
-{
-       return pci_register_driver(&sis7019_driver);
-}
-
-static void __exit sis7019_exit(void)
-{
-       pci_unregister_driver(&sis7019_driver);
-}
-
-module_init(sis7019_init);
-module_exit(sis7019_exit);
+module_pci_driver(sis7019_driver);
index 54cc802050f705b55b6675be122f7b649ed9b2c3..baa9946bedf019cb098032be10ee5df4c233cc05 100644 (file)
@@ -1530,22 +1530,11 @@ static void __devexit snd_sonic_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver sonicvibes_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_sonic_ids,
        .probe = snd_sonic_probe,
        .remove = __devexit_p(snd_sonic_remove),
 };
 
-static int __init alsa_card_sonicvibes_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_sonicvibes_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_sonicvibes_init)
-module_exit(alsa_card_sonicvibes_exit)
+module_pci_driver(sonicvibes_driver);
index 5f1def7f45e550949e547ea4fffcbded6051503b..611983ec73211f35e8ca761073a9c15803f65d96 100644 (file)
@@ -172,7 +172,7 @@ static void __devexit snd_trident_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver trident_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_trident_ids,
        .probe = snd_trident_probe,
@@ -183,15 +183,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_trident_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_trident_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_trident_init)
-module_exit(alsa_card_trident_exit)
+module_pci_driver(trident_driver);
index 75630408c6db495bf9c124a84738f57eb92c64b5..b5afab48943eeca6bcb8a6aac3836c83cafd7de8 100644 (file)
@@ -2619,7 +2619,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver via82xx_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_via82xx_ids,
        .probe = snd_via82xx_probe,
@@ -2630,15 +2630,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_via82xx_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_driver);
index 5efcbcac506a16f0e392551faa9f1d559dade231..59fd47ed0a31238e2aeb37e412cdfba591feafc5 100644 (file)
@@ -1223,7 +1223,7 @@ static void __devexit snd_via82xx_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver via82xx_modem_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_via82xx_modem_ids,
        .probe = snd_via82xx_probe,
@@ -1234,15 +1234,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_via82xx_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_via82xx_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_via82xx_init)
-module_exit(alsa_card_via82xx_exit)
+module_pci_driver(via82xx_modem_driver);
index 6a534bfe127408b6fe08d4045413508f1d0bec13..1ea1f656a5dc2e300fa48a757052e450dac6485d 100644 (file)
@@ -289,7 +289,7 @@ static int snd_vx222_resume(struct pci_dev *pci)
 }
 #endif
 
-static struct pci_driver driver = {
+static struct pci_driver vx222_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_vx222_ids,
        .probe = snd_vx222_probe,
@@ -300,15 +300,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_vx222_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_vx222_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_vx222_init)
-module_exit(alsa_card_vx222_exit)
+module_pci_driver(vx222_driver);
index 94ab728f5ca8b5a28b5a11ef9f36a723e3ebb707..9a1d01d653a7cff03b68fc64b572fb9e32376516 100644 (file)
@@ -350,7 +350,7 @@ static void __devexit snd_card_ymfpci_remove(struct pci_dev *pci)
        pci_set_drvdata(pci, NULL);
 }
 
-static struct pci_driver driver = {
+static struct pci_driver ymfpci_driver = {
        .name = KBUILD_MODNAME,
        .id_table = snd_ymfpci_ids,
        .probe = snd_card_ymfpci_probe,
@@ -361,15 +361,4 @@ static struct pci_driver driver = {
 #endif
 };
 
-static int __init alsa_card_ymfpci_init(void)
-{
-       return pci_register_driver(&driver);
-}
-
-static void __exit alsa_card_ymfpci_exit(void)
-{
-       pci_unregister_driver(&driver);
-}
-
-module_init(alsa_card_ymfpci_init)
-module_exit(alsa_card_ymfpci_exit)
+module_pci_driver(ymfpci_driver);
index b11f82b5718fdb1c49eec15d034bad31c9fd9e6f..f8b01c77b298dd2da058e049c2738d396e6b6892 100644 (file)
@@ -433,7 +433,7 @@ probe_error:
 /*
  * "driver" definition
  */
-static struct platform_driver driver = {
+static struct platform_driver sh_dac_driver = {
        .probe  = snd_sh_dac_probe,
        .remove = snd_sh_dac_remove,
        .driver = {
@@ -441,4 +441,4 @@ static struct platform_driver driver = {
        },
 };
 
-module_platform_driver(driver);
+module_platform_driver(sh_dac_driver);
index 5a537b7713a37f2d4a433dd565447aa9c76dde05..e0d45fdaa750842eb342dc9ced8116ff1a21e212 100644 (file)
@@ -565,22 +565,22 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
                        attn_tlv),
 
        SOC_SINGLE_TLV("SPK-IP Mono Volume",
-                       CS42L73_SPKMIPMA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_SPKMIPMA, 0, 0x3F, 1, attn_tlv),
        SOC_SINGLE_TLV("SPK-XSP Mono Volume",
-                       CS42L73_SPKMXSPA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_SPKMXSPA, 0, 0x3F, 1, attn_tlv),
        SOC_SINGLE_TLV("SPK-ASP Mono Volume",
-                       CS42L73_SPKMASPA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_SPKMASPA, 0, 0x3F, 1, attn_tlv),
        SOC_SINGLE_TLV("SPK-VSP Mono Volume",
-                       CS42L73_SPKMVSPMA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_SPKMVSPMA, 0, 0x3F, 1, attn_tlv),
 
        SOC_SINGLE_TLV("ESL-IP Mono Volume",
-                       CS42L73_ESLMIPMA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_ESLMIPMA, 0, 0x3F, 1, attn_tlv),
        SOC_SINGLE_TLV("ESL-XSP Mono Volume",
-                       CS42L73_ESLMXSPA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_ESLMXSPA, 0, 0x3F, 1, attn_tlv),
        SOC_SINGLE_TLV("ESL-ASP Mono Volume",
-                       CS42L73_ESLMASPA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_ESLMASPA, 0, 0x3F, 1, attn_tlv),
        SOC_SINGLE_TLV("ESL-VSP Mono Volume",
-                       CS42L73_ESLMVSPMA, 0, 0x3E, 1, attn_tlv),
+                       CS42L73_ESLMVSPMA, 0, 0x3F, 1, attn_tlv),
 
        SOC_ENUM("IP Digital Swap/Mono Select", ip_swap_enum),
 
index 2f9870aa0cf111a8efef48ee633426063e0eabe7..993639d694ce308aed067cea3003fcbf6ad2ee2b 100644 (file)
@@ -1127,7 +1127,7 @@ static int aif2clk_ev(struct snd_soc_dapm_widget *w,
                snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
                                    WM8994_AIF2DACL_ENA |
                                    WM8994_AIF2DACR_ENA, 0);
-               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_5,
+               snd_soc_update_bits(codec, WM8994_POWER_MANAGEMENT_4,
                                    WM8994_AIF2ADCL_ENA |
                                    WM8994_AIF2ADCR_ENA, 0);
 
index c6e81fb928e9acda430945fe7b0bf329f5760074..fb9255cca2146dd866496841b7784e89d690597d 100644 (file)
@@ -361,7 +361,7 @@ int register_sound_special_device(const struct file_operations *fops, int unit,
                                  struct device *dev)
 {
        const int chain = unit % SOUND_STEP;
-       int max_unit = 128 + chain;
+       int max_unit = 256;
        const char *name;
        char _name[16];
 
index 4a7be7b98331538228a49e13c81faf6b7f8243b7..d5b5c3388e28ced3cb7c287399755b56d90e1aed 100644 (file)
@@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head)
                subs = &as->substream[idx];
                if (!subs->num_formats)
                        continue;
-               snd_usb_release_substream_urbs(subs, 1);
                subs->interface = -1;
+               subs->data_endpoint = NULL;
+               subs->sync_endpoint = NULL;
        }
 }
 
@@ -276,6 +277,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
 
 static int snd_usb_audio_free(struct snd_usb_audio *chip)
 {
+       mutex_destroy(&chip->mutex);
        kfree(chip);
        return 0;
 }
@@ -336,6 +338,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
                return -ENOMEM;
        }
 
+       mutex_init(&chip->mutex);
        mutex_init(&chip->shutdown_mutex);
        chip->index = idx;
        chip->dev = dev;
@@ -348,6 +351,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
        chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
                              le16_to_cpu(dev->descriptor.idProduct));
        INIT_LIST_HEAD(&chip->pcm_list);
+       INIT_LIST_HEAD(&chip->ep_list);
        INIT_LIST_HEAD(&chip->midi_list);
        INIT_LIST_HEAD(&chip->mixer_list);
 
@@ -565,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
                list_for_each(p, &chip->pcm_list) {
                        snd_usb_stream_disconnect(p);
                }
+               /* release the endpoint resources */
+               list_for_each(p, &chip->ep_list) {
+                       snd_usb_endpoint_free(p);
+               }
                /* release the midi resources */
                list_for_each(p, &chip->midi_list) {
                        snd_usbmidi_disconnect(p);
index da5fa1ac4edaf3608c7811dea5194b98a8bab37c..0d37238b84572fed54cef1c89ee23e1d44d8ae00 100644 (file)
@@ -30,20 +30,71 @@ struct audioformat {
 };
 
 struct snd_usb_substream;
+struct snd_usb_endpoint;
 
 struct snd_urb_ctx {
        struct urb *urb;
        unsigned int buffer_size;       /* size of data buffer, if data URB */
        struct snd_usb_substream *subs;
+       struct snd_usb_endpoint *ep;
        int index;      /* index for urb array */
        int packets;    /* number of packets per urb */
+       int packet_size[MAX_PACKS_HS]; /* size of packets for next submission */
+       struct list_head ready_list;
 };
 
-struct snd_urb_ops {
-       int (*prepare)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*retire)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*prepare_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
-       int (*retire_sync)(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime, struct urb *u);
+struct snd_usb_endpoint {
+       struct snd_usb_audio *chip;
+
+       int use_count;
+       int ep_num;             /* the referenced endpoint number */
+       int type;               /* SND_USB_ENDPOINT_TYPE_* */
+       unsigned long flags;
+
+       void (*prepare_data_urb) (struct snd_usb_substream *subs,
+                                 struct urb *urb);
+       void (*retire_data_urb) (struct snd_usb_substream *subs,
+                                struct urb *urb);
+
+       struct snd_usb_substream *data_subs;
+       struct snd_usb_endpoint *sync_master;
+       struct snd_usb_endpoint *sync_slave;
+
+       struct snd_urb_ctx urb[MAX_URBS];
+
+       struct snd_usb_packet_info {
+               uint32_t packet_size[MAX_PACKS_HS];
+               int packets;
+       } next_packet[MAX_URBS];
+       int next_packet_read_pos, next_packet_write_pos;
+       struct list_head ready_playback_urbs;
+
+       unsigned int nurbs;             /* # urbs */
+       unsigned long active_mask;      /* bitmask of active urbs */
+       unsigned long unlink_mask;      /* bitmask of unlinked urbs */
+       char *syncbuf;                  /* sync buffer for all sync URBs */
+       dma_addr_t sync_dma;            /* DMA address of syncbuf */
+
+       unsigned int pipe;              /* the data i/o pipe */
+       unsigned int freqn;             /* nominal sampling rate in fs/fps in Q16.16 format */
+       unsigned int freqm;             /* momentary sampling rate in fs/fps in Q16.16 format */
+       int        freqshift;           /* how much to shift the feedback value to get Q16.16 */
+       unsigned int freqmax;           /* maximum sampling rate, used for buffer management */
+       unsigned int phase;             /* phase accumulator */
+       unsigned int maxpacksize;       /* max packet size in bytes */
+       unsigned int maxframesize;      /* max packet size in frames */
+       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
+       unsigned int curframesize;      /* current packet size in frames (for capture) */
+       unsigned int syncmaxsize;       /* sync endpoint packet size */
+       unsigned int fill_max:1;        /* fill max packet size always */
+       unsigned int datainterval;      /* log_2 of data packet interval */
+       unsigned int syncinterval;      /* P for adaptive mode, 0 otherwise */
+       unsigned char silence_value;
+       unsigned int stride;
+       int iface, alt_idx;
+
+       spinlock_t lock;
+       struct list_head list;
 };
 
 struct snd_usb_substream {
@@ -57,21 +108,6 @@ struct snd_usb_substream {
        unsigned int cur_rate;          /* current rate (for hw_params callback) */
        unsigned int period_bytes;      /* current period bytes (for hw_params callback) */
        unsigned int altset_idx;     /* USB data format: index of alternate setting */
-       unsigned int datapipe;   /* the data i/o pipe */
-       unsigned int syncpipe;   /* 1 - async out or adaptive in */
-       unsigned int datainterval;      /* log_2 of data packet interval */
-       unsigned int syncinterval;  /* P for adaptive mode, 0 otherwise */
-       unsigned int freqn;      /* nominal sampling rate in fs/fps in Q16.16 format */
-       unsigned int freqm;      /* momentary sampling rate in fs/fps in Q16.16 format */
-       int          freqshift;  /* how much to shift the feedback value to get Q16.16 */
-       unsigned int freqmax;    /* maximum sampling rate, used for buffer management */
-       unsigned int phase;      /* phase accumulator */
-       unsigned int maxpacksize;       /* max packet size in bytes */
-       unsigned int maxframesize;      /* max packet size in frames */
-       unsigned int curpacksize;       /* current packet size in bytes (for capture) */
-       unsigned int curframesize;      /* current packet size in frames (for capture) */
-       unsigned int syncmaxsize;       /* sync endpoint packet size */
-       unsigned int fill_max: 1;       /* fill max packet size always */
        unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
 
@@ -82,11 +118,10 @@ struct snd_usb_substream {
        unsigned long active_mask;      /* bitmask of active urbs */
        unsigned long unlink_mask;      /* bitmask of unlinked urbs */
 
-       unsigned int nurbs;                     /* # urbs */
-       struct snd_urb_ctx dataurb[MAX_URBS];   /* data urb table */
-       struct snd_urb_ctx syncurb[SYNC_URBS];  /* sync urb table */
-       char *syncbuf;                          /* sync buffer for all sync URBs */
-       dma_addr_t sync_dma;                    /* DMA address of syncbuf */
+       /* data and sync endpoints for this stream */
+       struct snd_usb_endpoint *data_endpoint;
+       struct snd_usb_endpoint *sync_endpoint;
+       unsigned long flags;
 
        u64 formats;                    /* format bitmasks (all or'ed) */
        unsigned int num_formats;               /* number of supported audio formats (list) */
@@ -94,7 +129,6 @@ struct snd_usb_substream {
        struct snd_pcm_hw_constraint_list rate_list;    /* limited rates */
        spinlock_t lock;
 
-       struct snd_urb_ops ops;         /* callbacks (must be filled at init) */
        int last_frame_number;          /* stored frame number */
        int last_delay;                 /* stored delay */
 };
index 08dcce53720bad66499f48709c553b04e6ea6821..e6906901debbc2aaa71c40621fbd4241d8d15663 100644 (file)
 #include <linux/ratelimit.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
+#include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "usbaudio.h"
 #include "helper.h"
 #include "endpoint.h"
 #include "pcm.h"
 
+#define EP_FLAG_ACTIVATED      0
+#define EP_FLAG_RUNNING                1
+
+/*
+ * snd_usb_endpoint is a model that abstracts everything related to an
+ * USB endpoint and its streaming.
+ *
+ * There are functions to activate and deactivate the streaming URBs and
+ * optional callbacks to let the pcm logic handle the actual content of the
+ * packets for playback and record. Thus, the bus streaming and the audio
+ * handlers are fully decoupled.
+ *
+ * There are two different types of endpoints in audio applications.
+ *
+ * SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both
+ * inbound and outbound traffic.
+ *
+ * SND_USB_ENDPOINT_TYPE_SYNC endpoints are for inbound traffic only and
+ * expect the payload to carry Q10.14 / Q16.16 formatted sync information
+ * (3 or 4 bytes).
+ *
+ * Each endpoint has to be configured prior to being used by calling
+ * snd_usb_endpoint_set_params().
+ *
+ * The model incorporates a reference counting, so that multiple users
+ * can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and
+ * only the first user will effectively start the URBs, and only the last
+ * one to stop it will tear the URBs down again.
+ */
+
 /*
  * convert a sampling rate into our full speed format (fs/1000 in Q16.16)
  * this will overflow at approx 524 kHz
@@ -49,71 +81,415 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
 }
 
 /*
- * unlink active urbs.
+ * release a urb data
  */
-static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
+static void release_urb_ctx(struct snd_urb_ctx *u)
 {
-       struct snd_usb_audio *chip = subs->stream->chip;
-       unsigned int i;
-       int async;
+       if (u->buffer_size)
+               usb_free_coherent(u->ep->chip->dev, u->buffer_size,
+                                 u->urb->transfer_buffer,
+                                 u->urb->transfer_dma);
+       usb_free_urb(u->urb);
+       u->urb = NULL;
+}
+
+static const char *usb_error_string(int err)
+{
+       switch (err) {
+       case -ENODEV:
+               return "no device";
+       case -ENOENT:
+               return "endpoint not enabled";
+       case -EPIPE:
+               return "endpoint stalled";
+       case -ENOSPC:
+               return "not enough bandwidth";
+       case -ESHUTDOWN:
+               return "device disabled";
+       case -EHOSTUNREACH:
+               return "device suspended";
+       case -EINVAL:
+       case -EAGAIN:
+       case -EFBIG:
+       case -EMSGSIZE:
+               return "internal error";
+       default:
+               return "unknown error";
+       }
+}
+
+/**
+ * snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type
+ *
+ * @ep: The snd_usb_endpoint
+ *
+ * Determine whether an endpoint is driven by an implicit feedback
+ * data endpoint source.
+ */
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep)
+{
+       return  ep->sync_master &&
+               ep->sync_master->type == SND_USB_ENDPOINT_TYPE_DATA &&
+               ep->type == SND_USB_ENDPOINT_TYPE_DATA &&
+               usb_pipeout(ep->pipe);
+}
 
-       subs->running = 0;
+/*
+ * For streaming based on information derived from sync endpoints,
+ * prepare_outbound_urb_sizes() will call next_packet_size() to
+ * determine the number of samples to be sent in the next packet.
+ *
+ * For implicit feedback, next_packet_size() is unused.
+ */
+static int next_packet_size(struct snd_usb_endpoint *ep)
+{
+       unsigned long flags;
+       int ret;
 
-       if (!force && subs->stream->chip->shutdown) /* to be sure... */
-               return -EBADFD;
+       if (ep->fill_max)
+               return ep->maxframesize;
 
-       async = !can_sleep && chip->async_unlink;
+       spin_lock_irqsave(&ep->lock, flags);
+       ep->phase = (ep->phase & 0xffff)
+               + (ep->freqm << ep->datainterval);
+       ret = min(ep->phase >> 16, ep->maxframesize);
+       spin_unlock_irqrestore(&ep->lock, flags);
 
-       if (!async && in_interrupt())
-               return 0;
+       return ret;
+}
 
-       for (i = 0; i < subs->nurbs; i++) {
-               if (test_bit(i, &subs->active_mask)) {
-                       if (!test_and_set_bit(i, &subs->unlink_mask)) {
-                               struct urb *u = subs->dataurb[i].urb;
-                               if (async)
-                                       usb_unlink_urb(u);
-                               else
-                                       usb_kill_urb(u);
+static void retire_outbound_urb(struct snd_usb_endpoint *ep,
+                               struct snd_urb_ctx *urb_ctx)
+{
+       if (ep->retire_data_urb)
+               ep->retire_data_urb(ep->data_subs, urb_ctx->urb);
+}
+
+static void retire_inbound_urb(struct snd_usb_endpoint *ep,
+                              struct snd_urb_ctx *urb_ctx)
+{
+       struct urb *urb = urb_ctx->urb;
+
+       if (ep->sync_slave)
+               snd_usb_handle_sync_urb(ep->sync_slave, ep, urb);
+
+       if (ep->retire_data_urb)
+               ep->retire_data_urb(ep->data_subs, urb);
+}
+
+static void prepare_outbound_urb_sizes(struct snd_usb_endpoint *ep,
+                                      struct snd_urb_ctx *ctx)
+{
+       int i;
+
+       for (i = 0; i < ctx->packets; ++i)
+               ctx->packet_size[i] = next_packet_size(ep);
+}
+
+/*
+ * Prepare a PLAYBACK urb for submission to the bus.
+ */
+static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
+                                struct snd_urb_ctx *ctx)
+{
+       int i;
+       struct urb *urb = ctx->urb;
+       unsigned char *cp = urb->transfer_buffer;
+
+       urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+       switch (ep->type) {
+       case SND_USB_ENDPOINT_TYPE_DATA:
+               if (ep->prepare_data_urb) {
+                       ep->prepare_data_urb(ep->data_subs, urb);
+               } else {
+                       /* no data provider, so send silence */
+                       unsigned int offs = 0;
+                       for (i = 0; i < ctx->packets; ++i) {
+                               int counts = ctx->packet_size[i];
+                               urb->iso_frame_desc[i].offset = offs * ep->stride;
+                               urb->iso_frame_desc[i].length = counts * ep->stride;
+                               offs += counts;
                        }
+
+                       urb->number_of_packets = ctx->packets;
+                       urb->transfer_buffer_length = offs * ep->stride;
+                       memset(urb->transfer_buffer, ep->silence_value,
+                              offs * ep->stride);
                }
+               break;
+
+       case SND_USB_ENDPOINT_TYPE_SYNC:
+               if (snd_usb_get_speed(ep->chip->dev) >= USB_SPEED_HIGH) {
+                       /*
+                        * fill the length and offset of each urb descriptor.
+                        * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+                        */
+                       urb->iso_frame_desc[0].length = 4;
+                       urb->iso_frame_desc[0].offset = 0;
+                       cp[0] = ep->freqn;
+                       cp[1] = ep->freqn >> 8;
+                       cp[2] = ep->freqn >> 16;
+                       cp[3] = ep->freqn >> 24;
+               } else {
+                       /*
+                        * fill the length and offset of each urb descriptor.
+                        * the fixed 10.14 frequency is passed through the pipe.
+                        */
+                       urb->iso_frame_desc[0].length = 3;
+                       urb->iso_frame_desc[0].offset = 0;
+                       cp[0] = ep->freqn >> 2;
+                       cp[1] = ep->freqn >> 10;
+                       cp[2] = ep->freqn >> 18;
+               }
+
+               break;
        }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       if (test_bit(i+16, &subs->active_mask)) {
-                               if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
-                                       struct urb *u = subs->syncurb[i].urb;
-                                       if (async)
-                                               usb_unlink_urb(u);
-                                       else
-                                               usb_kill_urb(u);
-                               }
-                       }
+}
+
+/*
+ * Prepare a CAPTURE or SYNC urb for submission to the bus.
+ */
+static inline void prepare_inbound_urb(struct snd_usb_endpoint *ep,
+                                      struct snd_urb_ctx *urb_ctx)
+{
+       int i, offs;
+       struct urb *urb = urb_ctx->urb;
+
+       urb->dev = ep->chip->dev; /* we need to set this at each time */
+
+       switch (ep->type) {
+       case SND_USB_ENDPOINT_TYPE_DATA:
+               offs = 0;
+               for (i = 0; i < urb_ctx->packets; i++) {
+                       urb->iso_frame_desc[i].offset = offs;
+                       urb->iso_frame_desc[i].length = ep->curpacksize;
+                       offs += ep->curpacksize;
                }
+
+               urb->transfer_buffer_length = offs;
+               urb->number_of_packets = urb_ctx->packets;
+               break;
+
+       case SND_USB_ENDPOINT_TYPE_SYNC:
+               urb->iso_frame_desc[0].length = min(4u, ep->syncmaxsize);
+               urb->iso_frame_desc[0].offset = 0;
+               break;
        }
-       return 0;
 }
 
+/*
+ * Send output urbs that have been prepared previously. URBs are dequeued
+ * from ep->ready_playback_urbs and in case there there aren't any available
+ * or there are no packets that have been prepared, this function does
+ * nothing.
+ *
+ * The reason why the functionality of sending and preparing URBs is separated
+ * is that host controllers don't guarantee the order in which they return
+ * inbound and outbound packets to their submitters.
+ *
+ * This function is only used for implicit feedback endpoints. For endpoints
+ * driven by dedicated sync endpoints, URBs are immediately re-submitted
+ * from their completion handler.
+ */
+static void queue_pending_output_urbs(struct snd_usb_endpoint *ep)
+{
+       while (test_bit(EP_FLAG_RUNNING, &ep->flags)) {
+
+               unsigned long flags;
+               struct snd_usb_packet_info *uninitialized_var(packet);
+               struct snd_urb_ctx *ctx = NULL;
+               struct urb *urb;
+               int err, i;
+
+               spin_lock_irqsave(&ep->lock, flags);
+               if (ep->next_packet_read_pos != ep->next_packet_write_pos) {
+                       packet = ep->next_packet + ep->next_packet_read_pos;
+                       ep->next_packet_read_pos++;
+                       ep->next_packet_read_pos %= MAX_URBS;
+
+                       /* take URB out of FIFO */
+                       if (!list_empty(&ep->ready_playback_urbs))
+                               ctx = list_first_entry(&ep->ready_playback_urbs,
+                                              struct snd_urb_ctx, ready_list);
+               }
+               spin_unlock_irqrestore(&ep->lock, flags);
+
+               if (ctx == NULL)
+                       return;
+
+               list_del_init(&ctx->ready_list);
+               urb = ctx->urb;
+
+               /* copy over the length information */
+               for (i = 0; i < packet->packets; i++)
+                       ctx->packet_size[i] = packet->packet_size[i];
+
+               /* call the data handler to fill in playback data */
+               prepare_outbound_urb(ep, ctx);
+
+               err = usb_submit_urb(ctx->urb, GFP_ATOMIC);
+               if (err < 0)
+                       snd_printk(KERN_ERR "Unable to submit urb #%d: %d (urb %p)\n",
+                                  ctx->index, err, ctx->urb);
+               else
+                       set_bit(ctx->index, &ep->active_mask);
+       }
+}
 
 /*
- * release a urb data
+ * complete callback for urbs
  */
-static void release_urb_ctx(struct snd_urb_ctx *u)
+static void snd_complete_urb(struct urb *urb)
+{
+       struct snd_urb_ctx *ctx = urb->context;
+       struct snd_usb_endpoint *ep = ctx->ep;
+       int err;
+
+       if (unlikely(urb->status == -ENOENT ||          /* unlinked */
+                    urb->status == -ENODEV ||          /* device removed */
+                    urb->status == -ECONNRESET ||      /* unlinked */
+                    urb->status == -ESHUTDOWN ||       /* device disabled */
+                    ep->chip->shutdown))               /* device disconnected */
+               goto exit_clear;
+
+       if (usb_pipeout(ep->pipe)) {
+               retire_outbound_urb(ep, ctx);
+               /* can be stopped during retire callback */
+               if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+                       goto exit_clear;
+
+               if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+                       unsigned long flags;
+
+                       spin_lock_irqsave(&ep->lock, flags);
+                       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+                       spin_unlock_irqrestore(&ep->lock, flags);
+                       queue_pending_output_urbs(ep);
+
+                       goto exit_clear;
+               }
+
+               prepare_outbound_urb_sizes(ep, ctx);
+               prepare_outbound_urb(ep, ctx);
+       } else {
+               retire_inbound_urb(ep, ctx);
+               /* can be stopped during retire callback */
+               if (unlikely(!test_bit(EP_FLAG_RUNNING, &ep->flags)))
+                       goto exit_clear;
+
+               prepare_inbound_urb(ep, ctx);
+       }
+
+       err = usb_submit_urb(urb, GFP_ATOMIC);
+       if (err == 0)
+               return;
+
+       snd_printk(KERN_ERR "cannot submit urb (err = %d)\n", err);
+       //snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+
+exit_clear:
+       clear_bit(ctx->index, &ep->active_mask);
+}
+
+/**
+ * snd_usb_add_endpoint: Add an endpoint to an USB audio chip
+ *
+ * @chip: The chip
+ * @alts: The USB host interface
+ * @ep_num: The number of the endpoint to use
+ * @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
+ * @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC
+ *
+ * If the requested endpoint has not been added to the given chip before,
+ * a new instance is created. Otherwise, a pointer to the previoulsy
+ * created instance is returned. In case of any error, NULL is returned.
+ *
+ * New endpoints will be added to chip->ep_list and must be freed by
+ * calling snd_usb_endpoint_free().
+ */
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+                                             struct usb_host_interface *alts,
+                                             int ep_num, int direction, int type)
 {
-       if (u->urb) {
-               if (u->buffer_size)
-                       usb_free_coherent(u->subs->dev, u->buffer_size,
-                                       u->urb->transfer_buffer,
-                                       u->urb->transfer_dma);
-               usb_free_urb(u->urb);
-               u->urb = NULL;
+       struct list_head *p;
+       struct snd_usb_endpoint *ep;
+       int ret, is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
+
+       mutex_lock(&chip->mutex);
+
+       list_for_each(p, &chip->ep_list) {
+               ep = list_entry(p, struct snd_usb_endpoint, list);
+               if (ep->ep_num == ep_num &&
+                   ep->iface == alts->desc.bInterfaceNumber &&
+                   ep->alt_idx == alts->desc.bAlternateSetting) {
+                       snd_printdd(KERN_DEBUG "Re-using EP %x in iface %d,%d @%p\n",
+                                       ep_num, ep->iface, ep->alt_idx, ep);
+                       goto __exit_unlock;
+               }
+       }
+
+       snd_printdd(KERN_DEBUG "Creating new %s %s endpoint #%x\n",
+                   is_playback ? "playback" : "capture",
+                   type == SND_USB_ENDPOINT_TYPE_DATA ? "data" : "sync",
+                   ep_num);
+
+       /* select the alt setting once so the endpoints become valid */
+       ret = usb_set_interface(chip->dev, alts->desc.bInterfaceNumber,
+                               alts->desc.bAlternateSetting);
+       if (ret < 0) {
+               snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+                                       __func__, ret);
+               ep = NULL;
+               goto __exit_unlock;
        }
+
+       ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+       if (!ep)
+               goto __exit_unlock;
+
+       ep->chip = chip;
+       spin_lock_init(&ep->lock);
+       ep->type = type;
+       ep->ep_num = ep_num;
+       ep->iface = alts->desc.bInterfaceNumber;
+       ep->alt_idx = alts->desc.bAlternateSetting;
+       INIT_LIST_HEAD(&ep->ready_playback_urbs);
+       ep_num &= USB_ENDPOINT_NUMBER_MASK;
+
+       if (is_playback)
+               ep->pipe = usb_sndisocpipe(chip->dev, ep_num);
+       else
+               ep->pipe = usb_rcvisocpipe(chip->dev, ep_num);
+
+       if (type == SND_USB_ENDPOINT_TYPE_SYNC) {
+               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+                   get_endpoint(alts, 1)->bRefresh >= 1 &&
+                   get_endpoint(alts, 1)->bRefresh <= 9)
+                       ep->syncinterval = get_endpoint(alts, 1)->bRefresh;
+               else if (snd_usb_get_speed(chip->dev) == USB_SPEED_FULL)
+                       ep->syncinterval = 1;
+               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
+                        get_endpoint(alts, 1)->bInterval <= 16)
+                       ep->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
+               else
+                       ep->syncinterval = 3;
+
+               ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
+       }
+
+       list_add_tail(&ep->list, &chip->ep_list);
+
+__exit_unlock:
+       mutex_unlock(&chip->mutex);
+
+       return ep;
 }
 
 /*
  *  wait until all urbs are processed.
  */
-static int wait_clear_urbs(struct snd_usb_substream *subs)
+static int wait_clear_urbs(struct snd_usb_endpoint *ep)
 {
        unsigned long end_time = jiffies + msecs_to_jiffies(1000);
        unsigned int i;
@@ -121,153 +497,148 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
 
        do {
                alive = 0;
-               for (i = 0; i < subs->nurbs; i++) {
-                       if (test_bit(i, &subs->active_mask))
+               for (i = 0; i < ep->nurbs; i++)
+                       if (test_bit(i, &ep->active_mask))
                                alive++;
-               }
-               if (subs->syncpipe) {
-                       for (i = 0; i < SYNC_URBS; i++) {
-                               if (test_bit(i + 16, &subs->active_mask))
-                                       alive++;
-                       }
-               }
-               if (! alive)
+
+               if (!alive)
                        break;
+
                schedule_timeout_uninterruptible(1);
        } while (time_before(jiffies, end_time));
+
        if (alive)
-               snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
+               snd_printk(KERN_ERR "timeout: still %d active urbs on EP #%x\n",
+                                       alive, ep->ep_num);
+
        return 0;
 }
 
 /*
- * release a substream
+ * unlink active urbs.
  */
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
+static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep)
 {
-       int i;
+       unsigned int i;
+       int async;
 
-       /* stop urbs (to be sure) */
-       deactivate_urbs(subs, force, 1);
-       wait_clear_urbs(subs);
-
-       for (i = 0; i < MAX_URBS; i++)
-               release_urb_ctx(&subs->dataurb[i]);
-       for (i = 0; i < SYNC_URBS; i++)
-               release_urb_ctx(&subs->syncurb[i]);
-       usb_free_coherent(subs->dev, SYNC_URBS * 4,
-                       subs->syncbuf, subs->sync_dma);
-       subs->syncbuf = NULL;
-       subs->nurbs = 0;
-}
+       if (!force && ep->chip->shutdown) /* to be sure... */
+               return -EBADFD;
 
-/*
- * complete callback from data urb
- */
-static void snd_complete_urb(struct urb *urb)
-{
-       struct snd_urb_ctx *ctx = urb->context;
-       struct snd_usb_substream *subs = ctx->subs;
-       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-       int err = 0;
-
-       if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
-           !subs->running || /* can be stopped during retire callback */
-           (err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
-           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               clear_bit(ctx->index, &subs->active_mask);
-               if (err < 0) {
-                       snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+       async = !can_sleep && ep->chip->async_unlink;
+
+       clear_bit(EP_FLAG_RUNNING, &ep->flags);
+
+       INIT_LIST_HEAD(&ep->ready_playback_urbs);
+       ep->next_packet_read_pos = 0;
+       ep->next_packet_write_pos = 0;
+
+       if (!async && in_interrupt())
+               return 0;
+
+       for (i = 0; i < ep->nurbs; i++) {
+               if (test_bit(i, &ep->active_mask)) {
+                       if (!test_and_set_bit(i, &ep->unlink_mask)) {
+                               struct urb *u = ep->urb[i].urb;
+                               if (async)
+                                       usb_unlink_urb(u);
+                               else
+                                       usb_kill_urb(u);
+                       }
                }
        }
-}
 
+       return 0;
+}
 
 /*
- * complete callback from sync urb
+ * release an endpoint's urbs
  */
-static void snd_complete_sync_urb(struct urb *urb)
+static void release_urbs(struct snd_usb_endpoint *ep, int force)
 {
-       struct snd_urb_ctx *ctx = urb->context;
-       struct snd_usb_substream *subs = ctx->subs;
-       struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
-       int err = 0;
-
-       if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
-           !subs->running || /* can be stopped during retire callback */
-           (err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
-           (err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
-               clear_bit(ctx->index + 16, &subs->active_mask);
-               if (err < 0) {
-                       snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
-                       snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
-               }
-       }
-}
+       int i;
 
+       /* route incoming urbs to nirvana */
+       ep->retire_data_urb = NULL;
+       ep->prepare_data_urb = NULL;
+
+       /* stop urbs */
+       deactivate_urbs(ep, force, 1);
+       wait_clear_urbs(ep);
+
+       for (i = 0; i < ep->nurbs; i++)
+               release_urb_ctx(&ep->urb[i]);
+
+       if (ep->syncbuf)
+               usb_free_coherent(ep->chip->dev, SYNC_URBS * 4,
+                                 ep->syncbuf, ep->sync_dma);
+
+       ep->syncbuf = NULL;
+       ep->nurbs = 0;
+}
 
 /*
- * initialize a substream for plaback/capture
+ * configure a data endpoint
  */
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
-                               unsigned int period_bytes,
-                               unsigned int rate,
-                               unsigned int frame_bits)
+static int data_ep_set_params(struct snd_usb_endpoint *ep,
+                             struct snd_pcm_hw_params *hw_params,
+                             struct audioformat *fmt,
+                             struct snd_usb_endpoint *sync_ep)
 {
-       unsigned int maxsize, i;
-       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       unsigned int urb_packs, total_packs, packs_per_ms;
-       struct snd_usb_audio *chip = subs->stream->chip;
+       unsigned int maxsize, i, urb_packs, total_packs, packs_per_ms;
+       int period_bytes = params_period_bytes(hw_params);
+       int format = params_format(hw_params);
+       int is_playback = usb_pipeout(ep->pipe);
+       int frame_bits = snd_pcm_format_physical_width(params_format(hw_params)) *
+                                                       params_channels(hw_params);
+
+       ep->datainterval = fmt->datainterval;
+       ep->stride = frame_bits >> 3;
+       ep->silence_value = format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0;
 
-       /* calculate the frequency in 16.16 format */
-       if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-               subs->freqn = get_usb_full_speed_rate(rate);
-       else
-               subs->freqn = get_usb_high_speed_rate(rate);
-       subs->freqm = subs->freqn;
-       subs->freqshift = INT_MIN;
        /* calculate max. frequency */
-       if (subs->maxpacksize) {
+       if (ep->maxpacksize) {
                /* whatever fits into a max. size packet */
-               maxsize = subs->maxpacksize;
-               subs->freqmax = (maxsize / (frame_bits >> 3))
-                               << (16 - subs->datainterval);
+               maxsize = ep->maxpacksize;
+               ep->freqmax = (maxsize / (frame_bits >> 3))
+                               << (16 - ep->datainterval);
        } else {
                /* no max. packet size: just take 25% higher than nominal */
-               subs->freqmax = subs->freqn + (subs->freqn >> 2);
-               maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
-                               >> (16 - subs->datainterval);
+               ep->freqmax = ep->freqn + (ep->freqn >> 2);
+               maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
+                               >> (16 - ep->datainterval);
        }
-       subs->phase = 0;
 
-       if (subs->fill_max)
-               subs->curpacksize = subs->maxpacksize;
+       if (ep->fill_max)
+               ep->curpacksize = ep->maxpacksize;
        else
-               subs->curpacksize = maxsize;
+               ep->curpacksize = maxsize;
 
-       if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
-               packs_per_ms = 8 >> subs->datainterval;
+       if (snd_usb_get_speed(ep->chip->dev) != USB_SPEED_FULL)
+               packs_per_ms = 8 >> ep->datainterval;
        else
                packs_per_ms = 1;
 
-       if (is_playback) {
-               urb_packs = max(chip->nrpacks, 1);
-               urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
-       } else
+       if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
+               urb_packs = max(ep->chip->nrpacks, 1);
+               urb_packs = min(urb_packs, (unsigned int) MAX_PACKS);
+       } else {
                urb_packs = 1;
+       }
+
        urb_packs *= packs_per_ms;
-       if (subs->syncpipe)
-               urb_packs = min(urb_packs, 1U << subs->syncinterval);
+
+       if (sync_ep && !snd_usb_endpoint_implict_feedback_sink(ep))
+               urb_packs = min(urb_packs, 1U << sync_ep->syncinterval);
 
        /* decide how many packets to be used */
-       if (is_playback) {
+       if (is_playback && !snd_usb_endpoint_implict_feedback_sink(ep)) {
                unsigned int minsize, maxpacks;
                /* determine how small a packet can be */
-               minsize = (subs->freqn >> (16 - subs->datainterval))
+               minsize = (ep->freqn >> (16 - ep->datainterval))
                          * (frame_bits >> 3);
                /* with sync from device, assume it can be 12% lower */
-               if (subs->syncpipe)
+               if (sync_ep)
                        minsize -= minsize >> 3;
                minsize = max(minsize, 1u);
                total_packs = (period_bytes + minsize - 1) / minsize;
@@ -284,284 +655,472 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
                        urb_packs >>= 1;
                total_packs = MAX_URBS * urb_packs;
        }
-       subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
-       if (subs->nurbs > MAX_URBS) {
+
+       ep->nurbs = (total_packs + urb_packs - 1) / urb_packs;
+       if (ep->nurbs > MAX_URBS) {
                /* too much... */
-               subs->nurbs = MAX_URBS;
+               ep->nurbs = MAX_URBS;
                total_packs = MAX_URBS * urb_packs;
-       } else if (subs->nurbs < 2) {
+       } else if (ep->nurbs < 2) {
                /* too little - we need at least two packets
                 * to ensure contiguous playback/capture
                 */
-               subs->nurbs = 2;
+               ep->nurbs = 2;
        }
 
        /* allocate and initialize data urbs */
-       for (i = 0; i < subs->nurbs; i++) {
-               struct snd_urb_ctx *u = &subs->dataurb[i];
+       for (i = 0; i < ep->nurbs; i++) {
+               struct snd_urb_ctx *u = &ep->urb[i];
                u->index = i;
-               u->subs = subs;
-               u->packets = (i + 1) * total_packs / subs->nurbs
-                       - i * total_packs / subs->nurbs;
+               u->ep = ep;
+               u->packets = (i + 1) * total_packs / ep->nurbs
+                       - i * total_packs / ep->nurbs;
                u->buffer_size = maxsize * u->packets;
-               if (subs->fmt_type == UAC_FORMAT_TYPE_II)
+
+               if (fmt->fmt_type == UAC_FORMAT_TYPE_II)
                        u->packets++; /* for transfer delimiter */
                u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
                if (!u->urb)
                        goto out_of_memory;
+
                u->urb->transfer_buffer =
-                       usb_alloc_coherent(subs->dev, u->buffer_size,
+                       usb_alloc_coherent(ep->chip->dev, u->buffer_size,
                                           GFP_KERNEL, &u->urb->transfer_dma);
                if (!u->urb->transfer_buffer)
                        goto out_of_memory;
-               u->urb->pipe = subs->datapipe;
+               u->urb->pipe = ep->pipe;
                u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
-               u->urb->interval = 1 << subs->datainterval;
+               u->urb->interval = 1 << ep->datainterval;
                u->urb->context = u;
                u->urb->complete = snd_complete_urb;
+               INIT_LIST_HEAD(&u->ready_list);
        }
 
-       if (subs->syncpipe) {
-               /* allocate and initialize sync urbs */
-               subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
-                                                GFP_KERNEL, &subs->sync_dma);
-               if (!subs->syncbuf)
-                       goto out_of_memory;
-               for (i = 0; i < SYNC_URBS; i++) {
-                       struct snd_urb_ctx *u = &subs->syncurb[i];
-                       u->index = i;
-                       u->subs = subs;
-                       u->packets = 1;
-                       u->urb = usb_alloc_urb(1, GFP_KERNEL);
-                       if (!u->urb)
-                               goto out_of_memory;
-                       u->urb->transfer_buffer = subs->syncbuf + i * 4;
-                       u->urb->transfer_dma = subs->sync_dma + i * 4;
-                       u->urb->transfer_buffer_length = 4;
-                       u->urb->pipe = subs->syncpipe;
-                       u->urb->transfer_flags = URB_ISO_ASAP |
-                                                URB_NO_TRANSFER_DMA_MAP;
-                       u->urb->number_of_packets = 1;
-                       u->urb->interval = 1 << subs->syncinterval;
-                       u->urb->context = u;
-                       u->urb->complete = snd_complete_sync_urb;
-               }
-       }
        return 0;
 
 out_of_memory:
-       snd_usb_release_substream_urbs(subs, 0);
+       release_urbs(ep, 0);
        return -ENOMEM;
 }
 
 /*
- * prepare urb for full speed capture sync pipe
- *
- * fill the length and offset of each urb descriptor.
- * the fixed 10.14 frequency is passed through the pipe.
+ * configure a sync endpoint
  */
-static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
-                                   struct snd_pcm_runtime *runtime,
-                                   struct urb *urb)
+static int sync_ep_set_params(struct snd_usb_endpoint *ep,
+                             struct snd_pcm_hw_params *hw_params,
+                             struct audioformat *fmt)
 {
-       unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = urb->context;
+       int i;
+
+       ep->syncbuf = usb_alloc_coherent(ep->chip->dev, SYNC_URBS * 4,
+                                        GFP_KERNEL, &ep->sync_dma);
+       if (!ep->syncbuf)
+               return -ENOMEM;
+
+       for (i = 0; i < SYNC_URBS; i++) {
+               struct snd_urb_ctx *u = &ep->urb[i];
+               u->index = i;
+               u->ep = ep;
+               u->packets = 1;
+               u->urb = usb_alloc_urb(1, GFP_KERNEL);
+               if (!u->urb)
+                       goto out_of_memory;
+               u->urb->transfer_buffer = ep->syncbuf + i * 4;
+               u->urb->transfer_dma = ep->sync_dma + i * 4;
+               u->urb->transfer_buffer_length = 4;
+               u->urb->pipe = ep->pipe;
+               u->urb->transfer_flags = URB_ISO_ASAP |
+                                        URB_NO_TRANSFER_DMA_MAP;
+               u->urb->number_of_packets = 1;
+               u->urb->interval = 1 << ep->syncinterval;
+               u->urb->context = u;
+               u->urb->complete = snd_complete_urb;
+       }
+
+       ep->nurbs = SYNC_URBS;
 
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 3;
-       urb->iso_frame_desc[0].offset = 0;
-       cp[0] = subs->freqn >> 2;
-       cp[1] = subs->freqn >> 10;
-       cp[2] = subs->freqn >> 18;
        return 0;
+
+out_of_memory:
+       release_urbs(ep, 0);
+       return -ENOMEM;
 }
 
-/*
- * prepare urb for high speed capture sync pipe
+/**
+ * snd_usb_endpoint_set_params: configure an snd_usb_endpoint
+ *
+ * @ep: the snd_usb_endpoint to configure
+ * @hw_params: the hardware parameters
+ * @fmt: the USB audio format information
+ * @sync_ep: the sync endpoint to use, if any
  *
- * fill the length and offset of each urb descriptor.
- * the fixed 12.13 frequency is passed as 16.16 through the pipe.
+ * Determine the number of URBs to be used on this endpoint.
+ * An endpoint must be configured before it can be started.
+ * An endpoint that is already running can not be reconfigured.
  */
-static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+                               struct snd_pcm_hw_params *hw_params,
+                               struct audioformat *fmt,
+                               struct snd_usb_endpoint *sync_ep)
 {
-       unsigned char *cp = urb->transfer_buffer;
-       struct snd_urb_ctx *ctx = urb->context;
+       int err;
 
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = 4;
-       urb->iso_frame_desc[0].offset = 0;
-       cp[0] = subs->freqn;
-       cp[1] = subs->freqn >> 8;
-       cp[2] = subs->freqn >> 16;
-       cp[3] = subs->freqn >> 24;
-       return 0;
+       if (ep->use_count != 0) {
+               snd_printk(KERN_WARNING "Unable to change format on ep #%x: already in use\n",
+                          ep->ep_num);
+               return -EBUSY;
+       }
+
+       /* release old buffers, if any */
+       release_urbs(ep, 0);
+
+       ep->datainterval = fmt->datainterval;
+       ep->maxpacksize = fmt->maxpacksize;
+       ep->fill_max = !!(fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX);
+
+       if (snd_usb_get_speed(ep->chip->dev) == USB_SPEED_FULL)
+               ep->freqn = get_usb_full_speed_rate(params_rate(hw_params));
+       else
+               ep->freqn = get_usb_high_speed_rate(params_rate(hw_params));
+
+       /* calculate the frequency in 16.16 format */
+       ep->freqm = ep->freqn;
+       ep->freqshift = INT_MIN;
+
+       ep->phase = 0;
+
+       switch (ep->type) {
+       case  SND_USB_ENDPOINT_TYPE_DATA:
+               err = data_ep_set_params(ep, hw_params, fmt, sync_ep);
+               break;
+       case  SND_USB_ENDPOINT_TYPE_SYNC:
+               err = sync_ep_set_params(ep, hw_params, fmt);
+               break;
+       default:
+               err = -EINVAL;
+       }
+
+       snd_printdd(KERN_DEBUG "Setting params for ep #%x (type %d, %d urbs), ret=%d\n",
+                  ep->ep_num, ep->type, ep->nurbs, err);
+
+       return err;
 }
 
-/*
- * process after capture sync complete
- * - nothing to do
+/**
+ * snd_usb_endpoint_start: start an snd_usb_endpoint
+ *
+ * @ep: the endpoint to start
+ *
+ * A call to this function will increment the use count of the endpoint.
+ * In case it is not already running, the URBs for this endpoint will be
+ * submitted. Otherwise, this function does nothing.
+ *
+ * Must be balanced to calls of snd_usb_endpoint_stop().
+ *
+ * Returns an error if the URB submission failed, 0 in all other cases.
  */
-static int retire_capture_sync_urb(struct snd_usb_substream *subs,
-                                  struct snd_pcm_runtime *runtime,
-                                  struct urb *urb)
+int snd_usb_endpoint_start(struct snd_usb_endpoint *ep)
 {
+       int err;
+       unsigned int i;
+
+       if (ep->chip->shutdown)
+               return -EBADFD;
+
+       /* already running? */
+       if (++ep->use_count != 1)
+               return 0;
+
+       if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+               return -EINVAL;
+
+       /* just to be sure */
+       deactivate_urbs(ep, 0, 1);
+       wait_clear_urbs(ep);
+
+       ep->active_mask = 0;
+       ep->unlink_mask = 0;
+       ep->phase = 0;
+
+       /*
+        * If this endpoint has a data endpoint as implicit feedback source,
+        * don't start the urbs here. Instead, mark them all as available,
+        * wait for the record urbs to return and queue the playback urbs
+        * from that context.
+        */
+
+       set_bit(EP_FLAG_RUNNING, &ep->flags);
+
+       if (snd_usb_endpoint_implict_feedback_sink(ep)) {
+               for (i = 0; i < ep->nurbs; i++) {
+                       struct snd_urb_ctx *ctx = ep->urb + i;
+                       list_add_tail(&ctx->ready_list, &ep->ready_playback_urbs);
+               }
+
+               return 0;
+       }
+
+       for (i = 0; i < ep->nurbs; i++) {
+               struct urb *urb = ep->urb[i].urb;
+
+               if (snd_BUG_ON(!urb))
+                       goto __error;
+
+               if (usb_pipeout(ep->pipe)) {
+                       prepare_outbound_urb_sizes(ep, urb->context);
+                       prepare_outbound_urb(ep, urb->context);
+               } else {
+                       prepare_inbound_urb(ep, urb->context);
+               }
+
+               err = usb_submit_urb(urb, GFP_ATOMIC);
+               if (err < 0) {
+                       snd_printk(KERN_ERR "cannot submit urb %d, error %d: %s\n",
+                                  i, err, usb_error_string(err));
+                       goto __error;
+               }
+               set_bit(i, &ep->active_mask);
+       }
+
        return 0;
+
+__error:
+       clear_bit(EP_FLAG_RUNNING, &ep->flags);
+       ep->use_count--;
+       deactivate_urbs(ep, 0, 0);
+       return -EPIPE;
 }
 
-/*
- * prepare urb for capture data pipe
+/**
+ * snd_usb_endpoint_stop: stop an snd_usb_endpoint
+ *
+ * @ep: the endpoint to stop (may be NULL)
  *
- * fill the offset and length of each descriptor.
+ * A call to this function will decrement the use count of the endpoint.
+ * In case the last user has requested the endpoint stop, the URBs will
+ * actually be deactivated.
  *
- * we use a temporary buffer to write the captured data.
- * since the length of written data is determined by host, we cannot
- * write onto the pcm buffer directly...  the data is thus copied
- * later at complete callback to the global buffer.
+ * Must be balanced to calls of snd_usb_endpoint_start().
  */
-static int prepare_capture_urb(struct snd_usb_substream *subs,
-                              struct snd_pcm_runtime *runtime,
-                              struct urb *urb)
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+                          int force, int can_sleep, int wait)
 {
-       int i, offs;
-       struct snd_urb_ctx *ctx = urb->context;
+       if (!ep)
+               return;
 
-       offs = 0;
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       for (i = 0; i < ctx->packets; i++) {
-               urb->iso_frame_desc[i].offset = offs;
-               urb->iso_frame_desc[i].length = subs->curpacksize;
-               offs += subs->curpacksize;
+       if (snd_BUG_ON(ep->use_count == 0))
+               return;
+
+       if (snd_BUG_ON(!test_bit(EP_FLAG_ACTIVATED, &ep->flags)))
+               return;
+
+       if (--ep->use_count == 0) {
+               deactivate_urbs(ep, force, can_sleep);
+               ep->data_subs = NULL;
+               ep->sync_slave = NULL;
+               ep->retire_data_urb = NULL;
+               ep->prepare_data_urb = NULL;
+
+               if (wait)
+                       wait_clear_urbs(ep);
        }
-       urb->transfer_buffer_length = offs;
-       urb->number_of_packets = ctx->packets;
-       return 0;
 }
 
-/*
- * process after capture complete
+/**
+ * snd_usb_endpoint_activate: activate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to activate
+ *
+ * If the endpoint is not currently in use, this functions will select the
+ * correct alternate interface setting for the interface of this endpoint.
  *
- * copy the data from each desctiptor to the pcm buffer, and
- * update the current position.
+ * In case of any active users, this functions does nothing.
+ *
+ * Returns an error if usb_set_interface() failed, 0 in all other
+ * cases.
  */
-static int retire_capture_urb(struct snd_usb_substream *subs,
-                             struct snd_pcm_runtime *runtime,
-                             struct urb *urb)
+int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep)
 {
-       unsigned long flags;
-       unsigned char *cp;
-       int i;
-       unsigned int stride, frames, bytes, oldptr;
-       int period_elapsed = 0;
+       if (ep->use_count != 0)
+               return 0;
 
-       stride = runtime->frame_bits >> 3;
+       if (!ep->chip->shutdown &&
+           !test_and_set_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+               int ret;
 
-       for (i = 0; i < urb->number_of_packets; i++) {
-               cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
-               if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
-                       snd_printdd("frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
-                       // continue;
-               }
-               bytes = urb->iso_frame_desc[i].actual_length;
-               frames = bytes / stride;
-               if (!subs->txfr_quirk)
-                       bytes = frames * stride;
-               if (bytes % (runtime->sample_bits >> 3) != 0) {
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-                       int oldbytes = bytes;
-#endif
-                       bytes = frames * stride;
-                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
-                                                       oldbytes, bytes);
-               }
-               /* update the current pointer */
-               spin_lock_irqsave(&subs->lock, flags);
-               oldptr = subs->hwptr_done;
-               subs->hwptr_done += bytes;
-               if (subs->hwptr_done >= runtime->buffer_size * stride)
-                       subs->hwptr_done -= runtime->buffer_size * stride;
-               frames = (bytes + (oldptr % stride)) / stride;
-               subs->transfer_done += frames;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       period_elapsed = 1;
-               }
-               spin_unlock_irqrestore(&subs->lock, flags);
-               /* copy a data chunk */
-               if (oldptr + bytes > runtime->buffer_size * stride) {
-                       unsigned int bytes1 =
-                                       runtime->buffer_size * stride - oldptr;
-                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
-                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
-               } else {
-                       memcpy(runtime->dma_area + oldptr, cp, bytes);
+               ret = usb_set_interface(ep->chip->dev, ep->iface, ep->alt_idx);
+               if (ret < 0) {
+                       snd_printk(KERN_ERR "%s() usb_set_interface() failed, ret = %d\n",
+                                               __func__, ret);
+                       clear_bit(EP_FLAG_ACTIVATED, &ep->flags);
+                       return ret;
                }
+
+               return 0;
        }
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       return 0;
+
+       return -EBUSY;
 }
 
-/*
- * Process after capture complete when paused.  Nothing to do.
+/**
+ * snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint
+ *
+ * @ep: the endpoint to deactivate
+ *
+ * If the endpoint is not currently in use, this functions will select the
+ * alternate interface setting 0 for the interface of this endpoint.
+ *
+ * In case of any active users, this functions does nothing.
+ *
+ * Returns an error if usb_set_interface() failed, 0 in all other
+ * cases.
  */
-static int retire_paused_capture_urb(struct snd_usb_substream *subs,
-                                    struct snd_pcm_runtime *runtime,
-                                    struct urb *urb)
+int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep)
 {
-       return 0;
-}
+       if (!ep)
+               return -EINVAL;
 
+       if (ep->use_count != 0)
+               return 0;
 
-/*
- * prepare urb for playback sync pipe
+       if (!ep->chip->shutdown &&
+           test_and_clear_bit(EP_FLAG_ACTIVATED, &ep->flags)) {
+               int ret;
+
+               ret = usb_set_interface(ep->chip->dev, ep->iface, 0);
+               if (ret < 0) {
+                       snd_printk(KERN_ERR "%s(): usb_set_interface() failed, ret = %d\n",
+                                               __func__, ret);
+                       return ret;
+               }
+
+               return 0;
+       }
+
+       return -EBUSY;
+}
+
+/**
+ * snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
+ *
+ * @ep: the list header of the endpoint to free
  *
- * set up the offset and length to receive the current frequency.
+ * This function does not care for the endpoint's use count but will tear
+ * down all the streaming URBs immediately and free all resources.
  */
-static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
-                                    struct snd_pcm_runtime *runtime,
-                                    struct urb *urb)
+void snd_usb_endpoint_free(struct list_head *head)
 {
-       struct snd_urb_ctx *ctx = urb->context;
+       struct snd_usb_endpoint *ep;
 
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
-       urb->iso_frame_desc[0].offset = 0;
-       return 0;
+       ep = list_entry(head, struct snd_usb_endpoint, list);
+       release_urbs(ep, 1);
+       kfree(ep);
 }
 
-/*
- * process after playback sync complete
- *
- * Full speed devices report feedback values in 10.14 format as samples per
- * frame, high speed devices in 16.16 format as samples per microframe.
- * Because the Audio Class 1 spec was written before USB 2.0, many high speed
- * devices use a wrong interpretation, some others use an entirely different
- * format.  Therefore, we cannot predict what format any particular device uses
- * and must detect it automatically.
+/**
+ * snd_usb_handle_sync_urb: parse an USB sync packet
+ *
+ * @ep: the endpoint to handle the packet
+ * @sender: the sending endpoint
+ * @urb: the received packet
+ *
+ * This function is called from the context of an endpoint that received
+ * the packet and is used to let another endpoint object handle the payload.
  */
-static int retire_playback_sync_urb(struct snd_usb_substream *subs,
-                                   struct snd_pcm_runtime *runtime,
-                                   struct urb *urb)
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+                            struct snd_usb_endpoint *sender,
+                            const struct urb *urb)
 {
-       unsigned int f;
        int shift;
+       unsigned int f;
        unsigned long flags;
 
+       snd_BUG_ON(ep == sender);
+
+       /*
+        * In case the endpoint is operating in implicit feedback mode, prepare
+        * a new outbound URB that has the same layout as the received packet
+        * and add it to the list of pending urbs. queue_pending_output_urbs()
+        * will take care of them later.
+        */
+       if (snd_usb_endpoint_implict_feedback_sink(ep) &&
+           ep->use_count != 0) {
+
+               /* implicit feedback case */
+               int i, bytes = 0;
+               struct snd_urb_ctx *in_ctx;
+               struct snd_usb_packet_info *out_packet;
+
+               in_ctx = urb->context;
+
+               /* Count overall packet size */
+               for (i = 0; i < in_ctx->packets; i++)
+                       if (urb->iso_frame_desc[i].status == 0)
+                               bytes += urb->iso_frame_desc[i].actual_length;
+
+               /*
+                * skip empty packets. At least M-Audio's Fast Track Ultra stops
+                * streaming once it received a 0-byte OUT URB
+                */
+               if (bytes == 0)
+                       return;
+
+               spin_lock_irqsave(&ep->lock, flags);
+               out_packet = ep->next_packet + ep->next_packet_write_pos;
+
+               /*
+                * Iterate through the inbound packet and prepare the lengths
+                * for the output packet. The OUT packet we are about to send
+                * will have the same amount of payload bytes than the IN
+                * packet we just received.
+                */
+
+               out_packet->packets = in_ctx->packets;
+               for (i = 0; i < in_ctx->packets; i++) {
+                       if (urb->iso_frame_desc[i].status == 0)
+                               out_packet->packet_size[i] =
+                                       urb->iso_frame_desc[i].actual_length / ep->stride;
+                       else
+                               out_packet->packet_size[i] = 0;
+               }
+
+               ep->next_packet_write_pos++;
+               ep->next_packet_write_pos %= MAX_URBS;
+               spin_unlock_irqrestore(&ep->lock, flags);
+               queue_pending_output_urbs(ep);
+
+               return;
+       }
+
+       /*
+        * process after playback sync complete
+        *
+        * Full speed devices report feedback values in 10.14 format as samples
+        * per frame, high speed devices in 16.16 format as samples per
+        * microframe.
+        *
+        * Because the Audio Class 1 spec was written before USB 2.0, many high
+        * speed devices use a wrong interpretation, some others use an
+        * entirely different format.
+        *
+        * Therefore, we cannot predict what format any particular device uses
+        * and must detect it automatically.
+        */
+
        if (urb->iso_frame_desc[0].status != 0 ||
            urb->iso_frame_desc[0].actual_length < 3)
-               return 0;
+               return;
 
        f = le32_to_cpup(urb->transfer_buffer);
        if (urb->iso_frame_desc[0].actual_length == 3)
                f &= 0x00ffffff;
        else
                f &= 0x0fffffff;
+
        if (f == 0)
-               return 0;
+               return;
 
-       if (unlikely(subs->freqshift == INT_MIN)) {
+       if (unlikely(ep->freqshift == INT_MIN)) {
                /*
                 * The first time we see a feedback value, determine its format
                 * by shifting it left or right until it matches the nominal
@@ -569,398 +1128,34 @@ static int retire_playback_sync_urb(struct snd_usb_substream *subs,
                 * differ from the nominal value more than +50% or -25%.
                 */
                shift = 0;
-               while (f < subs->freqn - subs->freqn / 4) {
+               while (f < ep->freqn - ep->freqn / 4) {
                        f <<= 1;
                        shift++;
                }
-               while (f > subs->freqn + subs->freqn / 2) {
+               while (f > ep->freqn + ep->freqn / 2) {
                        f >>= 1;
                        shift--;
                }
-               subs->freqshift = shift;
-       }
-       else if (subs->freqshift >= 0)
-               f <<= subs->freqshift;
+               ep->freqshift = shift;
+       } else if (ep->freqshift >= 0)
+               f <<= ep->freqshift;
        else
-               f >>= -subs->freqshift;
+               f >>= -ep->freqshift;
 
-       if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
+       if (likely(f >= ep->freqn - ep->freqn / 8 && f <= ep->freqmax)) {
                /*
                 * If the frequency looks valid, set it.
                 * This value is referred to in prepare_playback_urb().
                 */
-               spin_lock_irqsave(&subs->lock, flags);
-               subs->freqm = f;
-               spin_unlock_irqrestore(&subs->lock, flags);
+               spin_lock_irqsave(&ep->lock, flags);
+               ep->freqm = f;
+               spin_unlock_irqrestore(&ep->lock, flags);
        } else {
                /*
                 * Out of range; maybe the shift value is wrong.
                 * Reset it so that we autodetect again the next time.
                 */
-               subs->freqshift = INT_MIN;
-       }
-
-       return 0;
-}
-
-/* determine the number of frames in the next packet */
-static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
-{
-       if (subs->fill_max)
-               return subs->maxframesize;
-       else {
-               subs->phase = (subs->phase & 0xffff)
-                       + (subs->freqm << subs->datainterval);
-               return min(subs->phase >> 16, subs->maxframesize);
+               ep->freqshift = INT_MIN;
        }
 }
 
-/*
- * Prepare urb for streaming before playback starts or when paused.
- *
- * We don't have any data, so we send silence.
- */
-static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
-                                      struct snd_pcm_runtime *runtime,
-                                      struct urb *urb)
-{
-       unsigned int i, offs, counts;
-       struct snd_urb_ctx *ctx = urb->context;
-       int stride = runtime->frame_bits >> 3;
-
-       offs = 0;
-       urb->dev = ctx->subs->dev;
-       for (i = 0; i < ctx->packets; ++i) {
-               counts = snd_usb_audio_next_packet_size(subs);
-               urb->iso_frame_desc[i].offset = offs * stride;
-               urb->iso_frame_desc[i].length = counts * stride;
-               offs += counts;
-       }
-       urb->number_of_packets = ctx->packets;
-       urb->transfer_buffer_length = offs * stride;
-       memset(urb->transfer_buffer,
-              runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
-              offs * stride);
-       return 0;
-}
-
-/*
- * prepare urb for playback data pipe
- *
- * Since a URB can handle only a single linear buffer, we must use double
- * buffering when the data to be transferred overflows the buffer boundary.
- * To avoid inconsistencies when updating hwptr_done, we use double buffering
- * for all URBs.
- */
-static int prepare_playback_urb(struct snd_usb_substream *subs,
-                               struct snd_pcm_runtime *runtime,
-                               struct urb *urb)
-{
-       int i, stride;
-       unsigned int counts, frames, bytes;
-       unsigned long flags;
-       int period_elapsed = 0;
-       struct snd_urb_ctx *ctx = urb->context;
-
-       stride = runtime->frame_bits >> 3;
-
-       frames = 0;
-       urb->dev = ctx->subs->dev; /* we need to set this at each time */
-       urb->number_of_packets = 0;
-       spin_lock_irqsave(&subs->lock, flags);
-       for (i = 0; i < ctx->packets; i++) {
-               counts = snd_usb_audio_next_packet_size(subs);
-               /* set up descriptor */
-               urb->iso_frame_desc[i].offset = frames * stride;
-               urb->iso_frame_desc[i].length = counts * stride;
-               frames += counts;
-               urb->number_of_packets++;
-               subs->transfer_done += counts;
-               if (subs->transfer_done >= runtime->period_size) {
-                       subs->transfer_done -= runtime->period_size;
-                       period_elapsed = 1;
-                       if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
-                               if (subs->transfer_done > 0) {
-                                       /* FIXME: fill-max mode is not
-                                        * supported yet */
-                                       frames -= subs->transfer_done;
-                                       counts -= subs->transfer_done;
-                                       urb->iso_frame_desc[i].length =
-                                               counts * stride;
-                                       subs->transfer_done = 0;
-                               }
-                               i++;
-                               if (i < ctx->packets) {
-                                       /* add a transfer delimiter */
-                                       urb->iso_frame_desc[i].offset =
-                                               frames * stride;
-                                       urb->iso_frame_desc[i].length = 0;
-                                       urb->number_of_packets++;
-                               }
-                               break;
-                       }
-               }
-               if (period_elapsed) /* finish at the period boundary */
-                       break;
-       }
-       bytes = frames * stride;
-       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-               /* err, the transferred area goes over buffer boundary. */
-               unsigned int bytes1 =
-                       runtime->buffer_size * stride - subs->hwptr_done;
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes1);
-               memcpy(urb->transfer_buffer + bytes1,
-                      runtime->dma_area, bytes - bytes1);
-       } else {
-               memcpy(urb->transfer_buffer,
-                      runtime->dma_area + subs->hwptr_done, bytes);
-       }
-       subs->hwptr_done += bytes;
-       if (subs->hwptr_done >= runtime->buffer_size * stride)
-               subs->hwptr_done -= runtime->buffer_size * stride;
-
-       /* update delay with exact number of samples queued */
-       runtime->delay = subs->last_delay;
-       runtime->delay += frames;
-       subs->last_delay = runtime->delay;
-
-       /* realign last_frame_number */
-       subs->last_frame_number = usb_get_current_frame_number(subs->dev);
-       subs->last_frame_number &= 0xFF; /* keep 8 LSBs */
-
-       spin_unlock_irqrestore(&subs->lock, flags);
-       urb->transfer_buffer_length = bytes;
-       if (period_elapsed)
-               snd_pcm_period_elapsed(subs->pcm_substream);
-       return 0;
-}
-
-/*
- * process after playback data complete
- * - decrease the delay count again
- */
-static int retire_playback_urb(struct snd_usb_substream *subs,
-                              struct snd_pcm_runtime *runtime,
-                              struct urb *urb)
-{
-       unsigned long flags;
-       int stride = runtime->frame_bits >> 3;
-       int processed = urb->transfer_buffer_length / stride;
-       int est_delay;
-
-       spin_lock_irqsave(&subs->lock, flags);
-
-       est_delay = snd_usb_pcm_delay(subs, runtime->rate);
-       /* update delay with exact number of samples played */
-       if (processed > subs->last_delay)
-               subs->last_delay = 0;
-       else
-               subs->last_delay -= processed;
-       runtime->delay = subs->last_delay;
-
-       /*
-        * Report when delay estimate is off by more than 2ms.
-        * The error should be lower than 2ms since the estimate relies
-        * on two reads of a counter updated every ms.
-        */
-       if (abs(est_delay - subs->last_delay) * 1000 > runtime->rate * 2)
-               snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n",
-                       est_delay, subs->last_delay);
-
-       spin_unlock_irqrestore(&subs->lock, flags);
-       return 0;
-}
-
-static const char *usb_error_string(int err)
-{
-       switch (err) {
-       case -ENODEV:
-               return "no device";
-       case -ENOENT:
-               return "endpoint not enabled";
-       case -EPIPE:
-               return "endpoint stalled";
-       case -ENOSPC:
-               return "not enough bandwidth";
-       case -ESHUTDOWN:
-               return "device disabled";
-       case -EHOSTUNREACH:
-               return "device suspended";
-       case -EINVAL:
-       case -EAGAIN:
-       case -EFBIG:
-       case -EMSGSIZE:
-               return "internal error";
-       default:
-               return "unknown error";
-       }
-}
-
-/*
- * set up and start data/sync urbs
- */
-static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
-{
-       unsigned int i;
-       int err;
-
-       if (subs->stream->chip->shutdown)
-               return -EBADFD;
-
-       for (i = 0; i < subs->nurbs; i++) {
-               if (snd_BUG_ON(!subs->dataurb[i].urb))
-                       return -EINVAL;
-               if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
-                       snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
-                       goto __error;
-               }
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       if (snd_BUG_ON(!subs->syncurb[i].urb))
-                               return -EINVAL;
-                       if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
-                               snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
-                               goto __error;
-                       }
-               }
-       }
-
-       subs->active_mask = 0;
-       subs->unlink_mask = 0;
-       subs->running = 1;
-       for (i = 0; i < subs->nurbs; i++) {
-               err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
-               if (err < 0) {
-                       snd_printk(KERN_ERR "cannot submit datapipe "
-                                  "for urb %d, error %d: %s\n",
-                                  i, err, usb_error_string(err));
-                       goto __error;
-               }
-               set_bit(i, &subs->active_mask);
-       }
-       if (subs->syncpipe) {
-               for (i = 0; i < SYNC_URBS; i++) {
-                       err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
-                       if (err < 0) {
-                               snd_printk(KERN_ERR "cannot submit syncpipe "
-                                          "for urb %d, error %d: %s\n",
-                                          i, err, usb_error_string(err));
-                               goto __error;
-                       }
-                       set_bit(i + 16, &subs->active_mask);
-               }
-       }
-       return 0;
-
- __error:
-       // snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
-       deactivate_urbs(subs, 0, 0);
-       return -EPIPE;
-}
-
-
-/*
- */
-static struct snd_urb_ops audio_urb_ops[2] = {
-       {
-               .prepare =      prepare_nodata_playback_urb,
-               .retire =       retire_playback_urb,
-               .prepare_sync = prepare_playback_sync_urb,
-               .retire_sync =  retire_playback_sync_urb,
-       },
-       {
-               .prepare =      prepare_capture_urb,
-               .retire =       retire_capture_urb,
-               .prepare_sync = prepare_capture_sync_urb,
-               .retire_sync =  retire_capture_sync_urb,
-       },
-};
-
-/*
- * initialize the substream instance.
- */
-
-void snd_usb_init_substream(struct snd_usb_stream *as,
-                           int stream, struct audioformat *fp)
-{
-       struct snd_usb_substream *subs = &as->substream[stream];
-
-       INIT_LIST_HEAD(&subs->fmt_list);
-       spin_lock_init(&subs->lock);
-
-       subs->stream = as;
-       subs->direction = stream;
-       subs->dev = as->chip->dev;
-       subs->txfr_quirk = as->chip->txfr_quirk;
-       subs->ops = audio_urb_ops[stream];
-       if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
-               subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
-
-       snd_usb_set_pcm_ops(as->pcm, stream);
-
-       list_add_tail(&fp->list, &subs->fmt_list);
-       subs->formats |= fp->formats;
-       subs->endpoint = fp->endpoint;
-       subs->num_formats++;
-       subs->fmt_type = fp->fmt_type;
-}
-
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               subs->ops.prepare = prepare_playback_urb;
-               return 0;
-       case SNDRV_PCM_TRIGGER_STOP:
-               return deactivate_urbs(subs, 0, 0);
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               subs->ops.prepare = prepare_nodata_playback_urb;
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct snd_usb_substream *subs = substream->runtime->private_data;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               subs->ops.retire = retire_capture_urb;
-               return start_urbs(subs, substream->runtime);
-       case SNDRV_PCM_TRIGGER_STOP:
-               return deactivate_urbs(subs, 0, 0);
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               subs->ops.retire = retire_paused_capture_urb;
-               return 0;
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               subs->ops.retire = retire_capture_urb;
-               return 0;
-       }
-
-       return -EINVAL;
-}
-
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
-                             struct snd_pcm_runtime *runtime)
-{
-       /* clear urbs (to be sure) */
-       deactivate_urbs(subs, 0, 1);
-       wait_clear_urbs(subs);
-
-       /* for playback, submit the URBs now; otherwise, the first hwptr_done
-        * updates for all URBs would happen at the same time when starting */
-       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
-               subs->ops.prepare = prepare_nodata_playback_urb;
-               return start_urbs(subs, runtime);
-       }
-
-       return 0;
-}
-
index 88eb63a636eb28fa3b0fc56e05340d4be581cd72..ee2723fb174f61ddb9a3c0c0224bc065b36ba173 100644 (file)
@@ -1,21 +1,29 @@
 #ifndef __USBAUDIO_ENDPOINT_H
 #define __USBAUDIO_ENDPOINT_H
 
-void snd_usb_init_substream(struct snd_usb_stream *as,
-                           int stream,
-                           struct audioformat *fp);
+#define SND_USB_ENDPOINT_TYPE_DATA     0
+#define SND_USB_ENDPOINT_TYPE_SYNC     1
 
-int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
-                               unsigned int period_bytes,
-                               unsigned int rate,
-                               unsigned int frame_bits);
+struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
+                                             struct usb_host_interface *alts,
+                                             int ep_num, int direction, int type);
 
-void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
+int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep,
+                               struct snd_pcm_hw_params *hw_params,
+                               struct audioformat *fmt,
+                               struct snd_usb_endpoint *sync_ep);
 
-int snd_usb_substream_prepare(struct snd_usb_substream *subs,
-                             struct snd_pcm_runtime *runtime);
+int  snd_usb_endpoint_start(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep,
+                          int force, int can_sleep, int wait);
+int  snd_usb_endpoint_activate(struct snd_usb_endpoint *ep);
+int  snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep);
+void snd_usb_endpoint_free(struct list_head *head);
 
-int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
-int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
+int snd_usb_endpoint_implict_feedback_sink(struct snd_usb_endpoint *ep);
+
+void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
+                            struct snd_usb_endpoint *sender,
+                            const struct urb *urb);
 
 #endif /* __USBAUDIO_ENDPOINT_H */
index ab23869c01bb6734073b65c6c397fe177224c217..4f40ba82316316fdad18839c1bc906af2a2af66f 100644 (file)
@@ -486,7 +486,7 @@ static int set_cur_mix_value(struct usb_mixer_elem_info *cval, int channel,
 /*
  * TLV callback for mixer volume controls
  */
-static int mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
                         unsigned int size, unsigned int __user *_tlv)
 {
        struct usb_mixer_elem_info *cval = kcontrol->private_data;
@@ -770,6 +770,26 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval,
                                  struct snd_kcontrol *kctl)
 {
        switch (cval->mixer->chip->usb_id) {
+       case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
+       case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
+               if (strcmp(kctl->id.name, "Effect Duration") == 0) {
+                       snd_printk(KERN_INFO
+                               "usb-audio: set quirk for FTU Effect Duration\n");
+                       cval->min = 0x0000;
+                       cval->max = 0x7f00;
+                       cval->res = 0x0100;
+                       break;
+               }
+               if (strcmp(kctl->id.name, "Effect Volume") == 0 ||
+                   strcmp(kctl->id.name, "Effect Feedback Volume") == 0) {
+                       snd_printk(KERN_INFO
+                               "usb-audio: set quirks for FTU Effect Feedback/Volume\n");
+                       cval->min = 0x00;
+                       cval->max = 0x7f;
+                       break;
+               }
+               break;
+
        case USB_ID(0x0471, 0x0101):
        case USB_ID(0x0471, 0x0104):
        case USB_ID(0x0471, 0x0105):
@@ -1121,9 +1141,6 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                len = snd_usb_copy_string_desc(state, nameid,
                                kctl->id.name, sizeof(kctl->id.name));
 
-       /* get min/max values */
-       get_min_max_with_quirks(cval, 0, kctl);
-
        switch (control) {
        case UAC_FU_MUTE:
        case UAC_FU_VOLUME:
@@ -1155,17 +1172,7 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                }
                append_ctl_name(kctl, control == UAC_FU_MUTE ?
                                " Switch" : " Volume");
-               if (control == UAC_FU_VOLUME) {
-                       check_mapped_dB(map, cval);
-                       if (cval->dBmin < cval->dBmax || !cval->initialized) {
-                               kctl->tlv.c = mixer_vol_tlv;
-                               kctl->vd[0].access |= 
-                                       SNDRV_CTL_ELEM_ACCESS_TLV_READ |
-                                       SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
-                       }
-               }
                break;
-
        default:
                if (! len)
                        strlcpy(kctl->id.name, audio_feature_info[control-1].name,
@@ -1173,6 +1180,19 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc,
                break;
        }
 
+       /* get min/max values */
+       get_min_max_with_quirks(cval, 0, kctl);
+
+       if (control == UAC_FU_VOLUME) {
+               check_mapped_dB(map, cval);
+               if (cval->dBmin < cval->dBmax || !cval->initialized) {
+                       kctl->tlv.c = snd_usb_mixer_vol_tlv;
+                       kctl->vd[0].access |=
+                               SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                               SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+               }
+       }
+
        range = (cval->max - cval->min) / cval->res;
        /* Are there devices with volume range more than 255? I use a bit more
         * to be sure. 384 is a resolution magic number found on Logitech
@@ -1388,7 +1408,7 @@ static int parse_audio_mixer_unit(struct mixer_build *state, int unitid, void *r
        for (pin = 0; pin < input_pins; pin++) {
                err = parse_audio_unit(state, desc->baSourceID[pin]);
                if (err < 0)
-                       return err;
+                       continue;
                err = check_input_term(state, desc->baSourceID[pin], &iterm);
                if (err < 0)
                        return err;
index 81b2d8a32fb0f82e33475fabf6aa890ca7a675f4..a7f3d45a8acf1f6255a86f88038ca64012069137 100644 (file)
@@ -68,4 +68,7 @@ int snd_usb_mixer_activate(struct usb_mixer_interface *mixer);
 int snd_usb_mixer_add_control(struct usb_mixer_interface *mixer,
                              struct snd_kcontrol *kctl);
 
+int snd_usb_mixer_vol_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                         unsigned int size, unsigned int __user *_tlv);
+
 #endif /* __USBMIXER_H */
index f1324c423835c8ade54cdb30730e7999b3cec853..41daaa24c25f465e6ff209118d4a24f6f68ec220 100644 (file)
@@ -288,6 +288,15 @@ static struct usbmix_name_map scratch_live_map[] = {
        { 0 } /* terminator */
 };
 
+static struct usbmix_name_map ebox44_map[] = {
+       { 4, NULL }, /* FU */
+       { 6, NULL }, /* MU */
+       { 7, NULL }, /* FU */
+       { 10, NULL }, /* FU */
+       { 11, NULL }, /* MU */
+       { 0 }
+};
+
 /* "Gamesurround Muse Pocket LT" looks same like "Sound Blaster MP3+"
  *  most importand difference is SU[8], it should be set to "Capture Source"
  *  to make alsamixer and PA working properly.
@@ -371,6 +380,10 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
                .map = scratch_live_map,
                .ignore_ctl_error = 1,
        },
+       {
+               .id = USB_ID(0x200c, 0x1018),
+               .map = ebox44_map,
+       },
        { 0 } /* terminator */
 };
 
index ab125ee0b0f0e20091a13a7b354fc9f9abb69590..41f4b69119205d04f9b50e1e6ccc3bd5d16d980f 100644 (file)
 
 extern struct snd_kcontrol_new *snd_usb_feature_unit_ctl;
 
+/* private_free callback */
+static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+{
+       kfree(kctl->private_data);
+       kctl->private_data = NULL;
+}
+
+/* This function allows for the creation of standard UAC controls.
+ * See the quirks for M-Audio FTUs or Ebox-44.
+ * If you don't want to set a TLV callback pass NULL.
+ *
+ * Since there doesn't seem to be a devices that needs a multichannel
+ * version, we keep it mono for simplicity.
+ */
+static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer,
+                               unsigned int unitid,
+                               unsigned int control,
+                               unsigned int cmask,
+                               int val_type,
+                               const char *name,
+                               snd_kcontrol_tlv_rw_t *tlv_callback)
+{
+       int err;
+       struct usb_mixer_elem_info *cval;
+       struct snd_kcontrol *kctl;
+
+       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
+       if (!cval)
+               return -ENOMEM;
+
+       cval->id = unitid;
+       cval->mixer = mixer;
+       cval->val_type = val_type;
+       cval->channels = 1;
+       cval->control = control;
+       cval->cmask = cmask;
+
+       /* get_min_max() is called only for integer volumes later,
+        * so provide a short-cut for booleans */
+       cval->min = 0;
+       cval->max = 1;
+       cval->res = 0;
+       cval->dBmin = 0;
+       cval->dBmax = 0;
+
+       /* Create control */
+       kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+       if (!kctl) {
+               kfree(cval);
+               return -ENOMEM;
+       }
+
+       /* Set name */
+       snprintf(kctl->id.name, sizeof(kctl->id.name), name);
+       kctl->private_free = usb_mixer_elem_free;
+
+       /* set TLV */
+       if (tlv_callback) {
+               kctl->tlv.c = tlv_callback;
+               kctl->vd[0].access |=
+                       SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+                       SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+       }
+       /* Add control to mixer */
+       err = snd_usb_mixer_add_control(mixer, kctl);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 /*
  * Sound Blaster remote control configuration
  *
@@ -495,60 +566,218 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer,
 }
 
 /* M-Audio FastTrack Ultra quirks */
+/* FTU Effect switch */
+struct snd_ftu_eff_switch_priv_val {
+       struct usb_mixer_interface *mixer;
+       int cached_value;
+       int is_cached;
+};
 
-/* private_free callback */
-static void usb_mixer_elem_free(struct snd_kcontrol *kctl)
+static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol,
+                                       struct snd_ctl_elem_info *uinfo)
 {
-       kfree(kctl->private_data);
-       kctl->private_data = NULL;
+       static const char *texts[8] = {"Room 1",
+                                      "Room 2",
+                                      "Room 3",
+                                      "Hall 1",
+                                      "Hall 2",
+                                      "Plate",
+                                      "Delay",
+                                      "Echo"
+       };
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = 8;
+       if (uinfo->value.enumerated.item > 7)
+               uinfo->value.enumerated.item = 7;
+       strcpy(uinfo->value.enumerated.name,
+               texts[uinfo->value.enumerated.item]);
+
+       return 0;
 }
 
-static int snd_maudio_ftu_create_ctl(struct usb_mixer_interface *mixer,
-                                    int in, int out, const char *name)
+static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
 {
-       struct usb_mixer_elem_info *cval;
+       struct snd_usb_audio *chip;
+       struct usb_mixer_interface *mixer;
+       struct snd_ftu_eff_switch_priv_val *pval;
+       int err;
+       unsigned char value[2];
+
+       const int id = 6;
+       const int validx = 1;
+       const int val_len = 2;
+
+       value[0] = 0x00;
+       value[1] = 0x00;
+
+       pval = (struct snd_ftu_eff_switch_priv_val *)
+               kctl->private_value;
+
+       if (pval->is_cached) {
+               ucontrol->value.enumerated.item[0] = pval->cached_value;
+               return 0;
+       }
+
+       mixer = (struct usb_mixer_interface *) pval->mixer;
+       if (snd_BUG_ON(!mixer))
+               return -EINVAL;
+
+       chip = (struct snd_usb_audio *) mixer->chip;
+       if (snd_BUG_ON(!chip))
+               return -EINVAL;
+
+
+       err = snd_usb_ctl_msg(chip->dev,
+                       usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+                       USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+                       validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+                       value, val_len);
+       if (err < 0)
+               return err;
+
+       ucontrol->value.enumerated.item[0] = value[0];
+       pval->cached_value = value[0];
+       pval->is_cached = 1;
+
+       return 0;
+}
+
+static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl,
+                                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_usb_audio *chip;
+       struct snd_ftu_eff_switch_priv_val *pval;
+
+       struct usb_mixer_interface *mixer;
+       int changed, cur_val, err, new_val;
+       unsigned char value[2];
+
+
+       const int id = 6;
+       const int validx = 1;
+       const int val_len = 2;
+
+       changed = 0;
+
+       pval = (struct snd_ftu_eff_switch_priv_val *)
+               kctl->private_value;
+       cur_val = pval->cached_value;
+       new_val = ucontrol->value.enumerated.item[0];
+
+       mixer = (struct usb_mixer_interface *) pval->mixer;
+       if (snd_BUG_ON(!mixer))
+               return -EINVAL;
+
+       chip = (struct snd_usb_audio *) mixer->chip;
+       if (snd_BUG_ON(!chip))
+               return -EINVAL;
+
+       if (!pval->is_cached) {
+               /* Read current value */
+               err = snd_usb_ctl_msg(chip->dev,
+                               usb_rcvctrlpipe(chip->dev, 0), UAC_GET_CUR,
+                               USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
+                               validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+                               value, val_len);
+               if (err < 0)
+                       return err;
+
+               cur_val = value[0];
+               pval->cached_value = cur_val;
+               pval->is_cached = 1;
+       }
+       /* update value if needed */
+       if (cur_val != new_val) {
+               value[0] = new_val;
+               value[1] = 0;
+               err = snd_usb_ctl_msg(chip->dev,
+                               usb_sndctrlpipe(chip->dev, 0), UAC_SET_CUR,
+                               USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
+                               validx << 8, snd_usb_ctrl_intf(chip) | (id << 8),
+                               value, val_len);
+               if (err < 0)
+                       return err;
+
+               pval->cached_value = new_val;
+               pval->is_cached = 1;
+               changed = 1;
+       }
+
+       return changed;
+}
+
+static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer)
+{
+       static struct snd_kcontrol_new template = {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = "Effect Program Switch",
+               .index = 0,
+               .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+               .info = snd_ftu_eff_switch_info,
+               .get = snd_ftu_eff_switch_get,
+               .put = snd_ftu_eff_switch_put
+       };
+
+       int err;
        struct snd_kcontrol *kctl;
+       struct snd_ftu_eff_switch_priv_val *pval;
 
-       cval = kzalloc(sizeof(*cval), GFP_KERNEL);
-       if (!cval)
+       pval = kzalloc(sizeof(*pval), GFP_KERNEL);
+       if (!pval)
                return -ENOMEM;
 
-       cval->id = 5;
-       cval->mixer = mixer;
-       cval->val_type = USB_MIXER_S16;
-       cval->channels = 1;
-       cval->control = out + 1;
-       cval->cmask = 1 << in;
+       pval->cached_value = 0;
+       pval->is_cached = 0;
+       pval->mixer = mixer;
 
-       kctl = snd_ctl_new1(snd_usb_feature_unit_ctl, cval);
+       template.private_value = (unsigned long) pval;
+       kctl = snd_ctl_new1(&template, mixer->chip);
        if (!kctl) {
-               kfree(cval);
+               kfree(pval);
                return -ENOMEM;
        }
 
-       snprintf(kctl->id.name, sizeof(kctl->id.name), name);
-       kctl->private_free = usb_mixer_elem_free;
-       return snd_usb_mixer_add_control(mixer, kctl);
+       err = snd_ctl_add(mixer->chip->card, kctl);
+       if (err < 0)
+               return err;
+
+       return 0;
 }
 
-static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
+/* Create volume controls for FTU devices*/
+static int snd_ftu_create_volume_ctls(struct usb_mixer_interface *mixer)
 {
        char name[64];
+       unsigned int control, cmask;
        int in, out, err;
 
+       const unsigned int id = 5;
+       const int val_type = USB_MIXER_S16;
+
        for (out = 0; out < 8; out++) {
+               control = out + 1;
                for (in = 0; in < 8; in++) {
+                       cmask = 1 << in;
                        snprintf(name, sizeof(name),
-                                "AIn%d - Out%d Capture Volume", in  + 1, out + 1);
-                       err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+                               "AIn%d - Out%d Capture Volume",
+                               in  + 1, out + 1);
+                       err = snd_create_std_mono_ctl(mixer, id, control,
+                                                       cmask, val_type, name,
+                                                       &snd_usb_mixer_vol_tlv);
                        if (err < 0)
                                return err;
                }
-
                for (in = 8; in < 16; in++) {
+                       cmask = 1 << in;
                        snprintf(name, sizeof(name),
-                                "DIn%d - Out%d Playback Volume", in - 7, out + 1);
-                       err = snd_maudio_ftu_create_ctl(mixer, in, out, name);
+                               "DIn%d - Out%d Playback Volume",
+                               in - 7, out + 1);
+                       err = snd_create_std_mono_ctl(mixer, id, control,
+                                                       cmask, val_type, name,
+                                                       &snd_usb_mixer_vol_tlv);
                        if (err < 0)
                                return err;
                }
@@ -557,6 +786,191 @@ static int snd_maudio_ftu_create_mixer(struct usb_mixer_interface *mixer)
        return 0;
 }
 
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_volume_ctl(struct usb_mixer_interface *mixer)
+{
+       static const char name[] = "Effect Volume";
+       const unsigned int id = 6;
+       const int val_type = USB_MIXER_U8;
+       const unsigned int control = 2;
+       const unsigned int cmask = 0;
+
+       return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+                                       name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_duration_ctl(struct usb_mixer_interface *mixer)
+{
+       static const char name[] = "Effect Duration";
+       const unsigned int id = 6;
+       const int val_type = USB_MIXER_S16;
+       const unsigned int control = 3;
+       const unsigned int cmask = 0;
+
+       return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+                                       name, snd_usb_mixer_vol_tlv);
+}
+
+/* This control needs a volume quirk, see mixer.c */
+static int snd_ftu_create_effect_feedback_ctl(struct usb_mixer_interface *mixer)
+{
+       static const char name[] = "Effect Feedback Volume";
+       const unsigned int id = 6;
+       const int val_type = USB_MIXER_U8;
+       const unsigned int control = 4;
+       const unsigned int cmask = 0;
+
+       return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type,
+                                       name, NULL);
+}
+
+static int snd_ftu_create_effect_return_ctls(struct usb_mixer_interface *mixer)
+{
+       unsigned int cmask;
+       int err, ch;
+       char name[48];
+
+       const unsigned int id = 7;
+       const int val_type = USB_MIXER_S16;
+       const unsigned int control = 7;
+
+       for (ch = 0; ch < 4; ++ch) {
+               cmask = 1 << ch;
+               snprintf(name, sizeof(name),
+                       "Effect Return %d Volume", ch + 1);
+               err = snd_create_std_mono_ctl(mixer, id, control,
+                                               cmask, val_type, name,
+                                               snd_usb_mixer_vol_tlv);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int snd_ftu_create_effect_send_ctls(struct usb_mixer_interface *mixer)
+{
+       unsigned int  cmask;
+       int err, ch;
+       char name[48];
+
+       const unsigned int id = 5;
+       const int val_type = USB_MIXER_S16;
+       const unsigned int control = 9;
+
+       for (ch = 0; ch < 8; ++ch) {
+               cmask = 1 << ch;
+               snprintf(name, sizeof(name),
+                       "Effect Send AIn%d Volume", ch + 1);
+               err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+                                               val_type, name,
+                                               snd_usb_mixer_vol_tlv);
+               if (err < 0)
+                       return err;
+       }
+       for (ch = 8; ch < 16; ++ch) {
+               cmask = 1 << ch;
+               snprintf(name, sizeof(name),
+                       "Effect Send DIn%d Volume", ch - 7);
+               err = snd_create_std_mono_ctl(mixer, id, control, cmask,
+                                               val_type, name,
+                                               snd_usb_mixer_vol_tlv);
+               if (err < 0)
+                       return err;
+       }
+       return 0;
+}
+
+static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer)
+{
+       int err;
+
+       err = snd_ftu_create_volume_ctls(mixer);
+       if (err < 0)
+               return err;
+
+       err = snd_ftu_create_effect_switch(mixer);
+       if (err < 0)
+               return err;
+       err = snd_ftu_create_effect_volume_ctl(mixer);
+       if (err < 0)
+               return err;
+
+       err = snd_ftu_create_effect_duration_ctl(mixer);
+       if (err < 0)
+               return err;
+
+       err = snd_ftu_create_effect_feedback_ctl(mixer);
+       if (err < 0)
+               return err;
+
+       err = snd_ftu_create_effect_return_ctls(mixer);
+       if (err < 0)
+               return err;
+
+       err = snd_ftu_create_effect_send_ctls(mixer);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
+
+/*
+ * Create mixer for Electrix Ebox-44
+ *
+ * The mixer units from this device are corrupt, and even where they
+ * are valid they presents mono controls as L and R channels of
+ * stereo. So we create a good mixer in code.
+ */
+
+static int snd_ebox44_create_mixer(struct usb_mixer_interface *mixer)
+{
+       int err;
+
+       err = snd_create_std_mono_ctl(mixer, 4, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+                               "Headphone Playback Switch", NULL);
+       if (err < 0)
+               return err;
+       err = snd_create_std_mono_ctl(mixer, 4, 2, 0x1, USB_MIXER_S16,
+                               "Headphone A Mix Playback Volume", NULL);
+       if (err < 0)
+               return err;
+       err = snd_create_std_mono_ctl(mixer, 4, 2, 0x2, USB_MIXER_S16,
+                               "Headphone B Mix Playback Volume", NULL);
+       if (err < 0)
+               return err;
+
+       err = snd_create_std_mono_ctl(mixer, 7, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+                               "Output Playback Switch", NULL);
+       if (err < 0)
+               return err;
+       err = snd_create_std_mono_ctl(mixer, 7, 2, 0x1, USB_MIXER_S16,
+                               "Output A Playback Volume", NULL);
+       if (err < 0)
+               return err;
+       err = snd_create_std_mono_ctl(mixer, 7, 2, 0x2, USB_MIXER_S16,
+                               "Output B Playback Volume", NULL);
+       if (err < 0)
+               return err;
+
+       err = snd_create_std_mono_ctl(mixer, 10, 1, 0x0, USB_MIXER_INV_BOOLEAN,
+                               "Input Capture Switch", NULL);
+       if (err < 0)
+               return err;
+       err = snd_create_std_mono_ctl(mixer, 10, 2, 0x1, USB_MIXER_S16,
+                               "Input A Capture Volume", NULL);
+       if (err < 0)
+               return err;
+       err = snd_create_std_mono_ctl(mixer, 10, 2, 0x2, USB_MIXER_S16,
+                               "Input B Capture Volume", NULL);
+       if (err < 0)
+               return err;
+
+       return 0;
+}
+
 void snd_emuusb_set_samplerate(struct snd_usb_audio *chip,
                               unsigned char samplerate_id)
 {
@@ -600,7 +1014,7 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
 
        case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */
        case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */
-               err = snd_maudio_ftu_create_mixer(mixer);
+               err = snd_ftu_create_mixer(mixer);
                break;
 
        case USB_ID(0x0b05, 0x1739):
@@ -619,6 +1033,10 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
                                snd_nativeinstruments_ta10_mixers,
                                ARRAY_SIZE(snd_nativeinstruments_ta10_mixers));
                break;
+
+       case USB_ID(0x200c, 0x1018): /* Electrix Ebox-44 */
+               err = snd_ebox44_create_mixer(mixer);
+               break;
        }
 
        return err;
index 0eed6115c2d444e166d770008238d5e11e4800fb..24839d932648c81849ecfe63214a8124d914dd0e 100644 (file)
@@ -16,6 +16,7 @@
 
 #include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/ratelimit.h>
 #include <linux/usb.h>
 #include <linux/usb/audio.h>
 #include <linux/usb/audio-v2.h>
@@ -34,6 +35,9 @@
 #include "clock.h"
 #include "power.h"
 
+#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
+#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
+
 /* return the estimated delay based on USB frame counters */
 snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
                                    unsigned int rate)
@@ -208,6 +212,84 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
        }
 }
 
+static int start_endpoints(struct snd_usb_substream *subs)
+{
+       int err;
+
+       if (!subs->data_endpoint)
+               return -EINVAL;
+
+       if (!test_and_set_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) {
+               struct snd_usb_endpoint *ep = subs->data_endpoint;
+
+               snd_printdd(KERN_DEBUG "Starting data EP @%p\n", ep);
+
+               ep->data_subs = subs;
+               err = snd_usb_endpoint_start(ep);
+               if (err < 0) {
+                       clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags);
+                       return err;
+               }
+       }
+
+       if (subs->sync_endpoint &&
+           !test_and_set_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) {
+               struct snd_usb_endpoint *ep = subs->sync_endpoint;
+
+               snd_printdd(KERN_DEBUG "Starting sync EP @%p\n", ep);
+
+               ep->sync_slave = subs->data_endpoint;
+               err = snd_usb_endpoint_start(ep);
+               if (err < 0) {
+                       clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags);
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static void stop_endpoints(struct snd_usb_substream *subs,
+                          int force, int can_sleep, int wait)
+{
+       if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags))
+               snd_usb_endpoint_stop(subs->sync_endpoint,
+                                     force, can_sleep, wait);
+
+       if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags))
+               snd_usb_endpoint_stop(subs->data_endpoint,
+                                     force, can_sleep, wait);
+}
+
+static int activate_endpoints(struct snd_usb_substream *subs)
+{
+       if (subs->sync_endpoint) {
+               int ret;
+
+               ret = snd_usb_endpoint_activate(subs->sync_endpoint);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return snd_usb_endpoint_activate(subs->data_endpoint);
+}
+
+static int deactivate_endpoints(struct snd_usb_substream *subs)
+{
+       int reta, retb;
+
+       reta = snd_usb_endpoint_deactivate(subs->sync_endpoint);
+       retb = snd_usb_endpoint_deactivate(subs->data_endpoint);
+
+       if (reta < 0)
+               return reta;
+
+       if (retb < 0)
+               return retb;
+
+       return 0;
+}
+
 /*
  * find a matching format and set up the interface
  */
@@ -219,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
        struct usb_interface *iface;
        unsigned int ep, attr;
        int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       int err;
+       int err, implicit_fb = 0;
 
        iface = usb_ifnum_to_if(dev, fmt->iface);
        if (WARN_ON(!iface))
@@ -232,40 +314,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
        if (fmt == subs->cur_audiofmt)
                return 0;
 
-       /* close the old interface */
-       if (subs->interface >= 0 && subs->interface != fmt->iface) {
-               if (usb_set_interface(subs->dev, subs->interface, 0) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: return to setting 0 failed\n",
-                               dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EIO;
-               }
-               subs->interface = -1;
-               subs->altset_idx = 0;
-       }
-
-       /* set interface */
-       if (subs->interface != fmt->iface || subs->altset_idx != fmt->altset_idx) {
-               if (usb_set_interface(dev, fmt->iface, fmt->altsetting) < 0) {
-                       snd_printk(KERN_ERR "%d:%d:%d: usb_set_interface failed\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting);
-                       return -EIO;
-               }
-               snd_printdd(KERN_INFO "setting usb interface %d:%d\n", fmt->iface, fmt->altsetting);
-               subs->interface = fmt->iface;
-               subs->altset_idx = fmt->altset_idx;
-       }
-
-       /* create a data pipe */
-       ep = fmt->endpoint & USB_ENDPOINT_NUMBER_MASK;
-       if (is_playback)
-               subs->datapipe = usb_sndisocpipe(dev, ep);
-       else
-               subs->datapipe = usb_rcvisocpipe(dev, ep);
-       subs->datainterval = fmt->datainterval;
-       subs->syncpipe = subs->syncinterval = 0;
-       subs->maxpacksize = fmt->maxpacksize;
-       subs->syncmaxsize = 0;
-       subs->fill_max = 0;
+       subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+                                                  alts, fmt->endpoint, subs->direction,
+                                                  SND_USB_ENDPOINT_TYPE_DATA);
+       if (!subs->data_endpoint)
+               return -EINVAL;
 
        /* we need a sync pipe in async OUT or adaptive IN mode */
        /* check the number of EP, since some devices have broken
@@ -273,8 +326,25 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
         * assume it as adaptive-out or sync-in.
         */
        attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+
+       switch (subs->stream->chip->usb_id) {
+       case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+       case USB_ID(0x0763, 0x2081):
+               if (is_playback) {
+                       implicit_fb = 1;
+                       ep = 0x81;
+                       iface = usb_ifnum_to_if(dev, 2);
+
+                       if (!iface || iface->num_altsetting == 0)
+                               return -EINVAL;
+
+                       alts = &iface->altsetting[1];
+                       goto add_sync_ep;
+               }
+       }
+
        if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
-            (! is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
+            (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
            altsd->bNumEndpoints >= 2) {
                /* check sync-pipe endpoint */
                /* ... and check descriptor size before accessing bSynchAddress
@@ -282,7 +352,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
                   the audio fields in the endpoint descriptors */
                if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 ||
                    (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                    get_endpoint(alts, 1)->bSynchAddress != 0)) {
+                    get_endpoint(alts, 1)->bSynchAddress != 0 &&
+                    !implicit_fb)) {
                        snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
                                   dev->devnum, fmt->iface, fmt->altsetting);
                        return -EINVAL;
@@ -290,33 +361,27 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
                ep = get_endpoint(alts, 1)->bEndpointAddress;
                if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
                    (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
-                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)) ||
+                    ( is_playback && !implicit_fb))) {
                        snd_printk(KERN_ERR "%d:%d:%d : invalid synch pipe\n",
                                   dev->devnum, fmt->iface, fmt->altsetting);
                        return -EINVAL;
                }
-               ep &= USB_ENDPOINT_NUMBER_MASK;
-               if (is_playback)
-                       subs->syncpipe = usb_rcvisocpipe(dev, ep);
-               else
-                       subs->syncpipe = usb_sndisocpipe(dev, ep);
-               if (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                   get_endpoint(alts, 1)->bRefresh >= 1 &&
-                   get_endpoint(alts, 1)->bRefresh <= 9)
-                       subs->syncinterval = get_endpoint(alts, 1)->bRefresh;
-               else if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
-                       subs->syncinterval = 1;
-               else if (get_endpoint(alts, 1)->bInterval >= 1 &&
-                        get_endpoint(alts, 1)->bInterval <= 16)
-                       subs->syncinterval = get_endpoint(alts, 1)->bInterval - 1;
-               else
-                       subs->syncinterval = 3;
-               subs->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
-       }
-
-       /* always fill max packet size */
-       if (fmt->attributes & UAC_EP_CS_ATTR_FILL_MAX)
-               subs->fill_max = 1;
+
+               implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+                               == USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
+add_sync_ep:
+               subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+                                                          alts, ep, !subs->direction,
+                                                          implicit_fb ?
+                                                               SND_USB_ENDPOINT_TYPE_DATA :
+                                                               SND_USB_ENDPOINT_TYPE_SYNC);
+               if (!subs->sync_endpoint)
+                       return -EINVAL;
+
+               subs->data_endpoint->sync_master = subs->sync_endpoint;
+       }
 
        if ((err = snd_usb_init_pitch(subs->stream->chip, subs->interface, alts, fmt)) < 0)
                return err;
@@ -390,15 +455,30 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
        if (changed) {
                mutex_lock(&subs->stream->chip->shutdown_mutex);
                /* format changed */
-               snd_usb_release_substream_urbs(subs, 0);
-               /* influenced: period_bytes, channels, rate, format, */
-               ret = snd_usb_init_substream_urbs(subs, params_period_bytes(hw_params),
-                                                 params_rate(hw_params),
-                                                 snd_pcm_format_physical_width(params_format(hw_params)) *
-                                                       params_channels(hw_params));
+               stop_endpoints(subs, 0, 0, 0);
+               deactivate_endpoints(subs);
+
+               ret = activate_endpoints(subs);
+               if (ret < 0)
+                       goto unlock;
+
+               ret = snd_usb_endpoint_set_params(subs->data_endpoint, hw_params, fmt,
+                                                 subs->sync_endpoint);
+               if (ret < 0)
+                       goto unlock;
+
+               if (subs->sync_endpoint)
+                       ret = snd_usb_endpoint_set_params(subs->sync_endpoint,
+                                                         hw_params, fmt, NULL);
+unlock:
                mutex_unlock(&subs->stream->chip->shutdown_mutex);
        }
 
+       if (ret == 0) {
+               subs->interface = fmt->iface;
+               subs->altset_idx = fmt->altset_idx;
+       }
+
        return ret;
 }
 
@@ -415,7 +495,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
        subs->cur_rate = 0;
        subs->period_bytes = 0;
        mutex_lock(&subs->stream->chip->shutdown_mutex);
-       snd_usb_release_substream_urbs(subs, 0);
+       stop_endpoints(subs, 0, 1, 1);
        mutex_unlock(&subs->stream->chip->shutdown_mutex);
        return snd_pcm_lib_free_vmalloc_buffer(substream);
 }
@@ -435,19 +515,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
                return -ENXIO;
        }
 
+       if (snd_BUG_ON(!subs->data_endpoint))
+               return -EIO;
+
        /* some unit conversions in runtime */
-       subs->maxframesize = bytes_to_frames(runtime, subs->maxpacksize);
-       subs->curframesize = bytes_to_frames(runtime, subs->curpacksize);
+       subs->data_endpoint->maxframesize =
+               bytes_to_frames(runtime, subs->data_endpoint->maxpacksize);
+       subs->data_endpoint->curframesize =
+               bytes_to_frames(runtime, subs->data_endpoint->curpacksize);
 
        /* reset the pointer */
        subs->hwptr_done = 0;
        subs->transfer_done = 0;
-       subs->phase = 0;
        subs->last_delay = 0;
        subs->last_frame_number = 0;
        runtime->delay = 0;
 
-       return snd_usb_substream_prepare(subs, runtime);
+       /* for playback, submit the URBs now; otherwise, the first hwptr_done
+        * updates for all URBs would happen at the same time when starting */
+       if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
+               return start_endpoints(subs);
+
+       return 0;
 }
 
 static struct snd_pcm_hardware snd_usb_hardware =
@@ -842,16 +931,171 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
 
 static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction)
 {
+       int ret;
        struct snd_usb_stream *as = snd_pcm_substream_chip(substream);
        struct snd_usb_substream *subs = &as->substream[direction];
 
-       if (!as->chip->shutdown && subs->interface >= 0) {
-               usb_set_interface(subs->dev, subs->interface, 0);
-               subs->interface = -1;
-       }
+       stop_endpoints(subs, 0, 0, 0);
+       ret = deactivate_endpoints(subs);
        subs->pcm_substream = NULL;
        snd_usb_autosuspend(subs->stream->chip);
-       return 0;
+
+       return ret;
+}
+
+/* Since a URB can handle only a single linear buffer, we must use double
+ * buffering when the data to be transferred overflows the buffer boundary.
+ * To avoid inconsistencies when updating hwptr_done, we use double buffering
+ * for all URBs.
+ */
+static void retire_capture_urb(struct snd_usb_substream *subs,
+                              struct urb *urb)
+{
+       struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+       unsigned int stride, frames, bytes, oldptr;
+       int i, period_elapsed = 0;
+       unsigned long flags;
+       unsigned char *cp;
+
+       stride = runtime->frame_bits >> 3;
+
+       for (i = 0; i < urb->number_of_packets; i++) {
+               cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
+               if (urb->iso_frame_desc[i].status && printk_ratelimit()) {
+                       snd_printdd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
+                       // continue;
+               }
+               bytes = urb->iso_frame_desc[i].actual_length;
+               frames = bytes / stride;
+               if (!subs->txfr_quirk)
+                       bytes = frames * stride;
+               if (bytes % (runtime->sample_bits >> 3) != 0) {
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+                       int oldbytes = bytes;
+#endif
+                       bytes = frames * stride;
+                       snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
+                                                       oldbytes, bytes);
+               }
+               /* update the current pointer */
+               spin_lock_irqsave(&subs->lock, flags);
+               oldptr = subs->hwptr_done;
+               subs->hwptr_done += bytes;
+               if (subs->hwptr_done >= runtime->buffer_size * stride)
+                       subs->hwptr_done -= runtime->buffer_size * stride;
+               frames = (bytes + (oldptr % stride)) / stride;
+               subs->transfer_done += frames;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+               }
+               spin_unlock_irqrestore(&subs->lock, flags);
+               /* copy a data chunk */
+               if (oldptr + bytes > runtime->buffer_size * stride) {
+                       unsigned int bytes1 =
+                                       runtime->buffer_size * stride - oldptr;
+                       memcpy(runtime->dma_area + oldptr, cp, bytes1);
+                       memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
+               } else {
+                       memcpy(runtime->dma_area + oldptr, cp, bytes);
+               }
+       }
+
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+static void prepare_playback_urb(struct snd_usb_substream *subs,
+                                struct urb *urb)
+{
+       struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+       struct snd_urb_ctx *ctx = urb->context;
+       unsigned int counts, frames, bytes;
+       int i, stride, period_elapsed = 0;
+       unsigned long flags;
+
+       stride = runtime->frame_bits >> 3;
+
+       frames = 0;
+       urb->number_of_packets = 0;
+       spin_lock_irqsave(&subs->lock, flags);
+       for (i = 0; i < ctx->packets; i++) {
+               counts = ctx->packet_size[i];
+               /* set up descriptor */
+               urb->iso_frame_desc[i].offset = frames * stride;
+               urb->iso_frame_desc[i].length = counts * stride;
+               frames += counts;
+               urb->number_of_packets++;
+               subs->transfer_done += counts;
+               if (subs->transfer_done >= runtime->period_size) {
+                       subs->transfer_done -= runtime->period_size;
+                       period_elapsed = 1;
+                       if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
+                               if (subs->transfer_done > 0) {
+                                       /* FIXME: fill-max mode is not
+                                        * supported yet */
+                                       frames -= subs->transfer_done;
+                                       counts -= subs->transfer_done;
+                                       urb->iso_frame_desc[i].length =
+                                               counts * stride;
+                                       subs->transfer_done = 0;
+                               }
+                               i++;
+                               if (i < ctx->packets) {
+                                       /* add a transfer delimiter */
+                                       urb->iso_frame_desc[i].offset =
+                                               frames * stride;
+                                       urb->iso_frame_desc[i].length = 0;
+                                       urb->number_of_packets++;
+                               }
+                               break;
+                       }
+               }
+               if (period_elapsed &&
+                   !snd_usb_endpoint_implict_feedback_sink(subs->data_endpoint)) /* finish at the period boundary */
+                       break;
+       }
+       bytes = frames * stride;
+       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+               /* err, the transferred area goes over buffer boundary. */
+               unsigned int bytes1 =
+                       runtime->buffer_size * stride - subs->hwptr_done;
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done, bytes1);
+               memcpy(urb->transfer_buffer + bytes1,
+                      runtime->dma_area, bytes - bytes1);
+       } else {
+               memcpy(urb->transfer_buffer,
+                      runtime->dma_area + subs->hwptr_done, bytes);
+       }
+       subs->hwptr_done += bytes;
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+       runtime->delay += frames;
+       spin_unlock_irqrestore(&subs->lock, flags);
+       urb->transfer_buffer_length = bytes;
+       if (period_elapsed)
+               snd_pcm_period_elapsed(subs->pcm_substream);
+}
+
+/*
+ * process after playback data complete
+ * - decrease the delay count again
+ */
+static void retire_playback_urb(struct snd_usb_substream *subs,
+                              struct urb *urb)
+{
+       unsigned long flags;
+       struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+       int stride = runtime->frame_bits >> 3;
+       int processed = urb->transfer_buffer_length / stride;
+
+       spin_lock_irqsave(&subs->lock, flags);
+       if (processed > runtime->delay)
+               runtime->delay = 0;
+       else
+               runtime->delay -= processed;
+       spin_unlock_irqrestore(&subs->lock, flags);
 }
 
 static int snd_usb_playback_open(struct snd_pcm_substream *substream)
@@ -874,6 +1118,63 @@ static int snd_usb_capture_close(struct snd_pcm_substream *substream)
        return snd_usb_pcm_close(substream, SNDRV_PCM_STREAM_CAPTURE);
 }
 
+static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream,
+                                             int cmd)
+{
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->data_endpoint->prepare_data_urb = prepare_playback_urb;
+               subs->data_endpoint->retire_data_urb = retire_playback_urb;
+               subs->running = 1;
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               stop_endpoints(subs, 0, 0, 0);
+               subs->running = 0;
+               return 0;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->data_endpoint->prepare_data_urb = NULL;
+               subs->data_endpoint->retire_data_urb = NULL;
+               subs->running = 0;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
+int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       int err;
+       struct snd_usb_substream *subs = substream->runtime->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               err = start_endpoints(subs);
+               if (err < 0)
+                       return err;
+
+               subs->data_endpoint->retire_data_urb = retire_capture_urb;
+               subs->running = 1;
+               return 0;
+       case SNDRV_PCM_TRIGGER_STOP:
+               stop_endpoints(subs, 0, 0, 0);
+               subs->running = 0;
+               return 0;
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               subs->data_endpoint->retire_data_urb = NULL;
+               subs->running = 0;
+               return 0;
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               subs->data_endpoint->retire_data_urb = retire_capture_urb;
+               subs->running = 1;
+               return 0;
+       }
+
+       return -EINVAL;
+}
+
 static struct snd_pcm_ops snd_usb_playback_ops = {
        .open =         snd_usb_playback_open,
        .close =        snd_usb_playback_close,
index 961c9a2506865b9a84cb337606e80b21705f2b04..ebc1a5b5b3f1ba79c4c25ea66aadd34fd053e6d6 100644 (file)
@@ -25,6 +25,7 @@
 #include "usbaudio.h"
 #include "helper.h"
 #include "card.h"
+#include "endpoint.h"
 #include "proc.h"
 
 /* convert our full speed USB rate into sampling rate in Hz */
@@ -115,28 +116,33 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
        }
 }
 
+static void proc_dump_ep_status(struct snd_usb_substream *subs,
+                               struct snd_usb_endpoint *ep,
+                               struct snd_info_buffer *buffer)
+{
+       if (!ep)
+               return;
+       snd_iprintf(buffer, "    Packet Size = %d\n", ep->curpacksize);
+       snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
+                   snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
+                   ? get_full_speed_hz(ep->freqm)
+                   : get_high_speed_hz(ep->freqm),
+                   ep->freqm >> 16, ep->freqm & 0xffff);
+       if (ep->freqshift != INT_MIN) {
+               int res = 16 - ep->freqshift;
+               snd_iprintf(buffer, "    Feedback Format = %d.%d\n",
+                           (ep->syncmaxsize > 3 ? 32 : 24) - res, res);
+       }
+}
+
 static void proc_dump_substream_status(struct snd_usb_substream *subs, struct snd_info_buffer *buffer)
 {
        if (subs->running) {
-               unsigned int i;
                snd_iprintf(buffer, "  Status: Running\n");
                snd_iprintf(buffer, "    Interface = %d\n", subs->interface);
                snd_iprintf(buffer, "    Altset = %d\n", subs->altset_idx);
-               snd_iprintf(buffer, "    URBs = %d [ ", subs->nurbs);
-               for (i = 0; i < subs->nurbs; i++)
-                       snd_iprintf(buffer, "%d ", subs->dataurb[i].packets);
-               snd_iprintf(buffer, "]\n");
-               snd_iprintf(buffer, "    Packet Size = %d\n", subs->curpacksize);
-               snd_iprintf(buffer, "    Momentary freq = %u Hz (%#x.%04x)\n",
-                           snd_usb_get_speed(subs->dev) == USB_SPEED_FULL
-                           ? get_full_speed_hz(subs->freqm)
-                           : get_high_speed_hz(subs->freqm),
-                           subs->freqm >> 16, subs->freqm & 0xffff);
-               if (subs->freqshift != INT_MIN)
-                       snd_iprintf(buffer, "    Feedback Format = %d.%d\n",
-                                   (subs->syncmaxsize > 3 ? 32 : 24)
-                                               - (16 - subs->freqshift),
-                                   16 - subs->freqshift);
+               proc_dump_ep_status(subs, subs->data_endpoint, buffer);
+               proc_dump_ep_status(subs, subs->sync_endpoint, buffer);
        } else {
                snd_iprintf(buffer, "  Status: Stop\n");
        }
index 5ff8010b2d6f593080060fcba47fd6017dfabd5a..6b7d7a2b7baa3a83c68e35917d95515a3caa482a 100644 (file)
@@ -73,6 +73,31 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
        }
 }
 
+/*
+ * initialize the substream instance.
+ */
+
+static void snd_usb_init_substream(struct snd_usb_stream *as,
+                                  int stream,
+                                  struct audioformat *fp)
+{
+       struct snd_usb_substream *subs = &as->substream[stream];
+
+       INIT_LIST_HEAD(&subs->fmt_list);
+       spin_lock_init(&subs->lock);
+
+       subs->stream = as;
+       subs->direction = stream;
+       subs->dev = as->chip->dev;
+       subs->txfr_quirk = as->chip->txfr_quirk;
+
+       snd_usb_set_pcm_ops(as->pcm, stream);
+
+       list_add_tail(&fp->list, &subs->fmt_list);
+       subs->formats |= fp->formats;
+       subs->num_formats++;
+       subs->fmt_type = fp->fmt_type;
+}
 
 /*
  * add this endpoint to the chip instance.
@@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
                if (as->fmt_type != fp->fmt_type)
                        continue;
                subs = &as->substream[stream];
-               if (!subs->endpoint)
+               if (!subs->data_endpoint)
                        continue;
-               if (subs->endpoint == fp->endpoint) {
+               if (subs->data_endpoint->ep_num == fp->endpoint) {
                        list_add_tail(&fp->list, &subs->fmt_list);
                        subs->num_formats++;
                        subs->formats |= fp->formats;
@@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
                if (as->fmt_type != fp->fmt_type)
                        continue;
                subs = &as->substream[stream];
-               if (subs->endpoint)
+               if (subs->data_endpoint)
                        continue;
                err = snd_pcm_new_stream(as->pcm, stream, 1);
                if (err < 0)
index 3e2b035779362d6adc733ba5fa8396590e98e8d6..b8233ebe250f88648cc0baf2f6cfcfd13aa6de8f 100644 (file)
@@ -36,6 +36,7 @@ struct snd_usb_audio {
        struct snd_card *card;
        struct usb_interface *pm_intf;
        u32 usb_id;
+       struct mutex mutex;
        struct mutex shutdown_mutex;
        unsigned int shutdown:1;
        unsigned int probing:1;
@@ -46,6 +47,7 @@ struct snd_usb_audio {
        int num_suspended_intf;
 
        struct list_head pcm_list;      /* list of pcm streams */
+       struct list_head ep_list;       /* list of audio-related endpoints */
        int pcm_devs;
 
        struct list_head midi_list;     /* list of midi interfaces */