Merge branch 'topic/hda' into for-next
authorTakashi Iwai <tiwai@suse.de>
Tue, 8 Mar 2016 09:49:43 +0000 (10:49 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 8 Mar 2016 09:49:43 +0000 (10:49 +0100)
43 files changed:
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt [new file with mode: 0644]
include/sound/hda_chmap.h [new file with mode: 0644]
include/sound/jack.h
include/uapi/sound/asequencer.h
include/uapi/sound/asound.h
sound/core/Kconfig
sound/core/compress_offload.c
sound/core/jack.c
sound/core/timer.c
sound/drivers/mts64.c
sound/drivers/portman2x4.c
sound/firewire/bebob/bebob.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_midi.c
sound/firewire/bebob/bebob_pcm.c
sound/firewire/bebob/bebob_stream.c
sound/firewire/dice/dice-midi.c
sound/firewire/dice/dice-pcm.c
sound/firewire/dice/dice-stream.c
sound/firewire/dice/dice-transaction.c
sound/firewire/dice/dice.c
sound/firewire/dice/dice.h
sound/firewire/fireworks/fireworks.c
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/oxfw/oxfw-scs1x.c
sound/hda/Makefile
sound/hda/hdmi_chmap.c [new file with mode: 0644]
sound/mips/Kconfig
sound/mips/Makefile
sound/mips/au1x00.c [deleted file]
sound/pci/Kconfig
sound/pci/hda/Kconfig
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/thinkpad_helper.c
sound/soc/Kconfig
sound/usb/card.c
sound/usb/midi.c
sound/usb/midi.h
sound/usb/quirks.c
sound/usb/quirks.h

index 48148d6..fc53ccd 100644 (file)
@@ -1910,6 +1910,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                     - Default: 0x0000 
     ignore_ctl_error - Ignore any USB-controller regarding mixer
                       interface (default: no)
+    autoclock      - Enable auto-clock selection for UAC2 devices
+                     (default: yes)
+    quirk_alias            - Quirk alias list, pass strings like
+                     "0123abcd:5678beef", which applies the existing
+                     quirk for the device 5678:beef to a new device
+                     0123:abcd.
 
     This module supports multiple devices, autoprobe and hotplugging.
 
@@ -1919,6 +1925,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     NB: ignore_ctl_error=1 may help when you get an error at accessing
         the mixer element such as URB error -22.  This happens on some
         buggy USB device or the controller.
+    NB: quirk_alias option is provided only for testing / development.
+        If you want to have a proper support, contact to upstream for
+       adding the matching quirk in the driver code statically.
 
   Module snd-usb-caiaq
   --------------------
diff --git a/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt b/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt
new file mode 100644 (file)
index 0000000..82744ac
--- /dev/null
@@ -0,0 +1,74 @@
+To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin
+and dynamic pcm assignment.
+
+Virtual pin is an extension of per_pin. The most difference of DP MST
+from legacy is that DP MST introduces device entry. Each pin can contain
+several device entries. Each device entry behaves as a pin.
+
+As each pin may contain several device entries and each codec may contain
+several pins, if we use one pcm per per_pin, there will be many PCMs.
+The new solution is to create a few PCMs and to dynamically bind pcm to
+per_pin. Driver uses spec->dyn_pcm_assign flag to indicate whether to use
+the new solution.
+
+PCM
+===
+To be added
+
+
+Jack
+====
+
+Presume:
+ - MST must be dyn_pcm_assign, and it is acomp (for Intel scenario);
+ - NON-MST may or may not be dyn_pcm_assign, it can be acomp or !acomp;
+
+So there are the following scenarios:
+ a. MST (&& dyn_pcm_assign && acomp)
+ b. NON-MST && dyn_pcm_assign && acomp
+ c. NON-MST && !dyn_pcm_assign && !acomp
+
+Below discussion will ignore MST and NON-MST difference as it doesn't
+impact on jack handling too much.
+
+Driver uses struct hdmi_pcm pcm[] array in hdmi_spec and snd_jack is
+a member of hdmi_pcm. Each pin has one struct hdmi_pcm * pcm pointer.
+
+For !dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n] statically.
+
+For dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n]
+when monitor is hotplugged.
+
+
+Build Jack
+----------
+
+- dyn_pcm_assign
+Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly.
+
+- !dyn_pcm_assign
+Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically.
+
+
+Unsolicited Event Enabling
+--------------------------
+Enable unsolicited event if !acomp.
+
+
+Monitor Hotplug Event Handling
+------------------------------
+- acomp
+pin_eld_notify() -> check_presence_and_report() -> hdmi_present_sense() ->
+sync_eld_via_acomp().
+Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for
+both dyn_pcm_assign and !dyn_pcm_assign
+
+- !acomp
+Hdmi_unsol_event() -> hdmi_intrinsic_event() -> check_presence_and_report() ->
+hdmi_present_sense() -> hdmi_prepsent_sense_via_verbs()
+Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for dyn_pcm_assign.
+Use hda_jack mechanism to handle jack events.
+
+
+Others to be added later
+========================
diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h
new file mode 100644 (file)
index 0000000..e20d219
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * For multichannel support
+ */
+
+#ifndef __SOUND_HDA_CHMAP_H
+#define __SOUND_HDA_CHMAP_H
+
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+
+struct hdac_cea_channel_speaker_allocation {
+       int ca_index;
+       int speakers[8];
+
+       /* derived values, just for convenience */
+       int channels;
+       int spk_mask;
+};
+struct hdac_chmap;
+
+struct hdac_chmap_ops {
+       /*
+        * Helpers for producing the channel map TLVs. These can be overridden
+        * for devices that have non-standard mapping requirements.
+        */
+       int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels);
+       void (*cea_alloc_to_tlv_chmap)(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels);
+
+       /* check that the user-given chmap is supported */
+       int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
+                       int channels, unsigned char *chmap);
+
+       void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap);
+       void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
+                       unsigned char *chmap, int prepared);
+       bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx);
+
+       /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
+       int (*pin_get_slot_channel)(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot);
+       int (*pin_set_slot_channel)(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot, int channel);
+       void (*set_channel_count)(struct hdac_device *codec,
+                               hda_nid_t cvt_nid, int chs);
+};
+
+struct hdac_chmap {
+       unsigned int channels_max; /* max over all cvts */
+       struct hdac_chmap_ops ops;
+       struct hdac_device *hdac;
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+                               struct hdac_chmap *chmap);
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+                       int channels, bool chmap_set,
+                       bool non_pcm, unsigned char *map);
+int snd_hdac_get_active_channels(int ca);
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+                      hda_nid_t pin_nid, bool non_pcm, int ca,
+                      int channels, unsigned char *map,
+                      bool chmap_set);
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca);
+int snd_hdac_chmap_to_spk_mask(unsigned char c);
+int snd_hdac_spk_to_chmap(int spk);
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+                               struct hdac_chmap *chmap);
+#endif /* __SOUND_HDA_CHMAP_H */
index 23bede1..1e84bfb 100644 (file)
@@ -72,14 +72,16 @@ enum snd_jack_types {
 #define SND_JACK_SWITCH_TYPES 6
 
 struct snd_jack {
-       struct input_dev *input_dev;
        struct list_head kctl_list;
        struct snd_card *card;
+       const char *id;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+       struct input_dev *input_dev;
        int registered;
        int type;
-       const char *id;
        char name[100];
        unsigned int key[6];   /* Keep in sync with definitions above */
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        void *private_data;
        void (*private_free)(struct snd_jack *);
 };
@@ -89,10 +91,11 @@ struct snd_jack {
 int snd_jack_new(struct snd_card *card, const char *id, int type,
                 struct snd_jack **jack, bool initial_kctl, bool phantom_jack);
 int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask);
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
 int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
                     int keytype);
-
+#endif
 void snd_jack_report(struct snd_jack *jack, int status);
 
 #else
@@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name
        return 0;
 }
 
+static inline void snd_jack_report(struct snd_jack *jack, int status)
+{
+}
+
+#endif
+
+#if !defined(CONFIG_SND_JACK) || !defined(CONFIG_SND_JACK_INPUT_DEV)
 static inline void snd_jack_set_parent(struct snd_jack *jack,
                                       struct device *parent)
 {
@@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack,
 {
        return 0;
 }
-
-static inline void snd_jack_report(struct snd_jack *jack, int status)
-{
-}
-
-#endif
+#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */
 
 #endif
index 5a5fa49..af96f20 100644 (file)
@@ -594,14 +594,8 @@ struct snd_seq_query_subs {
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO        _IOWR('S', 0x41, struct snd_seq_queue_tempo)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO        _IOW ('S', 0x42, struct snd_seq_queue_tempo)
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER        _IOWR('S', 0x43, struct snd_seq_queue_owner)
-#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER        _IOW ('S', 0x44, struct snd_seq_queue_owner)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER        _IOWR('S', 0x45, struct snd_seq_queue_timer)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER        _IOW ('S', 0x46, struct snd_seq_queue_timer)
-/* XXX
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct snd_seq_queue_sync)
-#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct snd_seq_queue_sync)
-*/
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT       _IOWR('S', 0x49, struct snd_seq_queue_client)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT       _IOW ('S', 0x4a, struct snd_seq_queue_client)
 #define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL        _IOWR('S', 0x4b, struct snd_seq_client_pool)
index a82108e..67bf49d 100644 (file)
 #ifndef _UAPI__SOUND_ASOUND_H
 #define _UAPI__SOUND_ASOUND_H
 
+#if defined(__KERNEL__) || defined(__linux__)
 #include <linux/types.h>
+#else
+#include <sys/ioctl.h>
+#endif
 
 #ifndef __KERNEL__
 #include <stdlib.h>
index a2a1e24..6d12ca9 100644 (file)
@@ -24,12 +24,15 @@ config SND_RAWMIDI
 config SND_COMPRESS_OFFLOAD
        tristate
 
-# To be effective this also requires INPUT - users should say:
-#    select SND_JACK if INPUT=y || INPUT=SND
-# to avoid having to force INPUT on.
 config SND_JACK
        bool
 
+# enable input device support in jack layer
+config SND_JACK_INPUT_DEV
+       bool
+       depends on SND_JACK
+       default y if INPUT=y || INPUT=SND
+
 config SND_SEQUENCER
        tristate "Sequencer support"
        select SND_TIMER
index 7fac3ca..a9933c0 100644 (file)
@@ -69,11 +69,14 @@ struct snd_compr_file {
 
 /*
  * a note on stream states used:
- * we use follwing states in the compressed core
+ * we use following states in the compressed core
  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
- *     calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
+ *     calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
  *     state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
+ * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
+ *     playback only). User after setting up stream writes the data buffer
+ *     before starting the stream.
  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
  *     decoding/encoding and rendering/capturing data.
  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
@@ -286,6 +289,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
        mutex_lock(&stream->device->lock);
        /* write is allowed when stream is running or has been steup */
        if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
+           stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
                        stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
                mutex_unlock(&stream->device->lock);
                return -EBADFD;
@@ -700,7 +704,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
 
        /*
         * We are called with lock held. So drop the lock while we wait for
-        * drain complete notfication from the driver
+        * drain complete notification from the driver
         *
         * It is expected that driver will notify the drain completion and then
         * stream will be moved to SETUP state, even if draining resulted in an
@@ -755,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
        if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
                return -EPERM;
 
-       /* you can signal next track isf this is intended to be a gapless stream
+       /* you can signal next track if this is intended to be a gapless stream
         * and current track metadata is set
         */
        if (stream->metadata_set == false)
index 7237acb..f652e90 100644 (file)
@@ -32,6 +32,7 @@ struct snd_jack_kctl {
        unsigned int mask_bits; /* only masked status bits are reported via kctl */
 };
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_HEADPHONE_INSERT,
        SW_MICROPHONE_INSERT,
@@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_VIDEOOUT_INSERT,
        SW_LINEIN_INSERT,
 };
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 
 static int snd_jack_dev_disconnect(struct snd_device *device)
 {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        struct snd_jack *jack = device->device_data;
 
        if (!jack->input_dev)
@@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
        else
                input_free_device(jack->input_dev);
        jack->input_dev = NULL;
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        return 0;
 }
 
@@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device)
        return 0;
 }
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 static int snd_jack_dev_register(struct snd_device *device)
 {
        struct snd_jack *jack = device->device_data;
@@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device)
 
        return err;
 }
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 
 static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
 {
@@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        struct snd_jack *jack;
        struct snd_jack_kctl *jack_kctl = NULL;
        int err;
-       int i;
        static struct snd_device_ops ops = {
                .dev_free = snd_jack_dev_free,
+#ifdef CONFIG_SND_JACK_INPUT_DEV
                .dev_register = snd_jack_dev_register,
                .dev_disconnect = snd_jack_dev_disconnect,
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        };
 
        if (initial_kctl) {
@@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
 
        /* don't creat input device for phantom jack */
        if (!phantom_jack) {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+               int i;
+
                jack->input_dev = input_allocate_device();
                if (jack->input_dev == NULL) {
                        err = -ENOMEM;
@@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
                                input_set_capability(jack->input_dev, EV_SW,
                                                     jack_switch_types[i]);
 
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        }
 
        err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
@@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        return 0;
 
 fail_input:
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        input_free_device(jack->input_dev);
+#endif
        kfree(jack->id);
        kfree(jack);
        return err;
 }
 EXPORT_SYMBOL(snd_jack_new);
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 /**
  * snd_jack_set_parent - Set the parent device for a jack
  *
@@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
 
        jack->type |= type;
        jack->key[key] = keytype;
-
        return 0;
 }
 EXPORT_SYMBOL(snd_jack_set_key);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 
 /**
  * snd_jack_report - Report the current status of a jack
@@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key);
 void snd_jack_report(struct snd_jack *jack, int status)
 {
        struct snd_jack_kctl *jack_kctl;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        int i;
+#endif
 
        if (!jack)
                return;
@@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
                snd_kctl_jack_report(jack->card, jack_kctl->kctl,
                                            status & jack_kctl->mask_bits);
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        if (!jack->input_dev)
                return;
 
@@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
        }
 
        input_sync(jack->input_dev);
-
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 }
 EXPORT_SYMBOL(snd_jack_report);
index dca817f..aa1b15c 100644 (file)
@@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti,
        return 0;
 }
 
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
-
 /*
  * close a timer instance
  */
@@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
        if (snd_BUG_ON(!timeri))
                return -ENXIO;
 
+       mutex_lock(&register_mutex);
+       list_del(&timeri->open_list);
+
        /* force to stop the timer */
        snd_timer_stop(timeri);
 
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               /* wait, until the active callback is finished */
-               spin_lock_irq(&slave_active_lock);
-               while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
-                       spin_unlock_irq(&slave_active_lock);
-                       udelay(10);
-                       spin_lock_irq(&slave_active_lock);
-               }
-               spin_unlock_irq(&slave_active_lock);
-               mutex_lock(&register_mutex);
-               list_del(&timeri->open_list);
-               mutex_unlock(&register_mutex);
-       } else {
-               timer = timeri->timer;
-               if (snd_BUG_ON(!timer))
-                       goto out;
+       timer = timeri->timer;
+       if (timer) {
                /* wait, until the active callback is finished */
                spin_lock_irq(&timer->lock);
                while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -345,11 +332,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                        spin_lock_irq(&timer->lock);
                }
                spin_unlock_irq(&timer->lock);
-               mutex_lock(&register_mutex);
-               list_del(&timeri->open_list);
-               if (list_empty(&timer->open_list_head) &&
-                   timer->hw.close)
-                       timer->hw.close(timer);
+
                /* remove slave links */
                spin_lock_irq(&slave_active_lock);
                spin_lock(&timer->lock);
@@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                }
                spin_unlock(&timer->lock);
                spin_unlock_irq(&slave_active_lock);
-               /* release a card refcount for safe disconnection */
-               if (timer->card)
-                       put_device(&timer->card->card_dev);
-               mutex_unlock(&register_mutex);
+
+               /* slave doesn't need to release timer resources below */
+               if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+                       timer = NULL;
        }
- out:
+
        if (timeri->private_free)
                timeri->private_free(timeri);
        kfree(timeri->owner);
        kfree(timeri);
-       if (timer)
+
+       if (timer) {
+               if (list_empty(&timer->open_list_head) && timer->hw.close)
+                       timer->hw.close(timer);
+               /* release a card refcount for safe disconnection */
+               if (timer->card)
+                       put_device(&timer->card->card_dev);
                module_put(timer->module);
+       }
+
+       mutex_unlock(&register_mutex);
        return 0;
 }
 
@@ -395,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
 static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
 {
        struct snd_timer *timer;
-       unsigned long flags;
        unsigned long resolution = 0;
        struct snd_timer_instance *ts;
        struct timespec tstamp;
@@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
                return;
        if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                return;
-       spin_lock_irqsave(&timer->lock, flags);
        list_for_each_entry(ts, &ti->slave_active_head, active_list)
                if (ts->ccallback)
                        ts->ccallback(ts, event + 100, &tstamp, resolution);
-       spin_unlock_irqrestore(&timer->lock, flags);
 }
 
-static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
-                           unsigned long sticks)
+/* start/continue a master timer */
+static int snd_timer_start1(struct snd_timer_instance *timeri,
+                           bool start, unsigned long ticks)
 {
+       struct snd_timer *timer;
+       int result;
+       unsigned long flags;
+
+       timer = timeri->timer;
+       if (!timer)
+               return -EINVAL;
+
+       spin_lock_irqsave(&timer->lock, flags);
+       if (timer->card && timer->card->shutdown) {
+               result = -ENODEV;
+               goto unlock;
+       }
+       if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+                            SNDRV_TIMER_IFLG_START)) {
+               result = -EBUSY;
+               goto unlock;
+       }
+
+       if (start)
+               timeri->ticks = timeri->cticks = ticks;
+       else if (!timeri->cticks)
+               timeri->cticks = 1;
+       timeri->pticks = 0;
+
        list_move_tail(&timeri->active_list, &timer->active_list_head);
        if (timer->running) {
                if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                        goto __start_now;
                timer->flags |= SNDRV_TIMER_FLG_RESCHED;
                timeri->flags |= SNDRV_TIMER_IFLG_START;
-               return 1;       /* delayed start */
+               result = 1; /* delayed start */
        } else {
-               timer->sticks = sticks;
+               if (start)
+                       timer->sticks = ticks;
                timer->hw.start(timer);
              __start_now:
                timer->running++;
                timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
-               return 0;
+               result = 0;
        }
+       snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+                         SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
+       spin_unlock_irqrestore(&timer->lock, flags);
+       return result;
 }
 
-static int snd_timer_start_slave(struct snd_timer_instance *timeri)
+/* start/continue a slave timer */
+static int snd_timer_start_slave(struct snd_timer_instance *timeri,
+                                bool start)
 {
        unsigned long flags;
 
@@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
                spin_lock(&timeri->timer->lock);
                list_add_tail(&timeri->active_list,
                              &timeri->master->slave_active_head);
+               snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+                                 SNDRV_TIMER_EVENT_CONTINUE);
                spin_unlock(&timeri->timer->lock);
        }
        spin_unlock_irqrestore(&slave_active_lock, flags);
        return 1; /* delayed start */
 }
 
-/*
- *  start the timer instance
- */
-int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+/* stop/pause a master timer */
+static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
 {
        struct snd_timer *timer;
-       int result = -EINVAL;
+       int result = 0;
        unsigned long flags;
 
-       if (timeri == NULL || ticks < 1)
-               return -EINVAL;
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               result = snd_timer_start_slave(timeri);
-               if (result >= 0)
-                       snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
-               return result;
-       }
-       timer = timeri->timer;
-       if (timer == NULL)
-               return -EINVAL;
-       if (timer->card && timer->card->shutdown)
-               return -ENODEV;
-       spin_lock_irqsave(&timer->lock, flags);
-       if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
-                            SNDRV_TIMER_IFLG_START)) {
-               result = -EBUSY;
-               goto unlock;
-       }
-       timeri->ticks = timeri->cticks = ticks;
-       timeri->pticks = 0;
-       result = snd_timer_start1(timer, timeri, ticks);
- unlock:
-       spin_unlock_irqrestore(&timer->lock, flags);
-       if (result >= 0)
-               snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
-       return result;
-}
-
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
-{
-       struct snd_timer *timer;
-       unsigned long flags;
-
-       if (snd_BUG_ON(!timeri))
-               return -ENXIO;
-
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               spin_lock_irqsave(&slave_active_lock, flags);
-               if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
-                       spin_unlock_irqrestore(&slave_active_lock, flags);
-                       return -EBUSY;
-               }
-               if (timeri->timer)
-                       spin_lock(&timeri->timer->lock);
-               timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
-               list_del_init(&timeri->ack_list);
-               list_del_init(&timeri->active_list);
-               if (timeri->timer)
-                       spin_unlock(&timeri->timer->lock);
-               spin_unlock_irqrestore(&slave_active_lock, flags);
-               goto __end;
-       }
        timer = timeri->timer;
        if (!timer)
                return -EINVAL;
        spin_lock_irqsave(&timer->lock, flags);
        if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
                               SNDRV_TIMER_IFLG_START))) {
-               spin_unlock_irqrestore(&timer->lock, flags);
-               return -EBUSY;
+               result = -EBUSY;
+               goto unlock;
        }
        list_del_init(&timeri->ack_list);
        list_del_init(&timeri->active_list);
-       if (timer->card && timer->card->shutdown) {
-               spin_unlock_irqrestore(&timer->lock, flags);
-               return 0;
+       if (timer->card && timer->card->shutdown)
+               goto unlock;
+       if (stop) {
+               timeri->cticks = timeri->ticks;
+               timeri->pticks = 0;
        }
        if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
            !(--timer->running)) {
@@ -556,13 +528,49 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
                }
        }
        timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+       snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+                         SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
        spin_unlock_irqrestore(&timer->lock, flags);
-      __end:
-       if (event != SNDRV_TIMER_EVENT_RESOLUTION)
-               snd_timer_notify1(timeri, event);
+       return result;
+}
+
+/* stop/pause a slave timer */
+static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&slave_active_lock, flags);
+       if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
+               spin_unlock_irqrestore(&slave_active_lock, flags);
+               return -EBUSY;
+       }
+       timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+       if (timeri->timer) {
+               spin_lock(&timeri->timer->lock);
+               list_del_init(&timeri->ack_list);
+               list_del_init(&timeri->active_list);
+               snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+                                 SNDRV_TIMER_EVENT_CONTINUE);
+               spin_unlock(&timeri->timer->lock);
+       }
+       spin_unlock_irqrestore(&slave_active_lock, flags);
        return 0;
 }
 
+/*
+ *  start the timer instance
+ */
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+{
+       if (timeri == NULL || ticks < 1)
+               return -EINVAL;
+       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+               return snd_timer_start_slave(timeri, true);
+       else
+               return snd_timer_start1(timeri, true, ticks);
+}
+
 /*
  * stop the timer instance.
  *
@@ -570,21 +578,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
  */
 int snd_timer_stop(struct snd_timer_instance *timeri)
 {
-       struct snd_timer *timer;
-       unsigned long flags;
-       int err;
-
-       err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
-       if (err < 0)
-               return err;
-       timer = timeri->timer;
-       if (!timer)
-               return -EINVAL;
-       spin_lock_irqsave(&timer->lock, flags);
-       timeri->cticks = timeri->ticks;
-       timeri->pticks = 0;
-       spin_unlock_irqrestore(&timer->lock, flags);
-       return 0;
+       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+               return snd_timer_stop_slave(timeri, true);
+       else
+               return snd_timer_stop1(timeri, true);
 }
 
 /*
@@ -592,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
  */
 int snd_timer_continue(struct snd_timer_instance *timeri)
 {
-       struct snd_timer *timer;
-       int result = -EINVAL;
-       unsigned long flags;
-
-       if (timeri == NULL)
-               return result;
        if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
-               return snd_timer_start_slave(timeri);
-       timer = timeri->timer;
-       if (! timer)
-               return -EINVAL;
-       if (timer->card && timer->card->shutdown)
-               return -ENODEV;
-       spin_lock_irqsave(&timer->lock, flags);
-       if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
-               result = -EBUSY;
-               goto unlock;
-       }
-       if (!timeri->cticks)
-               timeri->cticks = 1;
-       timeri->pticks = 0;
-       result = snd_timer_start1(timer, timeri, timer->sticks);
- unlock:
-       spin_unlock_irqrestore(&timer->lock, flags);
-       snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
-       return result;
+               return snd_timer_start_slave(timeri, false);
+       else
+               return snd_timer_start1(timeri, false, 0);
 }
 
 /*
@@ -625,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
  */
 int snd_timer_pause(struct snd_timer_instance * timeri)
 {
-       return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
+       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+               return snd_timer_stop_slave(timeri, false);
+       else
+               return snd_timer_stop1(timeri, false);
 }
 
 /*
index 2a008a9..fd4d18d 100644 (file)
@@ -65,8 +65,6 @@ struct mts64 {
        struct snd_card *card;
        struct snd_rawmidi *rmidi;
        struct pardevice *pardev;
-       int pardev_claimed;
-
        int open_count;
        int current_midi_output_port;
        int current_midi_input_port;
@@ -850,30 +848,6 @@ __out:
        spin_unlock(&mts->lock);
 }
 
-static int snd_mts64_probe_port(struct parport *p)
-{
-       struct pardevice *pardev;
-       int res;
-
-       pardev = parport_register_device(p, DRIVER_NAME,
-                                        NULL, NULL, NULL,
-                                        0, NULL);
-       if (!pardev)
-               return -EIO;
-       
-       if (parport_claim(pardev)) {
-               parport_unregister_device(pardev);
-               return -EIO;
-       }
-
-       res = mts64_probe(p);
-
-       parport_release(pardev);
-       parport_unregister_device(pardev);
-
-       return res;
-}
-
 static void snd_mts64_attach(struct parport *p)
 {
        struct platform_device *device;
@@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p)
        /* nothing to do here */
 }
 
+static int snd_mts64_dev_probe(struct pardevice *pardev)
+{
+       if (strcmp(pardev->name, DRIVER_NAME))
+               return -ENODEV;
+
+       return 0;
+}
+
 static struct parport_driver mts64_parport_driver = {
-       .name   = "mts64",
-       .attach = snd_mts64_attach,
-       .detach = snd_mts64_detach
+       .name           = "mts64",
+       .probe          = snd_mts64_dev_probe,
+       .match_port     = snd_mts64_attach,
+       .detach         = snd_mts64_detach,
+       .devmodel       = true,
 };
 
 /*********************************************************************
@@ -922,8 +906,7 @@ static void snd_mts64_card_private_free(struct snd_card *card)
        struct pardevice *pardev = mts->pardev;
 
        if (pardev) {
-               if (mts->pardev_claimed)
-                       parport_release(pardev);
+               parport_release(pardev);
                parport_unregister_device(pardev);
        }
 
@@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev)
        struct snd_card *card = NULL;
        struct mts64 *mts = NULL;
        int err;
+       struct pardev_cb mts64_cb = {
+               .preempt = NULL,
+               .wakeup = NULL,
+               .irq_func = snd_mts64_interrupt,        /* ISR */
+               .flags = PARPORT_DEV_EXCL,              /* flags */
+       };
 
        p = platform_get_drvdata(pdev);
        platform_set_drvdata(pdev, NULL);
@@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev)
                return -ENODEV;
        if (!enable[dev]) 
                return -ENOENT;
-       if ((err = snd_mts64_probe_port(p)) < 0)
-               return err;
 
        err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
                           0, &card);
@@ -960,40 +947,42 @@ static int snd_mts64_probe(struct platform_device *pdev)
        sprintf(card->longname,  "%s at 0x%lx, irq %i", 
                card->shortname, p->base, p->irq);
 
-       pardev = parport_register_device(p,                   /* port */
-                                        DRIVER_NAME,         /* name */
-                                        NULL,                /* preempt */
-                                        NULL,                /* wakeup */
-                                        snd_mts64_interrupt, /* ISR */
-                                        PARPORT_DEV_EXCL,    /* flags */
-                                        (void *)card);       /* private */
-       if (pardev == NULL) {
+       mts64_cb.private = card;                         /* private */
+       pardev = parport_register_dev_model(p,           /* port */
+                                           DRIVER_NAME, /* name */
+                                           &mts64_cb,   /* callbacks */
+                                           pdev->id);   /* device number */
+       if (!pardev) {
                snd_printd("Cannot register pardevice\n");
                err = -EIO;
                goto __err;
        }
 
+       /* claim parport */
+       if (parport_claim(pardev)) {
+               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+               err = -EIO;
+               goto free_pardev;
+       }
+
        if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
                snd_printd("Cannot create main component\n");
-               parport_unregister_device(pardev);
-               goto __err;
+               goto release_pardev;
        }
        card->private_data = mts;
        card->private_free = snd_mts64_card_private_free;
+
+       err = mts64_probe(p);
+       if (err) {
+               err = -EIO;
+               goto __err;
+       }
        
        if ((err = snd_mts64_rawmidi_create(card)) < 0) {
                snd_printd("Creating Rawmidi component failed\n");
                goto __err;
        }
 
-       /* claim parport */
-       if (parport_claim(pardev)) {
-               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
-               err = -EIO;
-               goto __err;
-       }
-       mts->pardev_claimed = 1;
-
        /* init device */
        if ((err = mts64_device_init(p)) < 0)
                goto __err;
@@ -1009,6 +998,10 @@ static int snd_mts64_probe(struct platform_device *pdev)
        snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
        return 0;
 
+release_pardev:
+       parport_release(pardev);
+free_pardev:
+       parport_unregister_device(pardev);
 __err:
        snd_card_free(card);
        return err;
@@ -1024,7 +1017,6 @@ static int snd_mts64_remove(struct platform_device *pdev)
        return 0;
 }
 
-
 static struct platform_driver snd_mts64_driver = {
        .probe  = snd_mts64_probe,
        .remove = snd_mts64_remove,
index 464385a..189e3e7 100644 (file)
@@ -83,8 +83,6 @@ struct portman {
        struct snd_card *card;
        struct snd_rawmidi *rmidi;
        struct pardevice *pardev;
-       int pardev_claimed;
-
        int open_count;
        int mode[PORTMAN_NUM_INPUT_PORTS];
        struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
@@ -648,30 +646,6 @@ static void snd_portman_interrupt(void *userdata)
        spin_unlock(&pm->reg_lock);
 }
 
-static int snd_portman_probe_port(struct parport *p)
-{
-       struct pardevice *pardev;
-       int res;
-
-       pardev = parport_register_device(p, DRIVER_NAME,
-                                        NULL, NULL, NULL,
-                                        0, NULL);
-       if (!pardev)
-               return -EIO;
-       
-       if (parport_claim(pardev)) {
-               parport_unregister_device(pardev);
-               return -EIO;
-       }
-
-       res = portman_probe(p);
-
-       parport_release(pardev);
-       parport_unregister_device(pardev);
-
-       return res ? -EIO : 0;
-}
-
 static void snd_portman_attach(struct parport *p)
 {
        struct platform_device *device;
@@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p)
        /* nothing to do here */
 }
 
+static int snd_portman_dev_probe(struct pardevice *pardev)
+{
+       if (strcmp(pardev->name, DRIVER_NAME))
+               return -ENODEV;
+
+       return 0;
+}
+
 static struct parport_driver portman_parport_driver = {
-       .name   = "portman2x4",
-       .attach = snd_portman_attach,
-       .detach = snd_portman_detach
+       .name           = "portman2x4",
+       .probe          = snd_portman_dev_probe,
+       .match_port     = snd_portman_attach,
+       .detach         = snd_portman_detach,
+       .devmodel       = true,
 };
 
 /*********************************************************************
@@ -720,8 +704,7 @@ static void snd_portman_card_private_free(struct snd_card *card)
        struct pardevice *pardev = pm->pardev;
 
        if (pardev) {
-               if (pm->pardev_claimed)
-                       parport_release(pardev);
+               parport_release(pardev);
                parport_unregister_device(pardev);
        }
 
@@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev)
        struct snd_card *card = NULL;
        struct portman *pm = NULL;
        int err;
+       struct pardev_cb portman_cb = {
+               .preempt = NULL,
+               .wakeup = NULL,
+               .irq_func = snd_portman_interrupt,      /* ISR */
+               .flags = PARPORT_DEV_EXCL,              /* flags */
+       };
 
        p = platform_get_drvdata(pdev);
        platform_set_drvdata(pdev, NULL);
@@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev)
        if (!enable[dev]) 
                return -ENOENT;
 
-       if ((err = snd_portman_probe_port(p)) < 0)
-               return err;
-
        err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
                           0, &card);
        if (err < 0) {
@@ -759,40 +745,42 @@ static int snd_portman_probe(struct platform_device *pdev)
        sprintf(card->longname,  "%s at 0x%lx, irq %i", 
                card->shortname, p->base, p->irq);
 
-       pardev = parport_register_device(p,                     /* port */
-                                        DRIVER_NAME,           /* name */
-                                        NULL,                  /* preempt */
-                                        NULL,                  /* wakeup */
-                                        snd_portman_interrupt, /* ISR */
-                                        PARPORT_DEV_EXCL,      /* flags */
-                                        (void *)card);         /* private */
+       portman_cb.private = card;                         /* private */
+       pardev = parport_register_dev_model(p,             /* port */
+                                           DRIVER_NAME,   /* name */
+                                           &portman_cb,   /* callbacks */
+                                           pdev->id);     /* device number */
        if (pardev == NULL) {
                snd_printd("Cannot register pardevice\n");
                err = -EIO;
                goto __err;
        }
 
+       /* claim parport */
+       if (parport_claim(pardev)) {
+               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+               err = -EIO;
+               goto free_pardev;
+       }
+
        if ((err = portman_create(card, pardev, &pm)) < 0) {
                snd_printd("Cannot create main component\n");
-               parport_unregister_device(pardev);
-               goto __err;
+               goto release_pardev;
        }
        card->private_data = pm;
        card->private_free = snd_portman_card_private_free;
+
+       err = portman_probe(p);
+       if (err) {
+               err = -EIO;
+               goto __err;
+       }
        
        if ((err = snd_portman_rawmidi_create(card)) < 0) {
                snd_printd("Creating Rawmidi component failed\n");
                goto __err;
        }
 
-       /* claim parport */
-       if (parport_claim(pardev)) {
-               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
-               err = -EIO;
-               goto __err;
-       }
-       pm->pardev_claimed = 1;
-
        /* init device */
        if ((err = portman_device_init(pm)) < 0)
                goto __err;
@@ -808,6 +796,10 @@ static int snd_portman_probe(struct platform_device *pdev)
        snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
        return 0;
 
+release_pardev:
+       parport_release(pardev);
+free_pardev:
+       parport_unregister_device(pardev);
 __err:
        snd_card_free(card);
        return err;
index 091290d..3e4e075 100644 (file)
@@ -300,6 +300,22 @@ error:
        return err;
 }
 
+/*
+ * This driver doesn't update streams in bus reset handler.
+ *
+ * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with
+ * discontinued counter at bus reset. This discontinuity is immediately
+ * detected in packet streaming layer, then it sets XRUN to PCM substream.
+ *
+ * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation.
+ * Then, they can recover the PCM substream by executing ioctl(2) with
+ * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers
+ * restart packet streaming.
+ *
+ * The above processing may be executed before this bus-reset handler is
+ * executed. When this handler updates streams with current isochronous
+ * channels, the streams already have the current ones.
+ */
 static void
 bebob_update(struct fw_unit *unit)
 {
@@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit)
                return;
 
        fcp_bus_reset(bebob->unit);
-       snd_bebob_stream_update_duplex(bebob);
 
        if (bebob->deferred_registration) {
                if (snd_card_register(bebob->card) < 0) {
@@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit)
        if (bebob == NULL)
                return;
 
-       /* Awake bus-reset waiters. */
-       if (!completion_done(&bebob->bus_reset))
-               complete_all(&bebob->bus_reset);
-
        /* No need to wait for releasing card object in this context. */
        snd_card_free_when_closed(bebob->card);
 }
index 4d8fcc7..b50bb33 100644 (file)
@@ -88,8 +88,6 @@ struct snd_bebob {
        unsigned int midi_input_ports;
        unsigned int midi_output_ports;
 
-       /* for bus reset quirk */
-       struct completion bus_reset;
        bool connected;
 
        struct amdtp_stream *master;
@@ -97,7 +95,7 @@ struct snd_bebob {
        struct amdtp_stream rx_stream;
        struct cmp_connection out_conn;
        struct cmp_connection in_conn;
-       atomic_t substreams_counter;
+       unsigned int substreams_counter;
 
        struct snd_bebob_stream_formation
                tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@@ -219,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob);
 int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
 int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
 void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
-void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
 
 void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
index 90d95be..868eb0d 100644 (file)
@@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
        if (err < 0)
                goto end;
 
-       atomic_inc(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter++;
        err = snd_bebob_stream_start_duplex(bebob, 0);
+       mutex_unlock(&bebob->mutex);
        if (err < 0)
                snd_bebob_stream_lock_release(bebob);
 end:
@@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
        if (err < 0)
                goto end;
 
-       atomic_inc(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter++;
        err = snd_bebob_stream_start_duplex(bebob, 0);
+       mutex_unlock(&bebob->mutex);
        if (err < 0)
                snd_bebob_stream_lock_release(bebob);
 end:
@@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_bebob *bebob = substream->rmidi->private_data;
 
-       atomic_dec(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter--;
        snd_bebob_stream_stop_duplex(bebob);
+       mutex_unlock(&bebob->mutex);
 
        snd_bebob_stream_lock_release(bebob);
        return 0;
@@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_bebob *bebob = substream->rmidi->private_data;
 
-       atomic_dec(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter--;
        snd_bebob_stream_stop_duplex(bebob);
+       mutex_unlock(&bebob->mutex);
 
        snd_bebob_stream_lock_release(bebob);
        return 0;
index ef224d6..5d7b934 100644 (file)
@@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                return err;
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-               atomic_inc(&bebob->substreams_counter);
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter++;
+               mutex_unlock(&bebob->mutex);
+       }
 
        amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
 
@@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                return err;
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-               atomic_inc(&bebob->substreams_counter);
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter++;
+               mutex_unlock(&bebob->mutex);
+       }
 
        amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
 
@@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               atomic_dec(&bebob->substreams_counter);
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter--;
+               mutex_unlock(&bebob->mutex);
+       }
 
        snd_bebob_stream_stop_duplex(bebob);
 
@@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               atomic_dec(&bebob->substreams_counter);
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter--;
+               mutex_unlock(&bebob->mutex);
+       }
 
        snd_bebob_stream_stop_duplex(bebob);
 
index 5022c9b..77cbb02 100644 (file)
@@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
                destroy_both_connections(bebob);
                goto end;
        }
-       /* See comments in next function */
-       init_completion(&bebob->bus_reset);
+
        bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
 
        /*
@@ -588,29 +587,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
        struct amdtp_stream *master, *slave;
        enum cip_flags sync_mode;
        unsigned int curr_rate;
-       bool updated = false;
        int err = 0;
 
-       /*
-        * Normal BeBoB firmware has a quirk at bus reset to transmits packets
-        * with discontinuous value in dbc field.
-        *
-        * This 'struct completion' is used to call .update() at first to update
-        * connections/streams. Next following codes handle streaming error.
-        */
-       if (amdtp_streaming_error(&bebob->tx_stream)) {
-               if (completion_done(&bebob->bus_reset))
-                       reinit_completion(&bebob->bus_reset);
-
-               updated = (wait_for_completion_interruptible_timeout(
-                               &bebob->bus_reset,
-                               msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
-       }
-
-       mutex_lock(&bebob->mutex);
-
        /* Need no substreams */
-       if (atomic_read(&bebob->substreams_counter) == 0)
+       if (bebob->substreams_counter == 0)
                goto end;
 
        err = get_sync_mode(bebob, &sync_mode);
@@ -642,8 +622,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
                amdtp_stream_stop(master);
        if (amdtp_streaming_error(slave))
                amdtp_stream_stop(slave);
-       if (!updated &&
-           !amdtp_stream_running(master) && !amdtp_stream_running(slave))
+       if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
                break_both_connections(bebob);
 
        /* stop streams if rate is different */
@@ -741,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
                }
        }
 end:
-       mutex_unlock(&bebob->mutex);
        return err;
 }
 
@@ -757,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
                master = &bebob->tx_stream;
        }
 
-       mutex_lock(&bebob->mutex);
-
-       if (atomic_read(&bebob->substreams_counter) == 0) {
+       if (bebob->substreams_counter == 0) {
                amdtp_stream_pcm_abort(master);
                amdtp_stream_stop(master);
 
@@ -768,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
 
                break_both_connections(bebob);
        }
-
-       mutex_unlock(&bebob->mutex);
-}
-
-void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
-{
-       /* vs. XRUN recovery due to discontinuity at bus reset */
-       mutex_lock(&bebob->mutex);
-
-       if ((cmp_connection_update(&bebob->in_conn) < 0) ||
-           (cmp_connection_update(&bebob->out_conn) < 0)) {
-               amdtp_stream_pcm_abort(&bebob->rx_stream);
-               amdtp_stream_pcm_abort(&bebob->tx_stream);
-               amdtp_stream_stop(&bebob->rx_stream);
-               amdtp_stream_stop(&bebob->tx_stream);
-               break_both_connections(bebob);
-       } else {
-               amdtp_stream_update(&bebob->rx_stream);
-               amdtp_stream_update(&bebob->tx_stream);
-       }
-
-       /* wake up stream_start_duplex() */
-       if (!completion_done(&bebob->bus_reset))
-               complete_all(&bebob->bus_reset);
-
-       mutex_unlock(&bebob->mutex);
 }
 
 /*
index 151b09f..2461311 100644 (file)
@@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice,
 
 int snd_dice_create_midi(struct snd_dice *dice)
 {
+       __be32 reg;
        struct snd_rawmidi *rmidi;
        struct snd_rawmidi_str *str;
-       unsigned int i, midi_in_ports, midi_out_ports;
+       unsigned int midi_in_ports, midi_out_ports;
        int err;
 
-       midi_in_ports = midi_out_ports = 0;
-       for (i = 0; i < 3; i++) {
-               midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
-               midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
-       }
+       /*
+        * Use the number of MIDI conformant data channel at current sampling
+        * transfer frequency.
+        */
+       err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
+                                          &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+       midi_in_ports = be32_to_cpu(reg);
+
+       err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
+                                          &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+       midi_out_ports = be32_to_cpu(reg);
 
        if (midi_in_ports + midi_out_ports == 0)
                return 0;
index 9b34319..a5c9b58 100644 (file)
@@ -9,99 +9,40 @@
 
 #include "dice.h"
 
-static int dice_rate_constraint(struct snd_pcm_hw_params *params,
-                               struct snd_pcm_hw_rule *rule)
-{
-       struct snd_pcm_substream *substream = rule->private;
-       struct snd_dice *dice = substream->private_data;
-
-       const struct snd_interval *c =
-               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       struct snd_interval *r =
-               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_interval rates = {
-               .min = UINT_MAX, .max = 0, .integer = 1
-       };
-       unsigned int i, rate, mode, *pcm_channels;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               pcm_channels = dice->tx_channels;
-       else
-               pcm_channels = dice->rx_channels;
-
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-               rate = snd_dice_rates[i];
-               if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-                       continue;
-
-               if (!snd_interval_test(c, pcm_channels[mode]))
-                       continue;
-
-               rates.min = min(rates.min, rate);
-               rates.max = max(rates.max, rate);
-       }
-
-       return snd_interval_refine(r, &rates);
-}
-
-static int dice_channels_constraint(struct snd_pcm_hw_params *params,
-                                   struct snd_pcm_hw_rule *rule)
-{
-       struct snd_pcm_substream *substream = rule->private;
-       struct snd_dice *dice = substream->private_data;
-
-       const struct snd_interval *r =
-               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_interval *c =
-               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       struct snd_interval channels = {
-               .min = UINT_MAX, .max = 0, .integer = 1
-       };
-       unsigned int i, rate, mode, *pcm_channels;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               pcm_channels = dice->tx_channels;
-       else
-               pcm_channels = dice->rx_channels;
-
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-               rate = snd_dice_rates[i];
-               if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-                       continue;
-
-               if (!snd_interval_test(r, rate))
-                       continue;
-
-               channels.min = min(channels.min, pcm_channels[mode]);
-               channels.max = max(channels.max, pcm_channels[mode]);
-       }
-
-       return snd_interval_refine(c, &channels);
-}
-
-static void limit_channels_and_rates(struct snd_dice *dice,
-                                    struct snd_pcm_runtime *runtime,
-                                    unsigned int *pcm_channels)
+static int limit_channels_and_rates(struct snd_dice *dice,
+                                   struct snd_pcm_runtime *runtime,
+                                   struct amdtp_stream *stream)
 {
        struct snd_pcm_hardware *hw = &runtime->hw;
-       unsigned int i, rate, mode;
+       unsigned int rate;
+       __be32 reg[2];
+       int err;
 
-       hw->channels_min = UINT_MAX;
-       hw->channels_max = 0;
+       /*
+        * Retrieve current Multi Bit Linear Audio data channel and limit to
+        * it.
+        */
+       if (stream == &dice->tx_stream) {
+               err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
+                                                  reg, sizeof(reg));
+       } else {
+               err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
+                                                  reg, sizeof(reg));
+       }
+       if (err < 0)
+               return err;
 
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-               rate = snd_dice_rates[i];
-               if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-                       continue;
-               hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+       hw->channels_min = hw->channels_max = be32_to_cpu(reg[0]);
 
-               if (pcm_channels[mode] == 0)
-                       continue;
-               hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
-               hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
-       }
+       /* Retrieve current sampling transfer frequency and limit to it. */
+       err = snd_dice_transaction_get_rate(dice, &rate);
+       if (err < 0)
+               return err;
 
+       hw->rates = snd_pcm_rate_to_rate_bit(rate);
        snd_pcm_limit_hw_rates(runtime);
+
+       return 0;
 }
 
 static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
@@ -122,7 +63,6 @@ static int init_hw_info(struct snd_dice *dice,
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_pcm_hardware *hw = &runtime->hw;
        struct amdtp_stream *stream;
-       unsigned int *pcm_channels;
        int err;
 
        hw->info = SNDRV_PCM_INFO_MMAP |
@@ -135,37 +75,22 @@ static int init_hw_info(struct snd_dice *dice,
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                hw->formats = AM824_IN_PCM_FORMAT_BITS;
                stream = &dice->tx_stream;
-               pcm_channels = dice->tx_channels;
        } else {
                hw->formats = AM824_OUT_PCM_FORMAT_BITS;
                stream = &dice->rx_stream;
-               pcm_channels = dice->rx_channels;
        }
 
-       limit_channels_and_rates(dice, runtime, pcm_channels);
-       limit_period_and_buffer(hw);
-
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                 dice_rate_constraint, substream,
-                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
-       if (err < 0)
-               goto end;
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                 dice_channels_constraint, substream,
-                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       err = limit_channels_and_rates(dice, runtime, stream);
        if (err < 0)
-               goto end;
+               return err;
+       limit_period_and_buffer(hw);
 
-       err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
-end:
-       return err;
+       return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
 }
 
 static int pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
-       unsigned int source, rate;
-       bool internal;
        int err;
 
        err = snd_dice_stream_lock_try(dice);
@@ -176,39 +101,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
        if (err < 0)
                goto err_locked;
 
-       err = snd_dice_transaction_get_clock_source(dice, &source);
-       if (err < 0)
-               goto err_locked;
-       switch (source) {
-       case CLOCK_SOURCE_AES1:
-       case CLOCK_SOURCE_AES2:
-       case CLOCK_SOURCE_AES3:
-       case CLOCK_SOURCE_AES4:
-       case CLOCK_SOURCE_AES_ANY:
-       case CLOCK_SOURCE_ADAT:
-       case CLOCK_SOURCE_TDIF:
-       case CLOCK_SOURCE_WC:
-               internal = false;
-               break;
-       default:
-               internal = true;
-               break;
-       }
-
-       /*
-        * When source of clock is not internal or any PCM streams are running,
-        * available sampling rate is limited at current sampling rate.
-        */
-       if (!internal ||
-           amdtp_stream_pcm_running(&dice->tx_stream) ||
-           amdtp_stream_pcm_running(&dice->rx_stream)) {
-               err = snd_dice_transaction_get_rate(dice, &rate);
-               if (err < 0)
-                       goto err_locked;
-               substream->runtime->hw.rate_min = rate;
-               substream->runtime->hw.rate_max = rate;
-       }
-
        snd_pcm_set_sync(substream);
 end:
        return err;
@@ -402,17 +294,30 @@ int snd_dice_create_pcm(struct snd_dice *dice)
                .page      = snd_pcm_lib_get_vmalloc_page,
                .mmap      = snd_pcm_lib_mmap_vmalloc,
        };
+       __be32 reg;
        struct snd_pcm *pcm;
-       unsigned int i, capture, playback;
+       unsigned int capture, playback;
        int err;
 
-       capture = playback = 0;
-       for (i = 0; i < 3; i++) {
-               if (dice->tx_channels[i] > 0)
-                       capture = 1;
-               if (dice->rx_channels[i] > 0)
-                       playback = 1;
-       }
+       /*
+        * Check whether PCM substreams are required.
+        *
+        * TODO: in the case that any PCM substreams are not avail at a certain
+        * sampling transfer frequency?
+        */
+       err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
+                                          &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+       if (be32_to_cpu(reg) > 0)
+               capture = 1;
+
+       err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
+                                          &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+       if (be32_to_cpu(reg) > 0)
+               playback = 1;
 
        err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
        if (err < 0)
index a6a39f7..df035b1 100644 (file)
@@ -10,6 +10,7 @@
 #include "dice.h"
 
 #define        CALLBACK_TIMEOUT        200
+#define NOTIFICATION_TIMEOUT_MS        (2 * MSEC_PER_SEC)
 
 const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
        /* mode 0 */
@@ -24,21 +25,44 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
        [6] = 192000,
 };
 
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
-                                 unsigned int *mode)
+/*
+ * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
+ * to GLOBAL_STATUS. Especially, just after powering on, these are different.
+ */
+static int ensure_phase_lock(struct snd_dice *dice)
 {
-       int i;
+       __be32 reg, nominal;
+       int err;
+
+       err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+                                              &reg, sizeof(reg));
+       if (err < 0)
+               return err;
 
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
-               if (!(dice->clock_caps & BIT(i)))
-                       continue;
-               if (snd_dice_rates[i] != rate)
-                       continue;
+       if (completion_done(&dice->clock_accepted))
+               reinit_completion(&dice->clock_accepted);
 
-               *mode = (i - 1) / 2;
-               return 0;
+       err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+                                               &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+
+       if (wait_for_completion_timeout(&dice->clock_accepted,
+                       msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+               /*
+                * Old versions of Dice firmware transfer no notification when
+                * the same clock status as current one is set. In this case,
+                * just check current clock status.
+                */
+               err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
+                                               &nominal, sizeof(nominal));
+               if (err < 0)
+                       return err;
+               if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
+                       return -ETIMEDOUT;
        }
-       return -EINVAL;
+
+       return 0;
 }
 
 static void release_resources(struct snd_dice *dice,
@@ -99,23 +123,27 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
                        unsigned int rate)
 {
        struct fw_iso_resources *resources;
-       unsigned int i, mode, pcm_chs, midi_ports;
+       __be32 reg[2];
+       unsigned int i, pcm_chs, midi_ports;
        bool double_pcm_frames;
        int err;
 
-       err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
-       if (err < 0)
-               goto end;
        if (stream == &dice->tx_stream) {
                resources = &dice->tx_resources;
-               pcm_chs = dice->tx_channels[mode];
-               midi_ports = dice->tx_midi_ports[mode];
+               err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
+                                                  reg, sizeof(reg));
        } else {
                resources = &dice->rx_resources;
-               pcm_chs = dice->rx_channels[mode];
-               midi_ports = dice->rx_midi_ports[mode];
+               err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
+                                                  reg, sizeof(reg));
        }
 
+       if (err < 0)
+               goto end;
+
+       pcm_chs = be32_to_cpu(reg[0]);
+       midi_ports = be32_to_cpu(reg[1]);
+
        /*
         * At 176.4/192.0 kHz, Dice has a quirk to transfer two PCM frames in
         * one data block of AMDTP packet. Thus sampling transfer frequency is
@@ -126,7 +154,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
         * For this quirk, blocking mode is required and PCM buffer size should
         * be aligned to SYT_INTERVAL.
         */
-       double_pcm_frames = mode > 1;
+       double_pcm_frames = rate > 96000;
        if (double_pcm_frames) {
                rate /= 2;
                pcm_chs *= 2;
@@ -163,53 +191,17 @@ end:
        return err;
 }
 
-static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
-{
-       u32 source;
-       int err;
-
-       err = snd_dice_transaction_get_clock_source(dice, &source);
-       if (err < 0)
-               goto end;
-
-       switch (source) {
-       /* So-called 'SYT Match' modes, sync_to_syt value of packets received */
-       case CLOCK_SOURCE_ARX4: /* in 4th stream */
-       case CLOCK_SOURCE_ARX3: /* in 3rd stream */
-       case CLOCK_SOURCE_ARX2: /* in 2nd stream */
-               err = -ENOSYS;
-               break;
-       case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */
-               *sync_mode = 0;
-               break;
-       default:
-               *sync_mode = CIP_SYNC_TO_DEVICE;
-               break;
-       }
-end:
-       return err;
-}
-
 int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
 {
        struct amdtp_stream *master, *slave;
        unsigned int curr_rate;
-       enum cip_flags sync_mode;
        int err = 0;
 
        if (dice->substreams_counter == 0)
                goto end;
 
-       err = get_sync_mode(dice, &sync_mode);
-       if (err < 0)
-               goto end;
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master = &dice->tx_stream;
-               slave  = &dice->rx_stream;
-       } else {
-               master = &dice->rx_stream;
-               slave  = &dice->tx_stream;
-       }
+       master = &dice->rx_stream;
+       slave  = &dice->tx_stream;
 
        /* Some packet queueing errors. */
        if (amdtp_streaming_error(master) || amdtp_streaming_error(slave))
@@ -224,19 +216,19 @@ int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
        }
        if (rate == 0)
                rate = curr_rate;
-       if (rate != curr_rate)
-               stop_stream(dice, master);
+       if (rate != curr_rate) {
+               err = -EINVAL;
+               goto end;
+       }
 
        if (!amdtp_stream_running(master)) {
                stop_stream(dice, slave);
                snd_dice_transaction_clear_enable(dice);
 
-               amdtp_stream_set_sync(sync_mode, master, slave);
-
-               err = snd_dice_transaction_set_rate(dice, rate);
+               err = ensure_phase_lock(dice);
                if (err < 0) {
                        dev_err(&dice->unit->device,
-                               "fail to set sampling rate\n");
+                               "fail to ensure phase lock\n");
                        goto end;
                }
 
index a4ff4e0..0f03503 100644 (file)
@@ -9,8 +9,6 @@
 
 #include "dice.h"
 
-#define NOTIFICATION_TIMEOUT_MS        (2 * MSEC_PER_SEC)
-
 static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
                       u64 offset)
 {
@@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
                                                info, 4);
 }
 
-static int set_clock_info(struct snd_dice *dice,
-                         unsigned int rate, unsigned int source)
-{
-       unsigned int i;
-       __be32 info;
-       u32 mask;
-       u32 clock;
-       int err;
-
-       err = get_clock_info(dice, &info);
-       if (err < 0)
-               return err;
-
-       clock = be32_to_cpu(info);
-       if (source != UINT_MAX) {
-               mask = CLOCK_SOURCE_MASK;
-               clock &= ~mask;
-               clock |= source;
-       }
-       if (rate != UINT_MAX) {
-               for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
-                       if (snd_dice_rates[i] == rate)
-                               break;
-               }
-               if (i == ARRAY_SIZE(snd_dice_rates))
-                       return -EINVAL;
-
-               mask = CLOCK_RATE_MASK;
-               clock &= ~mask;
-               clock |= i << CLOCK_RATE_SHIFT;
-       }
-       info = cpu_to_be32(clock);
-
-       if (completion_done(&dice->clock_accepted))
-               reinit_completion(&dice->clock_accepted);
-
-       err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
-                                               &info, 4);
-       if (err < 0)
-               return err;
-
-       if (wait_for_completion_timeout(&dice->clock_accepted,
-                       msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
                                          unsigned int *source)
 {
@@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
 end:
        return err;
 }
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
-{
-       return set_clock_info(dice, rate, UINT_MAX);
-}
 
 int snd_dice_transaction_set_enable(struct snd_dice *dice)
 {
@@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 
        fw_send_response(card, request, RCODE_COMPLETE);
 
-       if (bits & NOTIFY_CLOCK_ACCEPTED)
+       if (bits & NOTIFY_LOCK_CHG)
                complete(&dice->clock_accepted);
        wake_up(&dice->hwdep_wait);
 }
index b91b373..f7303a6 100644 (file)
@@ -57,65 +57,10 @@ static int check_dice_category(struct fw_unit *unit)
        return 0;
 }
 
-static int highest_supported_mode_rate(struct snd_dice *dice,
-                                      unsigned int mode, unsigned int *rate)
-{
-       unsigned int i, m;
-
-       for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
-               *rate = snd_dice_rates[i - 1];
-               if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
-                       continue;
-               if (mode == m)
-                       break;
-       }
-       if (i == 0)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
-{
-       __be32 values[2];
-       unsigned int rate;
-       int err;
-
-       if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
-               dice->tx_channels[mode] = 0;
-               dice->tx_midi_ports[mode] = 0;
-               dice->rx_channels[mode] = 0;
-               dice->rx_midi_ports[mode] = 0;
-               return 0;
-       }
-
-       err = snd_dice_transaction_set_rate(dice, rate);
-       if (err < 0)
-               return err;
-
-       err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
-                                          values, sizeof(values));
-       if (err < 0)
-               return err;
-
-       dice->tx_channels[mode]   = be32_to_cpu(values[0]);
-       dice->tx_midi_ports[mode] = be32_to_cpu(values[1]);
-
-       err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
-                                          values, sizeof(values));
-       if (err < 0)
-               return err;
-
-       dice->rx_channels[mode]   = be32_to_cpu(values[0]);
-       dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
-
-       return 0;
-}
-
-static int dice_read_params(struct snd_dice *dice)
+static int check_clock_caps(struct snd_dice *dice)
 {
        __be32 value;
-       int mode, err;
+       int err;
 
        /* some very old firmwares don't tell about their clock support */
        if (dice->clock_caps > 0) {
@@ -133,12 +78,6 @@ static int dice_read_params(struct snd_dice *dice)
                                   CLOCK_CAP_SOURCE_INTERNAL;
        }
 
-       for (mode = 2; mode >= 0; --mode) {
-               err = dice_read_mode_params(dice, mode);
-               if (err < 0)
-                       return err;
-       }
-
        return 0;
 }
 
@@ -215,7 +154,7 @@ static void do_registration(struct work_struct *work)
        if (err < 0)
                goto error;
 
-       err = dice_read_params(dice);
+       err = check_clock_caps(dice);
        if (err < 0)
                goto error;
 
index 3d5ebeb..423cdba 100644 (file)
@@ -56,10 +56,6 @@ struct snd_dice {
        unsigned int rsrv_offset;
 
        unsigned int clock_caps;
-       unsigned int tx_channels[3];
-       unsigned int rx_channels[3];
-       unsigned int tx_midi_ports[3];
-       unsigned int rx_midi_ports[3];
 
        struct fw_address_handler notification_handler;
        int owner_generation;
@@ -158,7 +154,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
 
 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
                                          unsigned int *source);
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
 int snd_dice_transaction_set_enable(struct snd_dice *dice);
 void snd_dice_transaction_clear_enable(struct snd_dice *dice);
@@ -169,9 +164,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
 #define SND_DICE_RATES_COUNT   7
 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
 
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
-                                 unsigned int rate, unsigned int *mode);
-
 int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
 void snd_dice_stream_stop_duplex(struct snd_dice *dice);
 int snd_dice_stream_init_duplex(struct snd_dice *dice);
index d5b19bc..8f27b67 100644 (file)
@@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit)
        struct snd_efw *efw = dev_get_drvdata(&unit->device);
 
        snd_efw_transaction_bus_reset(efw->unit);
+
+       mutex_lock(&efw->mutex);
        snd_efw_stream_update_duplex(efw);
+       mutex_unlock(&efw->mutex);
 }
 
 static void efw_remove(struct fw_unit *unit)
index 968a40a..425db8d 100644 (file)
@@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 
 void snd_efw_stream_update_duplex(struct snd_efw *efw)
 {
-       if ((cmp_connection_update(&efw->out_conn) < 0) ||
-           (cmp_connection_update(&efw->in_conn) < 0)) {
-               mutex_lock(&efw->mutex);
+       if (cmp_connection_update(&efw->out_conn) < 0 ||
+           cmp_connection_update(&efw->in_conn) < 0) {
                stop_stream(efw, &efw->rx_stream);
                stop_stream(efw, &efw->tx_stream);
-               mutex_unlock(&efw->mutex);
        } else {
                amdtp_stream_update(&efw->rx_stream);
                amdtp_stream_update(&efw->tx_stream);
index bb53eb3..f897c98 100644 (file)
@@ -26,11 +26,13 @@ struct fw_scs1x {
        u8 output_bytes;
        bool output_escaped;
        bool output_escape_high_nibble;
-       struct tasklet_struct tasklet;
+       struct work_struct work;
        wait_queue_head_t idle_wait;
        u8 buffer[HSS1394_MAX_PACKET_SIZE];
        bool transaction_running;
        struct fw_transaction transaction;
+       unsigned int transaction_bytes;
+       bool error;
        struct fw_device *fw_dev;
 };
 
@@ -125,11 +127,16 @@ static void scs_write_callback(struct fw_card *card, int rcode,
 {
        struct fw_scs1x *scs = callback_data;
 
-       if (rcode == RCODE_GENERATION)
-               ;       /* TODO: retry this packet */
+       if (!rcode_is_permanent_error(rcode)) {
+               /* Don't retry for this data. */
+               if (rcode == RCODE_COMPLETE)
+                       scs->transaction_bytes = 0;
+       } else {
+               scs->error = true;
+       }
 
        scs->transaction_running = false;
-       tasklet_schedule(&scs->tasklet);
+       schedule_work(&scs->work);
 }
 
 static bool is_valid_running_status(u8 status)
@@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status)
               status == 0xfd;
 }
 
-static void scs_output_tasklet(unsigned long data)
+static void scs_output_work(struct work_struct *work)
 {
-       struct fw_scs1x *scs = (struct fw_scs1x *)data;
+       struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
        struct snd_rawmidi_substream *stream;
        unsigned int i;
        u8 byte;
@@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data)
                return;
 
        stream = ACCESS_ONCE(scs->output);
-       if (!stream) {
+       if (!stream || scs->error) {
                scs->output_idle = true;
                wake_up(&scs->idle_wait);
                return;
        }
 
+       if (scs->transaction_bytes > 0)
+               goto retry;
+
        i = scs->output_bytes;
        for (;;) {
                if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
@@ -253,13 +263,16 @@ static void scs_output_tasklet(unsigned long data)
        scs->output_bytes = 1;
        scs->output_escaped = false;
 
+       scs->transaction_bytes = i;
+retry:
        scs->transaction_running = true;
        generation = scs->fw_dev->generation;
        smp_rmb(); /* node_id vs. generation */
        fw_send_request(scs->fw_dev->card, &scs->transaction,
                        TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
                        generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
-                       scs->buffer, i, scs_write_callback, scs);
+                       scs->buffer, scs->transaction_bytes,
+                       scs_write_callback, scs);
 }
 
 static int midi_capture_open(struct snd_rawmidi_substream *stream)
@@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
                scs->output_bytes = 1;
                scs->output_escaped = false;
                scs->output_idle = false;
+               scs->transaction_bytes = 0;
+               scs->error = false;
 
                ACCESS_ONCE(scs->output) = stream;
-               tasklet_schedule(&scs->tasklet);
+               schedule_work(&scs->work);
        } else {
                ACCESS_ONCE(scs->output) = NULL;
        }
@@ -395,7 +410,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
                            &midi_playback_ops);
 
-       tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
+       INIT_WORK(&scs->work, scs_output_work);
        init_waitqueue_head(&scs->idle_wait);
        scs->output_idle = true;
 
index 7e999c9..3b9bede 100644 (file)
@@ -1,5 +1,5 @@
 snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
-       hdac_regmap.o hdac_controller.o hdac_stream.o array.o
+       hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
 
 snd-hda-core-objs += trace.o
 CFLAGS_trace.o := -I$(src)
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
new file mode 100644 (file)
index 0000000..d7ec862
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * HDMI Channel map support helpers
+ */
+
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/hda_chmap.h>
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+       FL  = (1 <<  0),        /* Front Left           */
+       FC  = (1 <<  1),        /* Front Center         */
+       FR  = (1 <<  2),        /* Front Right          */
+       FLC = (1 <<  3),        /* Front Left Center    */
+       FRC = (1 <<  4),        /* Front Right Center   */
+       RL  = (1 <<  5),        /* Rear Left            */
+       RC  = (1 <<  6),        /* Rear Center          */
+       RR  = (1 <<  7),        /* Rear Right           */
+       RLC = (1 <<  8),        /* Rear Left Center     */
+       RRC = (1 <<  9),        /* Rear Right Center    */
+       LFE = (1 << 10),        /* Low Frequency Effect */
+       FLW = (1 << 11),        /* Front Left Wide      */
+       FRW = (1 << 12),        /* Front Right Wide     */
+       FLH = (1 << 13),        /* Front Left High      */
+       FCH = (1 << 14),        /* Front Center High    */
+       FRH = (1 << 15),        /* Front Right High     */
+       TC  = (1 << 16),        /* Top Center           */
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+       /*  0 */ "FL/FR",
+       /*  1 */ "LFE",
+       /*  2 */ "FC",
+       /*  3 */ "RL/RR",
+       /*  4 */ "RC",
+       /*  5 */ "FLC/FRC",
+       /*  6 */ "RLC/RRC",
+       /*  7 */ "FLW/FRW",
+       /*  8 */ "FLH/FRH",
+       /*  9 */ "TC",
+       /* 10 */ "FCH",
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+       [0] = FL | FR,
+       [1] = LFE,
+       [2] = FC,
+       [3] = RL | RR,
+       [4] = RC,
+       [5] = FLC | FRC,
+       [6] = RLC | RRC,
+       /* the following are not defined in ELD yet */
+       [7] = FLW | FRW,
+       [8] = FLH | FRH,
+       [9] = TC,
+       [10] = FCH,
+};
+
+/*
+ * ALSA sequence is:
+ *
+ *       surround40   surround41   surround50   surround51   surround71
+ * ch0   front left   =            =            =            =
+ * ch1   front right  =            =            =            =
+ * ch2   rear left    =            =            =            =
+ * ch3   rear right   =            =            =            =
+ * ch4                LFE          center       center       center
+ * ch5                                          LFE          LFE
+ * ch6                                                       side left
+ * ch7                                                       side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+       /* stereo */
+       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* 2.1 */
+       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* Dolby Surround */
+       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* surround40 */
+       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+       /* 4ch */
+       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+       /* surround41 */
+       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
+       /* surround50 */
+       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+       /* surround51 */
+       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+       /* 7.1 */
+       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_channel_allocation().
+ */
+static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
+/*                       channel:   7     6    5    4    3     2    1    0  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+                                /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+                                /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+                                /* surround40 */
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+                                /* surround41 */
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+                                /* surround50 */
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* surround51 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* surround71 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
+               hda_nid_t pin_nid, int asp_slot, int channel)
+{
+       return snd_hdac_codec_write(codec, pin_nid, 0,
+                               AC_VERB_SET_HDMI_CHAN_SLOT,
+                               (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot)
+{
+       return (snd_hdac_codec_read(codec, pin_nid, 0,
+                                  AC_VERB_GET_HDMI_CHAN_SLOT,
+                                  asp_slot) & 0xf0) >> 4;
+}
+
+static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
+{
+       return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
+                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hdac_device *codec,
+                                  hda_nid_t cvt_nid, int chs)
+{
+       if (chs != hdmi_get_channel_count(codec, cvt_nid))
+               snd_hdac_codec_write(codec, cvt_nid, 0,
+                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+       int i, j;
+       struct hdac_cea_channel_speaker_allocation *p;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               p = channel_allocations + i;
+               p->channels = 0;
+               p->spk_mask = 0;
+               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+                       if (p->speakers[j]) {
+                               p->channels++;
+                               p->spk_mask |= p->speakers[j];
+                       }
+       }
+}
+
+static int get_channel_allocation_order(int ca)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channel_allocations[i].ca_index == ca)
+                       break;
+       }
+       return i;
+}
+
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+               if (spk_alloc & (1 << i))
+                       j += snprintf(buf + j, buflen - j,  " %s",
+                                       cea_speaker_allocation_names[i]);
+       }
+       buf[j] = '\0';  /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
+
+/*
+ * The transformation takes two steps:
+ *
+ *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ *           spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
+                                  int spk_alloc, int channels)
+{
+       int i;
+       int ca = 0;
+       int spk_mask = 0;
+       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+       /*
+        * CA defaults to 0 for basic stereo audio
+        */
+       if (channels <= 2)
+               return 0;
+
+       /*
+        * expand ELD's speaker allocation mask
+        *
+        * ELD tells the speaker mask in a compact(paired) form,
+        * expand ELD's notions to match the ones used by Audio InfoFrame.
+        */
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       /* search for the first working match in the CA table */
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channels == channel_allocations[i].channels &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask) {
+                       ca = channel_allocations[i].ca_index;
+                       break;
+               }
+       }
+
+       if (!ca) {
+               /*
+                * if there was no match, select the regular ALSA channel
+                * allocation with the matching number of channels
+                */
+               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+                       if (channels == channel_allocations[i].channels) {
+                               ca = channel_allocations[i].ca_index;
+                               break;
+                       }
+               }
+       }
+
+       snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
+       dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+                   ca, channels, buf);
+
+       return ca;
+}
+
+static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int channel;
+
+       for (i = 0; i < 8; i++) {
+               channel = chmap->ops.pin_get_slot_channel(
+                               chmap->hdac, pin_nid, i);
+               dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
+                                               channel, i);
+       }
+#endif
+}
+
+static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid,
+                                      bool non_pcm,
+                                      int ca)
+{
+       struct hdac_cea_channel_speaker_allocation *ch_alloc;
+       int i;
+       int err;
+       int order;
+       int non_pcm_mapping[8];
+
+       order = get_channel_allocation_order(ca);
+       ch_alloc = &channel_allocations[order];
+
+       if (hdmi_channel_mapping[ca][1] == 0) {
+               int hdmi_slot = 0;
+               /* fill actual channel mappings in ALSA channel (i) order */
+               for (i = 0; i < ch_alloc->channels; i++) {
+                       while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
+                               hdmi_slot++; /* skip zero slots */
+
+                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+               }
+               /* fill the rest of the slots with ALSA channel 0xf */
+               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+                       if (!ch_alloc->speakers[7 - hdmi_slot])
+                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+       }
+
+       if (non_pcm) {
+               for (i = 0; i < ch_alloc->channels; i++)
+                       non_pcm_mapping[i] = (i << 4) | i;
+               for (; i < 8; i++)
+                       non_pcm_mapping[i] = (0xf << 4) | i;
+       }
+
+       for (i = 0; i < 8; i++) {
+               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+               int hdmi_slot = slotsetup & 0x0f;
+               int channel = (slotsetup & 0xf0) >> 4;
+
+               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+                               pin_nid, hdmi_slot, channel);
+               if (err) {
+                       dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
+                       break;
+               }
+       }
+}
+
+struct channel_map_table {
+       unsigned char map;              /* ALSA API channel map position */
+       int spk_mask;                   /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+       { SNDRV_CHMAP_FL,       FL },
+       { SNDRV_CHMAP_FR,       FR },
+       { SNDRV_CHMAP_RL,       RL },
+       { SNDRV_CHMAP_RR,       RR },
+       { SNDRV_CHMAP_LFE,      LFE },
+       { SNDRV_CHMAP_FC,       FC },
+       { SNDRV_CHMAP_RLC,      RLC },
+       { SNDRV_CHMAP_RRC,      RRC },
+       { SNDRV_CHMAP_RC,       RC },
+       { SNDRV_CHMAP_FLC,      FLC },
+       { SNDRV_CHMAP_FRC,      FRC },
+       { SNDRV_CHMAP_TFL,      FLH },
+       { SNDRV_CHMAP_TFR,      FRH },
+       { SNDRV_CHMAP_FLW,      FLW },
+       { SNDRV_CHMAP_FRW,      FRW },
+       { SNDRV_CHMAP_TC,       TC },
+       { SNDRV_CHMAP_TFC,      FCH },
+       {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+int snd_hdac_chmap_to_spk_mask(unsigned char c)
+{
+       struct channel_map_table *t = map_tables;
+
+       for (; t->map; t++) {
+               if (t->map == c)
+                       return t->spk_mask;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+       int mask = snd_hdac_chmap_to_spk_mask(pos);
+       int i;
+
+       if (mask) {
+               for (i = 0; i < 8; i++) {
+                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+                               return i;
+               }
+       }
+
+       return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+int snd_hdac_spk_to_chmap(int spk)
+{
+       struct channel_map_table *t = map_tables;
+
+       for (; t->map; t++) {
+               if (t->spk_mask == spk)
+                       return t->map;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+       int mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+       return snd_hdac_spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+       int i, spks = 0, spk_mask = 0;
+
+       for (i = 0; i < chs; i++) {
+               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+
+               if (mask) {
+                       spk_mask |= mask;
+                       spks++;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if ((chs == channel_allocations[i].channels ||
+                    spks == channel_allocations[i].channels) &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask)
+                       return channel_allocations[i].ca_index;
+       }
+       return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
+                                            hda_nid_t pin_nid,
+                                            int chs, unsigned char *map,
+                                            int ca)
+{
+       int ordered_ca = get_channel_allocation_order(ca);
+       int alsa_pos, hdmi_slot;
+       int assignments[8] = {[0 ... 7] = 0xf};
+
+       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+               if (hdmi_slot < 0)
+                       continue; /* unassigned channel */
+
+               assignments[hdmi_slot] = alsa_pos;
+       }
+
+       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+               int err;
+
+               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+                               pin_nid, hdmi_slot, assignments[hdmi_slot]);
+               if (err)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+       int i;
+       int ordered_ca = get_channel_allocation_order(ca);
+
+       for (i = 0; i < 8; i++) {
+               if (i < channel_allocations[ordered_ca].channels)
+                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+               else
+                       map[i] = 0;
+       }
+}
+
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid, bool non_pcm, int ca,
+                                      int channels, unsigned char *map,
+                                      bool chmap_set)
+{
+       if (!non_pcm && chmap_set) {
+               hdmi_manual_setup_channel_mapping(chmap, pin_nid,
+                                                 channels, map, ca);
+       } else {
+               hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
+               hdmi_setup_fake_chmap(map, ca);
+       }
+
+       hdmi_debug_channel_mapping(chmap, pin_nid);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
+
+int snd_hdac_get_active_channels(int ca)
+{
+       int ordered_ca = get_channel_allocation_order(ca);
+
+       return channel_allocations[ordered_ca].channels;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
+
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
+{
+       return &channel_allocations[get_channel_allocation_order(ca)];
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
+
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+               int channels, bool chmap_set, bool non_pcm, unsigned char *map)
+{
+       int ca;
+
+       if (!non_pcm && chmap_set)
+               ca = hdmi_manual_channel_allocation(channels, map);
+       else
+               ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
+                                       spk_alloc, channels);
+
+       if (ca < 0)
+               ca = 0;
+
+       return ca;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
+
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chmap->channels_max;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+       return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+       /* If the speaker allocation matches the channel count, it is OK.*/
+       if (cap->channels != channels)
+               return -1;
+
+       /* all channels are remappable freely */
+       return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels)
+{
+       int count = 0;
+       int c;
+
+       for (c = 7; c >= 0; c--) {
+               int spk = cap->speakers[c];
+
+               if (!spk)
+                       continue;
+
+               chmap[count++] = snd_hdac_spk_to_chmap(spk);
+       }
+
+       WARN_ON(count != channels);
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                             unsigned int size, unsigned int __user *tlv)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+       unsigned int __user *dst;
+       int chs, count = 0;
+
+       if (size < 8)
+               return -ENOMEM;
+       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+               return -EFAULT;
+       size -= 8;
+       dst = tlv + 2;
+       for (chs = 2; chs <= chmap->channels_max; chs++) {
+               int i;
+               struct hdac_cea_channel_speaker_allocation *cap;
+
+               cap = channel_allocations;
+               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+                       int chs_bytes = chs * 4;
+                       int type = chmap->ops.chmap_cea_alloc_validate_get_type(
+                                                               chmap, cap, chs);
+                       unsigned int tlv_chmap[8];
+
+                       if (type < 0)
+                               continue;
+                       if (size < 8)
+                               return -ENOMEM;
+                       if (put_user(type, dst) ||
+                           put_user(chs_bytes, dst + 1))
+                               return -EFAULT;
+                       dst += 2;
+                       size -= 8;
+                       count += 8;
+                       if (size < chs_bytes)
+                               return -ENOMEM;
+                       size -= chs_bytes;
+                       count += chs_bytes;
+                       chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
+                                               tlv_chmap, chs);
+                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
+                               return -EFAULT;
+                       dst += chs;
+               }
+       }
+       if (put_user(count, tlv + 1))
+               return -EFAULT;
+       return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned char pcm_chmap[8];
+       int i;
+
+       memset(pcm_chmap, 0, sizeof(pcm_chmap));
+       chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+       for (i = 0; i < sizeof(chmap); i++)
+               ucontrol->value.integer.value[i] = pcm_chmap[i];
+
+       return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *hchmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned int ctl_idx;
+       struct snd_pcm_substream *substream;
+       unsigned char chmap[8], per_pin_chmap[8];
+       int i, err, ca, prepared = 0;
+
+       /* No monitor is connected in dyn_pcm_assign.
+        * It's invalid to setup the chmap
+        */
+       if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
+               return 0;
+
+       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       substream = snd_pcm_chmap_substream(info, ctl_idx);
+       if (!substream || !substream->runtime)
+               return 0; /* just for avoiding error from alsactl restore */
+       switch (substream->runtime->status->state) {
+       case SNDRV_PCM_STATE_OPEN:
+       case SNDRV_PCM_STATE_SETUP:
+               break;
+       case SNDRV_PCM_STATE_PREPARED:
+               prepared = 1;
+               break;
+       default:
+               return -EBUSY;
+       }
+       memset(chmap, 0, sizeof(chmap));
+       for (i = 0; i < ARRAY_SIZE(chmap); i++)
+               chmap[i] = ucontrol->value.integer.value[i];
+
+       hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+       if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
+               return 0;
+       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+       if (ca < 0)
+               return -EINVAL;
+       if (hchmap->ops.chmap_validate) {
+               err = hchmap->ops.chmap_validate(hchmap, ca,
+                               ARRAY_SIZE(chmap), chmap);
+               if (err)
+                       return err;
+       }
+
+       hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
+
+       return 0;
+}
+
+static const struct hdac_chmap_ops chmap_ops = {
+       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
+       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
+       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
+       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
+       .set_channel_count                      = hdmi_set_channel_count,
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+                               struct hdac_chmap *chmap)
+{
+       chmap->ops = chmap_ops;
+       chmap->hdac = hdac;
+       init_channel_allocations();
+}
+EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
+
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+                               struct hdac_chmap *hchmap)
+{
+       struct snd_pcm_chmap *chmap;
+       struct snd_kcontrol *kctl;
+       int err, i;
+
+       err = snd_pcm_add_chmap_ctls(pcm,
+                                    SNDRV_PCM_STREAM_PLAYBACK,
+                                    NULL, 0, pcm_idx, &chmap);
+       if (err < 0)
+               return err;
+       /* override handlers */
+       chmap->private_data = hchmap;
+       kctl = chmap->kctl;
+       for (i = 0; i < kctl->count; i++)
+               kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+       kctl->info = hdmi_chmap_ctl_info;
+       kctl->get = hdmi_chmap_ctl_get;
+       kctl->put = hdmi_chmap_ctl_put;
+       kctl->tlv.c = hdmi_chmap_ctl_tlv;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
index 2153d31..4a47050 100644 (file)
@@ -23,17 +23,5 @@ config SND_SGI_HAL2
         help
                 Sound support for the SGI Indy and Indigo2 Workstation.
 
-
-config SND_AU1X00
-       tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
-       depends on MIPS_ALCHEMY
-       select SND_PCM
-       select SND_AC97_CODEC
-       help
-         ALSA Sound driver for the Au1x00's AC97 port.
-
-         Newer drivers for ASoC are available, please do not use
-         this driver as it will be removed in the future.
-
 endif  # SND_MIPS
 
index 861ec0a..b977c44 100644 (file)
@@ -2,11 +2,9 @@
 # Makefile for ALSA
 #
 
-snd-au1x00-objs := au1x00.o
 snd-sgi-o2-objs := sgio2audio.o ad1843.o
 snd-sgi-hal2-objs := hal2.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
 obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
 obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
deleted file mode 100644 (file)
index 1e30e84..0000000
+++ /dev/null
@@ -1,734 +0,0 @@
-/*
- * BRIEF MODULE DESCRIPTION
- *  Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
- *
- * Copyright 2004 Cooper Street Innovations Inc.
- * Author: Charles Eidsness    <charles@cooper-street.com>
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  You should have received a copy of the  GNU General Public License along
- *  with this program; if not, write  to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * History:
- *
- * 2004-09-09 Charles Eidsness -- Original verion -- based on
- *                               sa11xx-uda1341.c ALSA driver and the
- *                               au1000.c OSS driver.
- * 2004-09-09 Matt Porter      -- Added support for ALSA 1.0.6
- *
- */
-
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/ac97_codec.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1000_dma.h>
-
-MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
-MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
-
-#define PLAYBACK 0
-#define CAPTURE 1
-#define AC97_SLOT_3 0x01
-#define AC97_SLOT_4 0x02
-#define AC97_SLOT_6 0x08
-#define AC97_CMD_IRQ 31
-#define READ 0
-#define WRITE 1
-#define READ_WAIT 2
-#define RW_DONE 3
-
-struct au1000_period
-{
-       u32 start;
-       u32 relative_end;       /*realtive to start of buffer*/
-       struct au1000_period * next;
-};
-
-/*Au1000 AC97 Port Control Reisters*/
-struct au1000_ac97_reg {
-       u32 volatile config;
-       u32 volatile status;
-       u32 volatile data;
-       u32 volatile cmd;
-       u32 volatile cntrl;
-};
-
-struct audio_stream {
-       struct snd_pcm_substream *substream;
-       int dma;
-       spinlock_t dma_lock;
-       struct au1000_period * buffer;
-       unsigned int period_size;
-       unsigned int periods;
-};
-
-struct snd_au1000 {
-       struct snd_card *card;
-       struct au1000_ac97_reg volatile *ac97_ioport;
-
-       struct resource *ac97_res_port;
-       spinlock_t ac97_lock;
-       struct snd_ac97 *ac97;
-
-       struct snd_pcm *pcm;
-       struct audio_stream *stream[2]; /* playback & capture */
-       int dmaid[2];           /* tx(0)/rx(1) DMA ids */
-};
-
-/*--------------------------- Local Functions --------------------------------*/
-static void
-au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
-{
-       u32 volatile ac97_config;
-
-       spin_lock(&au1000->ac97_lock);
-       ac97_config = au1000->ac97_ioport->config;
-       ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
-       ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
-       au1000->ac97_ioport->config = ac97_config;
-       spin_unlock(&au1000->ac97_lock);
-}
-
-static void
-au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
-{
-       u32 volatile ac97_config;
-
-       spin_lock(&au1000->ac97_lock);
-       ac97_config = au1000->ac97_ioport->config;
-       ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
-       ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
-       au1000->ac97_ioport->config = ac97_config;
-       spin_unlock(&au1000->ac97_lock);
-}
-
-
-static void
-au1000_release_dma_link(struct audio_stream *stream)
-{
-       struct au1000_period * pointer;
-       struct au1000_period * pointer_next;
-
-       stream->period_size = 0;
-       stream->periods = 0;
-       pointer = stream->buffer;
-       if (! pointer)
-               return;
-       do {
-               pointer_next = pointer->next;
-               kfree(pointer);
-               pointer = pointer_next;
-       } while (pointer != stream->buffer);
-       stream->buffer = NULL;
-}
-
-static int
-au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
-                     unsigned int periods)
-{
-       struct snd_pcm_substream *substream = stream->substream;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct au1000_period *pointer;
-       unsigned long dma_start;
-       int i;
-
-       dma_start = virt_to_phys(runtime->dma_area);
-
-       if (stream->period_size == period_bytes &&
-           stream->periods == periods)
-               return 0; /* not changed */
-
-       au1000_release_dma_link(stream);
-
-       stream->period_size = period_bytes;
-       stream->periods = periods;
-
-       stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
-       if (! stream->buffer)
-               return -ENOMEM;
-       pointer = stream->buffer;
-       for (i = 0; i < periods; i++) {
-               pointer->start = (u32)(dma_start + (i * period_bytes));
-               pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
-               if (i < periods - 1) {
-                       pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
-                       if (! pointer->next) {
-                               au1000_release_dma_link(stream);
-                               return -ENOMEM;
-                       }
-                       pointer = pointer->next;
-               }
-       }
-       pointer->next = stream->buffer;
-       return 0;
-}
-
-static void
-au1000_dma_stop(struct audio_stream *stream)
-{
-       if (snd_BUG_ON(!stream->buffer))
-               return;
-       disable_dma(stream->dma);
-}
-
-static void
-au1000_dma_start(struct audio_stream *stream)
-{
-       if (snd_BUG_ON(!stream->buffer))
-               return;
-
-       init_dma(stream->dma);
-       if (get_dma_active_buffer(stream->dma) == 0) {
-               clear_dma_done0(stream->dma);
-               set_dma_addr0(stream->dma, stream->buffer->start);
-               set_dma_count0(stream->dma, stream->period_size >> 1);
-               set_dma_addr1(stream->dma, stream->buffer->next->start);
-               set_dma_count1(stream->dma, stream->period_size >> 1);
-       } else {
-               clear_dma_done1(stream->dma);
-               set_dma_addr1(stream->dma, stream->buffer->start);
-               set_dma_count1(stream->dma, stream->period_size >> 1);
-               set_dma_addr0(stream->dma, stream->buffer->next->start);
-               set_dma_count0(stream->dma, stream->period_size >> 1);
-       }
-       enable_dma_buffers(stream->dma);
-       start_dma(stream->dma);
-}
-
-static irqreturn_t
-au1000_dma_interrupt(int irq, void *dev_id)
-{
-       struct audio_stream *stream = (struct audio_stream *) dev_id;
-       struct snd_pcm_substream *substream = stream->substream;
-
-       spin_lock(&stream->dma_lock);
-       switch (get_dma_buffer_done(stream->dma)) {
-       case DMA_D0:
-               stream->buffer = stream->buffer->next;
-               clear_dma_done0(stream->dma);
-               set_dma_addr0(stream->dma, stream->buffer->next->start);
-               set_dma_count0(stream->dma, stream->period_size >> 1);
-               enable_dma_buffer0(stream->dma);
-               break;
-       case DMA_D1:
-               stream->buffer = stream->buffer->next;
-               clear_dma_done1(stream->dma);
-               set_dma_addr1(stream->dma, stream->buffer->next->start);
-               set_dma_count1(stream->dma, stream->period_size >> 1);
-               enable_dma_buffer1(stream->dma);
-               break;
-       case (DMA_D0 | DMA_D1):
-               printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
-               au1000_dma_stop(stream);
-               au1000_dma_start(stream);
-               break;
-       case (~DMA_D0 & ~DMA_D1):
-               printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
-       }
-       spin_unlock(&stream->dma_lock);
-       snd_pcm_period_elapsed(substream);
-       return IRQ_HANDLED;
-}
-
-/*-------------------------- PCM Audio Streams -------------------------------*/
-
-static unsigned int rates[] = {8000, 11025, 16000, 22050};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
-       .count  = ARRAY_SIZE(rates),
-       .list   = rates,
-       .mask   = 0,
-};
-
-static struct snd_pcm_hardware snd_au1000_hw =
-{
-       .info                   = (SNDRV_PCM_INFO_INTERLEAVED | \
-                               SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
-       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
-       .rates                  = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
-                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
-       .rate_min               = 8000,
-       .rate_max               = 22050,
-       .channels_min           = 1,
-       .channels_max           = 2,
-       .buffer_bytes_max       = 128*1024,
-       .period_bytes_min       = 32,
-       .period_bytes_max       = 16*1024,
-       .periods_min            = 8,
-       .periods_max            = 255,
-       .fifo_size              = 16,
-};
-
-static int
-snd_au1000_playback_open(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[PLAYBACK]->substream = substream;
-       au1000->stream[PLAYBACK]->buffer = NULL;
-       substream->private_data = au1000->stream[PLAYBACK];
-       substream->runtime->hw = snd_au1000_hw;
-       return (snd_pcm_hw_constraint_list(substream->runtime, 0,
-               SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_capture_open(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[CAPTURE]->substream = substream;
-       au1000->stream[CAPTURE]->buffer = NULL;
-       substream->private_data = au1000->stream[CAPTURE];
-       substream->runtime->hw = snd_au1000_hw;
-       return (snd_pcm_hw_constraint_list(substream->runtime, 0,
-               SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_playback_close(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[PLAYBACK]->substream = NULL;
-       return 0;
-}
-
-static int
-snd_au1000_capture_close(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[CAPTURE]->substream = NULL;
-       return 0;
-}
-
-static int
-snd_au1000_hw_params(struct snd_pcm_substream *substream,
-                                       struct snd_pcm_hw_params *hw_params)
-{
-       struct audio_stream *stream = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_malloc_pages(substream,
-                                      params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
-       return au1000_setup_dma_link(stream,
-                                    params_period_bytes(hw_params),
-                                    params_periods(hw_params));
-}
-
-static int
-snd_au1000_hw_free(struct snd_pcm_substream *substream)
-{
-       struct audio_stream *stream = substream->private_data;
-       au1000_release_dma_link(stream);
-       return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       if (runtime->channels == 1)
-               au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
-       else
-               au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
-       snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-       return 0;
-}
-
-static int
-snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       if (runtime->channels == 1)
-               au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
-       else
-               au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
-       snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-       return 0;
-}
-
-static int
-snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct audio_stream *stream = substream->private_data;
-       int err = 0;
-
-       spin_lock(&stream->dma_lock);
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               au1000_dma_start(stream);
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-               au1000_dma_stop(stream);
-               break;
-       default:
-               err = -EINVAL;
-               break;
-       }
-       spin_unlock(&stream->dma_lock);
-       return err;
-}
-
-static snd_pcm_uframes_t
-snd_au1000_pointer(struct snd_pcm_substream *substream)
-{
-       struct audio_stream *stream = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       long location;
-
-       spin_lock(&stream->dma_lock);
-       location = get_dma_residue(stream->dma);
-       spin_unlock(&stream->dma_lock);
-       location = stream->buffer->relative_end - location;
-       if (location == -1)
-               location = 0;
-       return bytes_to_frames(runtime,location);
-}
-
-static struct snd_pcm_ops snd_card_au1000_playback_ops = {
-       .open                   = snd_au1000_playback_open,
-       .close                  = snd_au1000_playback_close,
-       .ioctl                  = snd_pcm_lib_ioctl,
-       .hw_params              = snd_au1000_hw_params,
-       .hw_free                = snd_au1000_hw_free,
-       .prepare                = snd_au1000_playback_prepare,
-       .trigger                = snd_au1000_trigger,
-       .pointer                = snd_au1000_pointer,
-};
-
-static struct snd_pcm_ops snd_card_au1000_capture_ops = {
-       .open                   = snd_au1000_capture_open,
-       .close                  = snd_au1000_capture_close,
-       .ioctl                  = snd_pcm_lib_ioctl,
-       .hw_params              = snd_au1000_hw_params,
-       .hw_free                = snd_au1000_hw_free,
-       .prepare                = snd_au1000_capture_prepare,
-       .trigger                = snd_au1000_trigger,
-       .pointer                = snd_au1000_pointer,
-};
-
-static int
-snd_au1000_pcm_new(struct snd_au1000 *au1000)
-{
-       struct snd_pcm *pcm;
-       int err;
-       unsigned long flags;
-
-       if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
-               return err;
-
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-               snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
-
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-               &snd_card_au1000_playback_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
-               &snd_card_au1000_capture_ops);
-
-       pcm->private_data = au1000;
-       pcm->info_flags = 0;
-       strcpy(pcm->name, "Au1000 AC97 PCM");
-
-       spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
-       spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
-
-       flags = claim_dma_lock();
-       au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0],
-                       "AC97 TX", au1000_dma_interrupt, 0,
-                       au1000->stream[PLAYBACK]);
-       if (au1000->stream[PLAYBACK]->dma < 0) {
-               release_dma_lock(flags);
-               return -EBUSY;
-       }
-       au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1],
-                       "AC97 RX", au1000_dma_interrupt, 0,
-                       au1000->stream[CAPTURE]);
-       if (au1000->stream[CAPTURE]->dma < 0){
-               release_dma_lock(flags);
-               return -EBUSY;
-       }
-       /* enable DMA coherency in read/write DMA channels */
-       set_dma_mode(au1000->stream[PLAYBACK]->dma,
-                    get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
-       set_dma_mode(au1000->stream[CAPTURE]->dma,
-                    get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
-       release_dma_lock(flags);
-       au1000->pcm = pcm;
-       return 0;
-}
-
-
-/*-------------------------- AC97 CODEC Control ------------------------------*/
-
-static unsigned short
-snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
-       struct snd_au1000 *au1000 = ac97->private_data;
-       u32 volatile cmd;
-       u16 volatile data;
-       int             i;
-
-       spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
-       for (i = 0; i < 0x5000; i++)
-               if (!(au1000->ac97_ioport->status & AC97C_CP))
-                       break;
-       if (i == 0x5000)
-               printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-
-       cmd = (u32) reg & AC97C_INDEX_MASK;
-       cmd |= AC97C_READ;
-       au1000->ac97_ioport->cmd = cmd;
-
-       /* now wait for the data */
-       for (i = 0; i < 0x5000; i++)
-               if (!(au1000->ac97_ioport->status & AC97C_CP))
-                       break;
-       if (i == 0x5000) {
-               printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-               spin_unlock(&au1000->ac97_lock);
-               return 0;
-       }
-
-       data = au1000->ac97_ioport->cmd & 0xffff;
-       spin_unlock(&au1000->ac97_lock);
-
-       return data;
-
-}
-
-
-static void
-snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
-       struct snd_au1000 *au1000 = ac97->private_data;
-       u32 cmd;
-       int i;
-
-       spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
-       for (i = 0; i < 0x5000; i++)
-               if (!(au1000->ac97_ioport->status & AC97C_CP))
-                       break;
-       if (i == 0x5000)
-               printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
-
-       cmd = (u32) reg & AC97C_INDEX_MASK;
-       cmd &= ~AC97C_READ;
-       cmd |= ((u32) val << AC97C_WD_BIT);
-       au1000->ac97_ioport->cmd = cmd;
-       spin_unlock(&au1000->ac97_lock);
-}
-
-/*------------------------------ Setup / Destroy ----------------------------*/
-
-static void snd_au1000_free(struct snd_card *card)
-{
-       struct snd_au1000 *au1000 = card->private_data;
-
-       if (au1000->stream[PLAYBACK]) {
-               if (au1000->stream[PLAYBACK]->dma >= 0)
-                       free_au1000_dma(au1000->stream[PLAYBACK]->dma);
-               kfree(au1000->stream[PLAYBACK]);
-       }
-
-       if (au1000->stream[CAPTURE]) {
-               if (au1000->stream[CAPTURE]->dma >= 0)
-                       free_au1000_dma(au1000->stream[CAPTURE]->dma);
-               kfree(au1000->stream[CAPTURE]);
-       }
-
-       if (au1000->ac97_res_port) {
-               /* put internal AC97 block into reset */
-               if (au1000->ac97_ioport) {
-                       au1000->ac97_ioport->cntrl = AC97C_RS;
-                       iounmap(au1000->ac97_ioport);
-                       au1000->ac97_ioport = NULL;
-               }
-               release_and_free_resource(au1000->ac97_res_port);
-               au1000->ac97_res_port = NULL;
-       }
-}
-
-static struct snd_ac97_bus_ops ops = {
-       .write  = snd_au1000_ac97_write,
-       .read   = snd_au1000_ac97_read,
-};
-
-static int au1000_ac97_probe(struct platform_device *pdev)
-{
-       int err;
-       void __iomem *io;
-       struct resource *r;
-       struct snd_card *card;
-       struct snd_au1000 *au1000;
-       struct snd_ac97_bus *pbus;
-       struct snd_ac97_template ac97;
-
-       err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE,
-                          sizeof(struct snd_au1000), &card);
-       if (err < 0)
-               return err;
-
-       au1000 = card->private_data;
-       au1000->card = card;
-       spin_lock_init(&au1000->ac97_lock);
-
-       /* from here on let ALSA call the special freeing function */
-       card->private_free = snd_au1000_free;
-
-       /* TX DMA ID */
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!r) {
-               err = -ENODEV;
-               snd_printk(KERN_INFO "no TX DMA platform resource!\n");
-               goto out;
-       }
-       au1000->dmaid[0] = r->start;
-
-       /* RX DMA ID */
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!r) {
-               err = -ENODEV;
-               snd_printk(KERN_INFO "no RX DMA platform resource!\n");
-               goto out;
-       }
-       au1000->dmaid[1] = r->start;
-
-       au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
-                                          GFP_KERNEL);
-       if (!au1000->stream[PLAYBACK]) {
-               err = -ENOMEM;
-               goto out;
-       }
-       au1000->stream[PLAYBACK]->dma = -1;
-
-       au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
-                                         GFP_KERNEL);
-       if (!au1000->stream[CAPTURE]) {
-               err = -ENOMEM;
-               goto out;
-       }
-       au1000->stream[CAPTURE]->dma = -1;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!r) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       err = -EBUSY;
-       au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
-                                                  pdev->name);
-       if (!au1000->ac97_res_port) {
-               snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
-               goto out;
-       }
-
-       io = ioremap(r->start, resource_size(r));
-       if (!io)
-               goto out;
-
-       au1000->ac97_ioport = (struct au1000_ac97_reg *)io;
-
-       /* configure pins for AC'97
-       TODO: move to board_setup.c */
-       au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
-
-       /* Initialise Au1000's AC'97 Control Block */
-       au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
-       udelay(10);
-       au1000->ac97_ioport->cntrl = AC97C_CE;
-       udelay(10);
-
-       /* Initialise External CODEC -- cold reset */
-       au1000->ac97_ioport->config = AC97C_RESET;
-       udelay(10);
-       au1000->ac97_ioport->config = 0x0;
-       mdelay(5);
-
-       /* Initialise AC97 middle-layer */
-       err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus);
-       if (err < 0)
-               goto out;
-
-       memset(&ac97, 0, sizeof(ac97));
-       ac97.private_data = au1000;
-       err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97);
-       if (err < 0)
-               goto out;
-
-       err = snd_au1000_pcm_new(au1000);
-       if (err < 0)
-               goto out;
-
-       strcpy(card->driver, "Au1000-AC97");
-       strcpy(card->shortname, "AMD Au1000-AC97");
-       sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
-
-       err = snd_card_register(card);
-       if (err < 0)
-               goto out;
-
-       printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
-
-       platform_set_drvdata(pdev, card);
-
-       return 0;
-
- out:
-       snd_card_free(card);
-       return err;
-}
-
-static int au1000_ac97_remove(struct platform_device *pdev)
-{
-       return snd_card_free(platform_get_drvdata(pdev));
-}
-
-struct platform_driver au1000_ac97c_driver = {
-       .driver = {
-               .name   = "au1000-ac97c",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = au1000_ac97_probe,
-       .remove         = au1000_ac97_remove,
-};
-
-module_platform_driver(au1000_ac97c_driver);
index 8f6594a..32151d8 100644 (file)
@@ -866,7 +866,7 @@ config SND_VIRTUOSO
        select SND_OXYGEN_LIB
        select SND_PCM
        select SND_MPU401_UART
-       select SND_JACK if INPUT=y || INPUT=SND
+       select SND_JACK
        help
          Say Y here to include support for sound cards based on the
          Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX,
index e94cfd5..bb02c2d 100644 (file)
@@ -4,7 +4,7 @@ config SND_HDA
        tristate
        select SND_PCM
        select SND_VMASTER
-       select SND_JACK if INPUT=y || INPUT=SND
+       select SND_JACK
        select SND_HDA_CORE
 
 config SND_HDA_INTEL
index bc2e082..ba7fe9b 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <asm/unaligned.h>
+#include <sound/hda_chmap.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 
@@ -42,20 +43,6 @@ enum cea_edid_versions {
        CEA_EDID_VER_RESERVED   = 4,
 };
 
-static const char * const cea_speaker_allocation_names[] = {
-       /*  0 */ "FL/FR",
-       /*  1 */ "LFE",
-       /*  2 */ "FC",
-       /*  3 */ "RL/RR",
-       /*  4 */ "RC",
-       /*  5 */ "FLC/FRC",
-       /*  6 */ "RLC/RRC",
-       /*  7 */ "FLW/FRW",
-       /*  8 */ "FLH/FRH",
-       /*  9 */ "TC",
-       /* 10 */ "FCH",
-};
-
 static const char * const eld_connection_type_names[4] = {
        "HDMI",
        "DisplayPort",
@@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec,
                  a->channels, buf, buf2);
 }
 
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
-{
-       int i, j;
-
-       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
-               if (spk_alloc & (1 << i))
-                       j += snprintf(buf + j, buflen - j,  " %s",
-                                       cea_speaker_allocation_names[i]);
-       }
-       buf[j] = '\0';  /* necessary when j == 0 */
-}
-
 void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
 {
        int i;
@@ -441,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
 
        if (e->spk_alloc) {
                char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-               snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+               snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
                codec_dbg(codec, "HDMI: available speakers:%s\n", buf);
        }
 
@@ -516,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
        snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
        snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
 
-       snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+       snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
        snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
 
        snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
index e5240cb..2624cfe 100644 (file)
@@ -2145,7 +2145,7 @@ static int azx_probe_continue(struct azx *chip)
        azx_add_card_list(chip);
        snd_hda_set_power_save(&chip->bus, power_save * 1000);
        if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
-               pm_runtime_put_noidle(&pci->dev);
+               pm_runtime_put_autosuspend(&pci->dev);
 
 out_free:
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
index bcbc4ee..eb7da99 100644 (file)
@@ -39,6 +39,7 @@
 #include <sound/tlv.h>
 #include <sound/hdaudio.h>
 #include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_jack.h"
@@ -75,6 +76,8 @@ struct hdmi_spec_per_cvt {
 
 struct hdmi_spec_per_pin {
        hda_nid_t pin_nid;
+       /* pin idx, different device entries on the same pin use the same idx */
+       int pin_nid_idx;
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
        int mux_idx;
@@ -84,8 +87,8 @@ struct hdmi_spec_per_pin {
        struct hdmi_eld sink_eld;
        struct mutex lock;
        struct delayed_work work;
-       struct snd_kcontrol *eld_ctl;
-       struct snd_jack *acomp_jack; /* jack via audio component */
+       struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
+       int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
        int repoll_count;
        bool setup; /* the stream has been set up by prepare callback */
        int channels; /* current number of channels */
@@ -97,19 +100,11 @@ struct hdmi_spec_per_pin {
 #endif
 };
 
-struct cea_channel_speaker_allocation;
-
 /* operations used by generic code that can be overridden by patches */
 struct hdmi_ops {
        int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
                           unsigned char *buf, int *eld_size);
 
-       /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
-       int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   int asp_slot);
-       int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   int asp_slot, int channel);
-
        void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
                                    int ca, int active_channels, int conn_type);
 
@@ -119,15 +114,12 @@ struct hdmi_ops {
        int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
                            hda_nid_t pin_nid, u32 stream_tag, int format);
 
-       /* Helpers for producing the channel map TLVs. These can be overridden
-        * for devices that have non-standard mapping requirements. */
-       int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap,
-                                                int channels);
-       void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap,
-                                      unsigned int *chmap, int channels);
+};
 
-       /* check that the user-given chmap is supported */
-       int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
+struct hdmi_pcm {
+       struct hda_pcm *pcm;
+       struct snd_jack *jack;
+       struct snd_kcontrol *eld_ctl;
 };
 
 struct hdmi_spec {
@@ -137,14 +129,22 @@ struct hdmi_spec {
 
        int num_pins;
        struct snd_array pins; /* struct hdmi_spec_per_pin */
-       struct hda_pcm *pcm_rec[16];
-       unsigned int channels_max; /* max over all cvts */
+       struct hdmi_pcm pcm_rec[16];
+       struct mutex pcm_lock;
+       /* pcm_bitmap means which pcms have been assigned to pins*/
+       unsigned long pcm_bitmap;
+       int pcm_used;   /* counter of pcm_rec[] */
+       /* bitmap shows whether the pcm is opened in user space
+        * bit 0 means the first playback PCM (PCM3);
+        * bit 1 means the second playback PCM, and so on.
+        */
+       unsigned long pcm_in_use;
 
        struct hdmi_eld temp_eld;
        struct hdmi_ops ops;
 
        bool dyn_pin_out;
-
+       bool dyn_pcm_assign;
        /*
         * Non-generic VIA/NVIDIA specific
         */
@@ -154,6 +154,8 @@ struct hdmi_spec {
        /* i915/powerwell (Haswell+/Valleyview+) specific */
        struct i915_audio_component_audio_ops i915_audio_ops;
        bool i915_bound; /* was i915 bound in this driver? */
+
+       struct hdac_chmap chmap;
 };
 
 #ifdef CONFIG_SND_HDA_I915
@@ -195,173 +197,6 @@ union audio_infoframe {
        u8 bytes[0];
 };
 
-/*
- * CEA speaker placement:
- *
- *        FLH       FCH        FRH
- *  FLW    FL  FLC   FC   FRC   FR   FRW
- *
- *                                  LFE
- *                     TC
- *
- *          RL  RLC   RC   RRC   RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
-       FL  = (1 <<  0),        /* Front Left           */
-       FC  = (1 <<  1),        /* Front Center         */
-       FR  = (1 <<  2),        /* Front Right          */
-       FLC = (1 <<  3),        /* Front Left Center    */
-       FRC = (1 <<  4),        /* Front Right Center   */
-       RL  = (1 <<  5),        /* Rear Left            */
-       RC  = (1 <<  6),        /* Rear Center          */
-       RR  = (1 <<  7),        /* Rear Right           */
-       RLC = (1 <<  8),        /* Rear Left Center     */
-       RRC = (1 <<  9),        /* Rear Right Center    */
-       LFE = (1 << 10),        /* Low Frequency Effect */
-       FLW = (1 << 11),        /* Front Left Wide      */
-       FRW = (1 << 12),        /* Front Right Wide     */
-       FLH = (1 << 13),        /* Front Left High      */
-       FCH = (1 << 14),        /* Front Center High    */
-       FRH = (1 << 15),        /* Front Right High     */
-       TC  = (1 << 16),        /* Top Center           */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
-       [0] = FL | FR,
-       [1] = LFE,
-       [2] = FC,
-       [3] = RL | RR,
-       [4] = RC,
-       [5] = FLC | FRC,
-       [6] = RLC | RRC,
-       /* the following are not defined in ELD yet */
-       [7] = FLW | FRW,
-       [8] = FLH | FRH,
-       [9] = TC,
-       [10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
-       int ca_index;
-       int speakers[8];
-
-       /* derived values, just for convenience */
-       int channels;
-       int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- *       surround40   surround41   surround50   surround51   surround71
- * ch0   front left   =            =            =            =
- * ch1   front right  =            =            =            =
- * ch2   rear left    =            =            =            =
- * ch3   rear right   =            =            =            =
- * ch4                LFE          center       center       center
- * ch5                                          LFE          LFE
- * ch6                                                       side left
- * ch7                                                       side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
-       /* stereo */
-       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* 2.1 */
-       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* Dolby Surround */
-       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* surround40 */
-       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
-       /* 4ch */
-       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
-       /* surround41 */
-       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
-       /* surround50 */
-       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
-       /* surround51 */
-       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
-       /* 7.1 */
-       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/*                       channel:   7     6    5    4    3     2    1    0  */
-{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
-                                /* 2.1 */
-{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
-                                /* Dolby Surround */
-{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
-                                /* surround40 */
-{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
-                                /* surround41 */
-{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
-                                /* surround50 */
-{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-                                /* surround51 */
-{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* 6.1 */
-{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* surround71 */
-{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-
-{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
-{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
-};
-
-
 /*
  * HDMI routines
  */
@@ -370,7 +205,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
        ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
 #define get_cvt(spec, idx) \
        ((struct hdmi_spec_per_cvt  *)snd_array_elem(&spec->cvts, idx))
-#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
+/* obtain hdmi_pcm object assigned to idx */
+#define get_hdmi_pcm(spec, idx)        (&(spec)->pcm_rec[idx])
+/* obtain hda_pcm object assigned to idx */
+#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
 
 static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
 {
@@ -385,20 +223,52 @@ static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
        return -EINVAL;
 }
 
+static int hinfo_to_pcm_index(struct hda_codec *codec,
+                       struct hda_pcm_stream *hinfo)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int pcm_idx;
+
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++)
+               if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
+                       return pcm_idx;
+
+       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       return -EINVAL;
+}
+
 static int hinfo_to_pin_index(struct hda_codec *codec,
                              struct hda_pcm_stream *hinfo)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin;
        int pin_idx;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
-               if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               per_pin = get_pin(spec, pin_idx);
+               if (per_pin->pcm &&
+                       per_pin->pcm->pcm->stream == hinfo)
                        return pin_idx;
+       }
 
-       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
        return -EINVAL;
 }
 
+static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec,
+                                               int pcm_idx)
+{
+       int i;
+       struct hdmi_spec_per_pin *per_pin;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               per_pin = get_pin(spec, i);
+               if (per_pin->pcm_idx == pcm_idx)
+                       return per_pin;
+       }
+       return NULL;
+}
+
 static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -419,17 +289,22 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
-       int pin_idx;
+       int pcm_idx;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 
-       pin_idx = kcontrol->private_value;
-       per_pin = get_pin(spec, pin_idx);
+       pcm_idx = kcontrol->private_value;
+       mutex_lock(&spec->pcm_lock);
+       per_pin = pcm_idx_to_pin(spec, pcm_idx);
+       if (!per_pin) {
+               /* no pin is bound to the pcm */
+               uinfo->count = 0;
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
        eld = &per_pin->sink_eld;
-
-       mutex_lock(&per_pin->lock);
        uinfo->count = eld->eld_valid ? eld->eld_size : 0;
-       mutex_unlock(&per_pin->lock);
+       mutex_unlock(&spec->pcm_lock);
 
        return 0;
 }
@@ -441,16 +316,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
-       int pin_idx;
-
-       pin_idx = kcontrol->private_value;
-       per_pin = get_pin(spec, pin_idx);
+       int pcm_idx;
+
+       pcm_idx = kcontrol->private_value;
+       mutex_lock(&spec->pcm_lock);
+       per_pin = pcm_idx_to_pin(spec, pcm_idx);
+       if (!per_pin) {
+               /* no pin is bound to the pcm */
+               memset(ucontrol->value.bytes.data, 0,
+                      ARRAY_SIZE(ucontrol->value.bytes.data));
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
        eld = &per_pin->sink_eld;
 
-       mutex_lock(&per_pin->lock);
        if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
            eld->eld_size > ELD_MAX_SIZE) {
-               mutex_unlock(&per_pin->lock);
+               mutex_unlock(&spec->pcm_lock);
                snd_BUG();
                return -EINVAL;
        }
@@ -460,7 +342,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        if (eld->eld_valid)
                memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
                       eld->eld_size);
-       mutex_unlock(&per_pin->lock);
+       mutex_unlock(&spec->pcm_lock);
 
        return 0;
 }
@@ -473,7 +355,7 @@ static struct snd_kcontrol_new eld_bytes_ctl = {
        .get = hdmi_eld_ctl_get,
 };
 
-static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
                        int device)
 {
        struct snd_kcontrol *kctl;
@@ -483,14 +365,17 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
        kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
        if (!kctl)
                return -ENOMEM;
-       kctl->private_value = pin_idx;
+       kctl->private_value = pcm_idx;
        kctl->id.device = device;
 
-       err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
+       /* no pin nid is associated with the kctl now
+        * tbd: associate pin nid to eld ctl later
+        */
+       err = snd_hda_ctl_add(codec, 0, kctl);
        if (err < 0)
                return err;
 
-       get_pin(spec, pin_idx)->eld_ctl = kctl;
+       get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl;
        return 0;
 }
 
@@ -547,20 +432,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
                            AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
 }
 
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
-{
-       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
-                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
-                                  hda_nid_t cvt_nid, int chs)
-{
-       if (chs != hdmi_get_channel_count(codec, cvt_nid))
-               snd_hda_codec_write(codec, cvt_nid, 0,
-                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
 /*
  * ELD proc files
  */
@@ -624,339 +495,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
 }
 #endif
 
-/*
- * Channel mapping routines
- */
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
-       int i, j;
-       struct cea_channel_speaker_allocation *p;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               p = channel_allocations + i;
-               p->channels = 0;
-               p->spk_mask = 0;
-               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
-                       if (p->speakers[j]) {
-                               p->channels++;
-                               p->spk_mask |= p->speakers[j];
-                       }
-       }
-}
-
-static int get_channel_allocation_order(int ca)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channel_allocations[i].ca_index == ca)
-                       break;
-       }
-       return i;
-}
-
-/*
- * The transformation takes two steps:
- *
- *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- *           spk_mask => (channel_allocations[])         => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation(struct hda_codec *codec,
-                                  struct hdmi_eld *eld, int channels)
-{
-       int i;
-       int ca = 0;
-       int spk_mask = 0;
-       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
-       /*
-        * CA defaults to 0 for basic stereo audio
-        */
-       if (channels <= 2)
-               return 0;
-
-       /*
-        * expand ELD's speaker allocation mask
-        *
-        * ELD tells the speaker mask in a compact(paired) form,
-        * expand ELD's notions to match the ones used by Audio InfoFrame.
-        */
-       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (eld->info.spk_alloc & (1 << i))
-                       spk_mask |= eld_speaker_allocation_bits[i];
-       }
-
-       /* search for the first working match in the CA table */
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channels == channel_allocations[i].channels &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask) {
-                       ca = channel_allocations[i].ca_index;
-                       break;
-               }
-       }
-
-       if (!ca) {
-               /* if there was no match, select the regular ALSA channel
-                * allocation with the matching number of channels */
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-                       if (channels == channel_allocations[i].channels) {
-                               ca = channel_allocations[i].ca_index;
-                               break;
-                       }
-               }
-       }
-
-       snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
-       codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-                   ca, channels, buf);
-
-       return ca;
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-       int channel;
-
-       for (i = 0; i < 8; i++) {
-               channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
-               codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n",
-                                               channel, i);
-       }
-#endif
-}
-
-static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid,
-                                      bool non_pcm,
-                                      int ca)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct cea_channel_speaker_allocation *ch_alloc;
-       int i;
-       int err;
-       int order;
-       int non_pcm_mapping[8];
-
-       order = get_channel_allocation_order(ca);
-       ch_alloc = &channel_allocations[order];
-
-       if (hdmi_channel_mapping[ca][1] == 0) {
-               int hdmi_slot = 0;
-               /* fill actual channel mappings in ALSA channel (i) order */
-               for (i = 0; i < ch_alloc->channels; i++) {
-                       while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
-                               hdmi_slot++; /* skip zero slots */
-
-                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
-               }
-               /* fill the rest of the slots with ALSA channel 0xf */
-               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
-                       if (!ch_alloc->speakers[7 - hdmi_slot])
-                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
-       }
-
-       if (non_pcm) {
-               for (i = 0; i < ch_alloc->channels; i++)
-                       non_pcm_mapping[i] = (i << 4) | i;
-               for (; i < 8; i++)
-                       non_pcm_mapping[i] = (0xf << 4) | i;
-       }
-
-       for (i = 0; i < 8; i++) {
-               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
-               int hdmi_slot = slotsetup & 0x0f;
-               int channel = (slotsetup & 0xf0) >> 4;
-               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel);
-               if (err) {
-                       codec_dbg(codec, "HDMI: channel mapping failed\n");
-                       break;
-               }
-       }
-}
-
-struct channel_map_table {
-       unsigned char map;              /* ALSA API channel map position */
-       int spk_mask;                   /* speaker position bit mask */
-};
-
-static struct channel_map_table map_tables[] = {
-       { SNDRV_CHMAP_FL,       FL },
-       { SNDRV_CHMAP_FR,       FR },
-       { SNDRV_CHMAP_RL,       RL },
-       { SNDRV_CHMAP_RR,       RR },
-       { SNDRV_CHMAP_LFE,      LFE },
-       { SNDRV_CHMAP_FC,       FC },
-       { SNDRV_CHMAP_RLC,      RLC },
-       { SNDRV_CHMAP_RRC,      RRC },
-       { SNDRV_CHMAP_RC,       RC },
-       { SNDRV_CHMAP_FLC,      FLC },
-       { SNDRV_CHMAP_FRC,      FRC },
-       { SNDRV_CHMAP_TFL,      FLH },
-       { SNDRV_CHMAP_TFR,      FRH },
-       { SNDRV_CHMAP_FLW,      FLW },
-       { SNDRV_CHMAP_FRW,      FRW },
-       { SNDRV_CHMAP_TC,       TC },
-       { SNDRV_CHMAP_TFC,      FCH },
-       {} /* terminator */
-};
-
-/* from ALSA API channel position to speaker bit mask */
-static int to_spk_mask(unsigned char c)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->map == c)
-                       return t->spk_mask;
-       }
-       return 0;
-}
-
-/* from ALSA API channel position to CEA slot */
-static int to_cea_slot(int ordered_ca, unsigned char pos)
-{
-       int mask = to_spk_mask(pos);
-       int i;
-
-       if (mask) {
-               for (i = 0; i < 8; i++) {
-                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
-                               return i;
-               }
-       }
-
-       return -1;
-}
-
-/* from speaker bit mask to ALSA API channel position */
-static int spk_to_chmap(int spk)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->spk_mask == spk)
-                       return t->map;
-       }
-       return 0;
-}
-
-/* from CEA slot to ALSA API channel position */
-static int from_cea_slot(int ordered_ca, unsigned char slot)
-{
-       int mask = channel_allocations[ordered_ca].speakers[7 - slot];
-
-       return spk_to_chmap(mask);
-}
-
-/* get the CA index corresponding to the given ALSA API channel map */
-static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
-{
-       int i, spks = 0, spk_mask = 0;
-
-       for (i = 0; i < chs; i++) {
-               int mask = to_spk_mask(map[i]);
-               if (mask) {
-                       spk_mask |= mask;
-                       spks++;
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if ((chs == channel_allocations[i].channels ||
-                    spks == channel_allocations[i].channels) &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask)
-                       return channel_allocations[i].ca_index;
-       }
-       return -1;
-}
-
-/* set up the channel slots for the given ALSA API channel map */
-static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
-                                            hda_nid_t pin_nid,
-                                            int chs, unsigned char *map,
-                                            int ca)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int ordered_ca = get_channel_allocation_order(ca);
-       int alsa_pos, hdmi_slot;
-       int assignments[8] = {[0 ... 7] = 0xf};
-
-       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
-
-               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
-
-               if (hdmi_slot < 0)
-                       continue; /* unassigned channel */
-
-               assignments[hdmi_slot] = alsa_pos;
-       }
-
-       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
-               int err;
-
-               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot,
-                                                    assignments[hdmi_slot]);
-               if (err)
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-/* store ALSA API channel map from the current default map */
-static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
-{
-       int i;
-       int ordered_ca = get_channel_allocation_order(ca);
-       for (i = 0; i < 8; i++) {
-               if (i < channel_allocations[ordered_ca].channels)
-                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
-               else
-                       map[i] = 0;
-       }
-}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid, bool non_pcm, int ca,
-                                      int channels, unsigned char *map,
-                                      bool chmap_set)
-{
-       if (!non_pcm && chmap_set) {
-               hdmi_manual_setup_channel_mapping(codec, pin_nid,
-                                                 channels, map, ca);
-       } else {
-               hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
-               hdmi_setup_fake_chmap(map, ca);
-       }
-
-       hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                    int asp_slot, int channel)
-{
-       return snd_hda_codec_write(codec, pin_nid, 0,
-                                  AC_VERB_SET_HDMI_CHAN_SLOT,
-                                  (channel << 4) | asp_slot);
-}
-
-static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                    int asp_slot)
-{
-       return (snd_hda_codec_read(codec, pin_nid, 0,
-                                  AC_VERB_GET_HDMI_CHAN_SLOT,
-                                  asp_slot) & 0xf0) >> 4;
-}
-
 /*
  * Audio InfoFrame routines
  */
@@ -1132,11 +670,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                       bool non_pcm)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdac_chmap *chmap = &spec->chmap;
        hda_nid_t pin_nid = per_pin->pin_nid;
        int channels = per_pin->channels;
        int active_channels;
        struct hdmi_eld *eld;
-       int ca, ordered_ca;
+       int ca;
 
        if (!channels)
                return;
@@ -1148,25 +687,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
 
        eld = &per_pin->sink_eld;
 
-       if (!non_pcm && per_pin->chmap_set)
-               ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
-       else
-               ca = hdmi_channel_allocation(codec, eld, channels);
-       if (ca < 0)
-               ca = 0;
+       ca = snd_hdac_channel_allocation(&codec->core,
+                       eld->info.spk_alloc, channels,
+                       per_pin->chmap_set, non_pcm, per_pin->chmap);
 
-       ordered_ca = get_channel_allocation_order(ca);
-       active_channels = channel_allocations[ordered_ca].channels;
+       active_channels = snd_hdac_get_active_channels(ca);
 
-       hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
+       chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid,
+                                               active_channels);
 
        /*
         * always configure channel mapping, it may have been changed by the
         * user in the meantime
         */
-       hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                  channels, per_pin->chmap,
-                                  per_pin->chmap_set);
+       snd_hdac_setup_channel_mapping(&spec->chmap,
+                               pin_nid, non_pcm, ca, channels,
+                               per_pin->chmap, per_pin->chmap_set);
 
        spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
                                      eld->info.conn_type);
@@ -1338,6 +874,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
        return 0;
 }
 
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
 static int hdmi_choose_cvt(struct hda_codec *codec,
                        int pin_idx, int *cvt_id, int *mux_id)
 {
@@ -1346,7 +887,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
        struct hdmi_spec_per_cvt *per_cvt = NULL;
        int cvt_idx, mux_idx = 0;
 
-       per_pin = get_pin(spec, pin_idx);
+       /* pin_idx < 0 means no pin will be bound to the converter */
+       if (pin_idx < 0)
+               per_pin = NULL;
+       else
+               per_pin = get_pin(spec, pin_idx);
 
        /* Dynamically assign converter to stream */
        for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1355,6 +900,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
                /* Must not already be assigned */
                if (per_cvt->assigned)
                        continue;
+               if (per_pin == NULL)
+                       break;
                /* Must be in pin's mux's list of converters */
                for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
                        if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
@@ -1367,9 +914,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 
        /* No free converters */
        if (cvt_idx == spec->num_cvts)
-               return -ENODEV;
+               return -EBUSY;
 
-       per_pin->mux_idx = mux_idx;
+       if (per_pin != NULL)
+               per_pin->mux_idx = mux_idx;
 
        if (cvt_id)
                *cvt_id = cvt_idx;
@@ -1395,6 +943,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
                                            mux_idx);
 }
 
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+                       hda_nid_t cvt_nid)
+{
+       int i;
+
+       for (i = 0; i < spec->num_cvts; i++)
+               if (spec->cvt_nids[i] == cvt_nid)
+                       return i;
+       return -EINVAL;
+}
+
 /* Intel HDMI workaround to fix audio routing issue:
  * For some Intel display codecs, pins share the same connection list.
  * So a conveter can be selected by multiple pins and playback on any of these
@@ -1446,6 +1008,74 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
        }
 }
 
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+                       hda_nid_t pin_nid, hda_nid_t cvt_nid)
+{
+       int mux_idx;
+       struct hdmi_spec *spec = codec->spec;
+
+       if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
+               return;
+
+       /* On Intel platform, the mapping of converter nid to
+        * mux index of the pins are always the same.
+        * The pin nid may be 0, this means all pins will not
+        * share the converter.
+        */
+       mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+       if (mux_idx >= 0)
+               intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM
+ * in dyn_pcm_assign mode.
+ */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+                        struct hda_codec *codec,
+                        struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int cvt_idx, pcm_idx;
+       struct hdmi_spec_per_cvt *per_cvt = NULL;
+       int err;
+
+       pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+       if (pcm_idx < 0)
+               return -EINVAL;
+
+       err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+       if (err)
+               return err;
+
+       per_cvt = get_cvt(spec, cvt_idx);
+       per_cvt->assigned = 1;
+       hinfo->nid = per_cvt->cvt_nid;
+
+       intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+
+       set_bit(pcm_idx, &spec->pcm_in_use);
+       /* todo: setup spdif ctls assign */
+
+       /* Initially set the converter's capabilities */
+       hinfo->channels_min = per_cvt->channels_min;
+       hinfo->channels_max = per_cvt->channels_max;
+       hinfo->rates = per_cvt->rates;
+       hinfo->formats = per_cvt->formats;
+       hinfo->maxbps = per_cvt->maxbps;
+
+       /* Store the updated parameters */
+       runtime->hw.channels_min = hinfo->channels_min;
+       runtime->hw.channels_max = hinfo->channels_max;
+       runtime->hw.formats = hinfo->formats;
+       runtime->hw.rates = hinfo->rates;
+
+       snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+       return 0;
+}
+
 /*
  * HDA PCM callbacks
  */
@@ -1455,26 +1085,47 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 {
        struct hdmi_spec *spec = codec->spec;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int pin_idx, cvt_idx, mux_idx = 0;
+       int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
        struct hdmi_spec_per_cvt *per_cvt = NULL;
        int err;
 
        /* Validate hinfo */
-       pin_idx = hinfo_to_pin_index(codec, hinfo);
-       if (snd_BUG_ON(pin_idx < 0))
+       pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+       if (pcm_idx < 0)
                return -EINVAL;
-       per_pin = get_pin(spec, pin_idx);
-       eld = &per_pin->sink_eld;
+
+       mutex_lock(&spec->pcm_lock);
+       pin_idx = hinfo_to_pin_index(codec, hinfo);
+       if (!spec->dyn_pcm_assign) {
+               if (snd_BUG_ON(pin_idx < 0)) {
+                       mutex_unlock(&spec->pcm_lock);
+                       return -EINVAL;
+               }
+       } else {
+               /* no pin is assigned to the PCM
+                * PA need pcm open successfully when probe
+                */
+               if (pin_idx < 0) {
+                       err = hdmi_pcm_open_no_pin(hinfo, codec, substream);
+                       mutex_unlock(&spec->pcm_lock);
+                       return err;
+               }
+       }
 
        err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
-       if (err < 0)
+       if (err < 0) {
+               mutex_unlock(&spec->pcm_lock);
                return err;
+       }
 
        per_cvt = get_cvt(spec, cvt_idx);
        /* Claim converter */
        per_cvt->assigned = 1;
+
+       set_bit(pcm_idx, &spec->pcm_in_use);
+       per_pin = get_pin(spec, pin_idx);
        per_pin->cvt_nid = per_cvt->cvt_nid;
        hinfo->nid = per_cvt->cvt_nid;
 
@@ -1486,7 +1137,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        if (is_haswell_plus(codec) || is_valleyview_plus(codec))
                intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
 
-       snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+       snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
 
        /* Initially set the converter's capabilities */
        hinfo->channels_min = per_cvt->channels_min;
@@ -1495,6 +1146,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        hinfo->formats = per_cvt->formats;
        hinfo->maxbps = per_cvt->maxbps;
 
+       eld = &per_pin->sink_eld;
        /* Restrict capabilities by ELD if this isn't disabled */
        if (!static_hdmi_pcm && eld->eld_valid) {
                snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
@@ -1502,11 +1154,13 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                    !hinfo->rates || !hinfo->formats) {
                        per_cvt->assigned = 0;
                        hinfo->nid = 0;
-                       snd_hda_spdif_ctls_unassign(codec, pin_idx);
+                       snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+                       mutex_unlock(&spec->pcm_lock);
                        return -ENODEV;
                }
        }
 
+       mutex_unlock(&spec->pcm_lock);
        /* Store the updated parameters */
        runtime->hw.channels_min = hinfo->channels_min;
        runtime->hw.channels_max = hinfo->channels_max;
@@ -1541,6 +1195,125 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
        return 0;
 }
 
+static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int i;
+
+       /* try the prefer PCM */
+       if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+               return per_pin->pin_nid_idx;
+
+       /* have a second try; check the "reserved area" over num_pins */
+       for (i = spec->num_pins; i < spec->pcm_used; i++) {
+               if (!test_bit(i, &spec->pcm_bitmap))
+                       return i;
+       }
+
+       /* the last try; check the empty slots in pins */
+       for (i = 0; i < spec->num_pins; i++) {
+               if (!test_bit(i, &spec->pcm_bitmap))
+                       return i;
+       }
+       return -EBUSY;
+}
+
+static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int idx;
+
+       /* pcm already be attached to the pin */
+       if (per_pin->pcm)
+               return;
+       idx = hdmi_find_pcm_slot(spec, per_pin);
+       if (idx == -EBUSY)
+               return;
+       per_pin->pcm_idx = idx;
+       per_pin->pcm = get_hdmi_pcm(spec, idx);
+       set_bit(idx, &spec->pcm_bitmap);
+}
+
+static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int idx;
+
+       /* pcm already be detached from the pin */
+       if (!per_pin->pcm)
+               return;
+       idx = per_pin->pcm_idx;
+       per_pin->pcm_idx = -1;
+       per_pin->pcm = NULL;
+       if (idx >= 0 && idx < spec->pcm_used)
+               clear_bit(idx, &spec->pcm_bitmap);
+}
+
+static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec,
+               struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid)
+{
+       int mux_idx;
+
+       for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+               if (per_pin->mux_nids[mux_idx] == cvt_nid)
+                       break;
+       return mux_idx;
+}
+
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid);
+
+static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
+                          struct hdmi_spec_per_pin *per_pin)
+{
+       struct hda_codec *codec = per_pin->codec;
+       struct hda_pcm *pcm;
+       struct hda_pcm_stream *hinfo;
+       struct snd_pcm_substream *substream;
+       int mux_idx;
+       bool non_pcm;
+
+       if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+               pcm = get_pcm_rec(spec, per_pin->pcm_idx);
+       else
+               return;
+       if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use))
+               return;
+
+       /* hdmi audio only uses playback and one substream */
+       hinfo = pcm->stream;
+       substream = pcm->pcm->streams[0].substream;
+
+       per_pin->cvt_nid = hinfo->nid;
+
+       mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
+       if (mux_idx < per_pin->num_mux_nids)
+               snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+                               AC_VERB_SET_CONNECT_SEL,
+                               mux_idx);
+       snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
+
+       non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
+       if (substream->runtime)
+               per_pin->channels = substream->runtime->channels;
+       per_pin->setup = true;
+       per_pin->mux_idx = mux_idx;
+
+       hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+}
+
+static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
+                          struct hdmi_spec_per_pin *per_pin)
+{
+       if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+               snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx);
+
+       per_pin->chmap_set = false;
+       memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+       per_pin->setup = false;
+       per_pin->channels = 0;
+}
+
 /* update per_pin ELD from the given new ELD;
  * setup info frame and notification accordingly
  */
@@ -1549,8 +1322,27 @@ static void update_eld(struct hda_codec *codec,
                       struct hdmi_eld *eld)
 {
        struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+       struct hdmi_spec *spec = codec->spec;
        bool old_eld_valid = pin_eld->eld_valid;
        bool eld_changed;
+       int pcm_idx = -1;
+
+       /* for monitor disconnection, save pcm_idx firstly */
+       pcm_idx = per_pin->pcm_idx;
+       if (spec->dyn_pcm_assign) {
+               if (eld->eld_valid) {
+                       hdmi_attach_hda_pcm(spec, per_pin);
+                       hdmi_pcm_setup_pin(spec, per_pin);
+               } else {
+                       hdmi_pcm_reset_pin(spec, per_pin);
+                       hdmi_detach_hda_pcm(spec, per_pin);
+               }
+       }
+       /* if pcm_idx == -1, it means this is in monitor connection event
+        * we can get the correct pcm_idx now.
+        */
+       if (pcm_idx == -1)
+               pcm_idx = per_pin->pcm_idx;
 
        if (eld->eld_valid)
                snd_hdmi_show_eld(codec, &eld->info);
@@ -1584,11 +1376,11 @@ static void update_eld(struct hda_codec *codec,
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        }
 
-       if (eld_changed)
+       if (eld_changed && pcm_idx >= 0)
                snd_ctl_notify(codec->card,
                               SNDRV_CTL_EVENT_MASK_VALUE |
                               SNDRV_CTL_EVENT_MASK_INFO,
-                              &per_pin->eld_ctl->id);
+                              &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
 }
 
 /* update ELD and jack state via HD-audio verbs */
@@ -1656,12 +1448,36 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
        return ret;
 }
 
+static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
+                                struct hdmi_spec_per_pin *per_pin)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_jack *jack = NULL;
+       struct hda_jack_tbl *jack_tbl;
+
+       /* if !dyn_pcm_assign, get jack from hda_jack_tbl
+        * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
+        * NULL even after snd_hda_jack_tbl_clear() is called to
+        * free snd_jack. This may cause access invalid memory
+        * when calling snd_jack_report
+        */
+       if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign)
+               jack = spec->pcm_rec[per_pin->pcm_idx].jack;
+       else if (!spec->dyn_pcm_assign) {
+               jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+               if (jack_tbl)
+                       jack = jack_tbl->jack;
+       }
+       return jack;
+}
+
 /* update ELD and jack state via audio component */
 static void sync_eld_via_acomp(struct hda_codec *codec,
                               struct hdmi_spec_per_pin *per_pin)
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld = &spec->temp_eld;
+       struct snd_jack *jack = NULL;
        int size;
 
        mutex_lock(&per_pin->lock);
@@ -1685,8 +1501,16 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
                eld->eld_size = 0;
        }
 
+       /* pcm_idx >=0 before update_eld() means it is in monitor
+        * disconnected event. Jack must be fetched before update_eld()
+        */
+       jack = pin_idx_to_jack(codec, per_pin);
        update_eld(codec, per_pin, eld);
-       snd_jack_report(per_pin->acomp_jack,
+       if (jack == NULL)
+               jack = pin_idx_to_jack(codec, per_pin);
+       if (jack == NULL)
+               goto unlock;
+       snd_jack_report(jack,
                        eld->monitor_present ? SND_JACK_AVOUT : 0);
  unlock:
        mutex_unlock(&per_pin->lock);
@@ -1695,13 +1519,19 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
 static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
 {
        struct hda_codec *codec = per_pin->codec;
+       struct hdmi_spec *spec = codec->spec;
+       int ret;
 
+       mutex_lock(&spec->pcm_lock);
        if (codec_has_acomp(codec)) {
                sync_eld_via_acomp(codec, per_pin);
-               return false; /* don't call snd_hda_jack_report_sync() */
+               ret = false; /* don't call snd_hda_jack_report_sync() */
        } else {
-               return hdmi_present_sense_via_verbs(per_pin, repoll);
+               ret = hdmi_present_sense_via_verbs(per_pin, repoll);
        }
+       mutex_unlock(&spec->pcm_lock);
+
+       return ret;
 }
 
 static void hdmi_repoll_eld(struct work_struct *work)
@@ -1745,6 +1575,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 
        per_pin->pin_nid = pin_nid;
        per_pin->non_pcm = false;
+       if (spec->dyn_pcm_assign)
+               per_pin->pcm_idx = -1;
+       else {
+               per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
+               per_pin->pcm_idx = pin_idx;
+       }
+       per_pin->pin_nid_idx = pin_idx;
 
        err = hdmi_read_pin_conn(codec, pin_idx);
        if (err < 0)
@@ -1773,8 +1610,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        per_cvt->channels_min = 2;
        if (chans <= 16) {
                per_cvt->channels_max = chans;
-               if (chans > spec->channels_max)
-                       spec->channels_max = chans;
+               if (chans > spec->chmap.channels_max)
+                       spec->chmap.channels_max = chans;
        }
 
        err = snd_hda_query_supported_pcm(codec, cvt_nid,
@@ -1851,13 +1688,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 {
        hda_nid_t cvt_nid = hinfo->nid;
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = hinfo_to_pin_index(codec, hinfo);
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       hda_nid_t pin_nid = per_pin->pin_nid;
+       int pin_idx;
+       struct hdmi_spec_per_pin *per_pin;
+       hda_nid_t pin_nid;
        struct snd_pcm_runtime *runtime = substream->runtime;
        bool non_pcm;
        int pinctl;
+       int err;
+
+       mutex_lock(&spec->pcm_lock);
+       pin_idx = hinfo_to_pin_index(codec, hinfo);
+       if (spec->dyn_pcm_assign && pin_idx < 0) {
+               /* when dyn_pcm_assign and pcm is not bound to a pin
+                * skip pin setup and return 0 to make audio playback
+                * be ongoing
+                */
+               intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+               snd_hda_codec_setup_stream(codec, cvt_nid,
+                                       stream_tag, 0, format);
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
 
+       if (snd_BUG_ON(pin_idx < 0)) {
+               mutex_unlock(&spec->pcm_lock);
+               return -EINVAL;
+       }
+       per_pin = get_pin(spec, pin_idx);
+       pin_nid = per_pin->pin_nid;
        if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
                /* Verify pin:cvt selections to avoid silent audio after S3.
                 * After S3, the audio driver restores pin:cvt selections
@@ -1882,7 +1740,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
        mutex_unlock(&per_pin->lock);
-
        if (spec->dyn_pin_out) {
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@@ -1891,7 +1748,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                    pinctl | PIN_OUT);
        }
 
-       return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+       err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
+                                stream_tag, format);
+       mutex_unlock(&spec->pcm_lock);
+       return err;
 }
 
 static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1907,12 +1767,15 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                          struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
-       int cvt_idx, pin_idx;
+       int cvt_idx, pin_idx, pcm_idx;
        struct hdmi_spec_per_cvt *per_cvt;
        struct hdmi_spec_per_pin *per_pin;
        int pinctl;
 
        if (hinfo->nid) {
+               pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+               if (snd_BUG_ON(pcm_idx < 0))
+                       return -EINVAL;
                cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
                if (snd_BUG_ON(cvt_idx < 0))
                        return -EINVAL;
@@ -1922,9 +1785,19 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_cvt->assigned = 0;
                hinfo->nid = 0;
 
+               mutex_lock(&spec->pcm_lock);
+               snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+               clear_bit(pcm_idx, &spec->pcm_in_use);
                pin_idx = hinfo_to_pin_index(codec, hinfo);
-               if (snd_BUG_ON(pin_idx < 0))
+               if (spec->dyn_pcm_assign && pin_idx < 0) {
+                       mutex_unlock(&spec->pcm_lock);
+                       return 0;
+               }
+
+               if (snd_BUG_ON(pin_idx < 0)) {
+                       mutex_unlock(&spec->pcm_lock);
                        return -EINVAL;
+               }
                per_pin = get_pin(spec, pin_idx);
 
                if (spec->dyn_pin_out) {
@@ -1935,8 +1808,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                                            pinctl & ~PIN_OUT);
                }
 
-               snd_hda_spdif_ctls_unassign(codec, pin_idx);
-
                mutex_lock(&per_pin->lock);
                per_pin->chmap_set = false;
                memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
@@ -1944,6 +1815,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_pin->setup = false;
                per_pin->channels = 0;
                mutex_unlock(&per_pin->lock);
+               mutex_unlock(&spec->pcm_lock);
        }
 
        return 0;
@@ -1956,162 +1828,42 @@ static const struct hda_pcm_ops generic_ops = {
        .cleanup = generic_hdmi_playback_pcm_cleanup,
 };
 
-/*
- * ALSA API channel-map control callbacks
- */
-static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_info *uinfo)
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap)
 {
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        struct hdmi_spec *spec = codec->spec;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = spec->channels_max;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
-       return 0;
-}
-
-static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                 int channels)
-{
-       /* If the speaker allocation matches the channel count, it is OK.*/
-       if (cap->channels != channels)
-               return -1;
-
-       /* all channels are remappable freely */
-       return SNDRV_CTL_TLVT_CHMAP_VAR;
-}
-
-static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
-                                       unsigned int *chmap, int channels)
-{
-       int count = 0;
-       int c;
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 
-       for (c = 7; c >= 0; c--) {
-               int spk = cap->speakers[c];
-               if (!spk)
-                       continue;
-
-               chmap[count++] = spk_to_chmap(spk);
-       }
-
-       WARN_ON(count != channels);
-}
-
-static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                             unsigned int size, unsigned int __user *tlv)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
-       struct hdmi_spec *spec = codec->spec;
-       unsigned int __user *dst;
-       int chs, count = 0;
+       /* chmap is already set to 0 in caller */
+       if (!per_pin)
+               return;
 
-       if (size < 8)
-               return -ENOMEM;
-       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
-               return -EFAULT;
-       size -= 8;
-       dst = tlv + 2;
-       for (chs = 2; chs <= spec->channels_max; chs++) {
-               int i;
-               struct cea_channel_speaker_allocation *cap;
-               cap = channel_allocations;
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
-                       int chs_bytes = chs * 4;
-                       int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
-                       unsigned int tlv_chmap[8];
-
-                       if (type < 0)
-                               continue;
-                       if (size < 8)
-                               return -ENOMEM;
-                       if (put_user(type, dst) ||
-                           put_user(chs_bytes, dst + 1))
-                               return -EFAULT;
-                       dst += 2;
-                       size -= 8;
-                       count += 8;
-                       if (size < chs_bytes)
-                               return -ENOMEM;
-                       size -= chs_bytes;
-                       count += chs_bytes;
-                       spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs);
-                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
-                               return -EFAULT;
-                       dst += chs;
-               }
-       }
-       if (put_user(count, tlv + 1))
-               return -EFAULT;
-       return 0;
+       memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
 }
 
-static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+                               unsigned char *chmap, int prepared)
 {
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
-               ucontrol->value.integer.value[i] = per_pin->chmap[i];
-       return 0;
-}
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 
-static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
-       struct hdmi_spec *spec = codec->spec;
-       int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       unsigned int ctl_idx;
-       struct snd_pcm_substream *substream;
-       unsigned char chmap[8];
-       int i, err, ca, prepared = 0;
-
-       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       substream = snd_pcm_chmap_substream(info, ctl_idx);
-       if (!substream || !substream->runtime)
-               return 0; /* just for avoiding error from alsactl restore */
-       switch (substream->runtime->status->state) {
-       case SNDRV_PCM_STATE_OPEN:
-       case SNDRV_PCM_STATE_SETUP:
-               break;
-       case SNDRV_PCM_STATE_PREPARED:
-               prepared = 1;
-               break;
-       default:
-               return -EBUSY;
-       }
-       memset(chmap, 0, sizeof(chmap));
-       for (i = 0; i < ARRAY_SIZE(chmap); i++)
-               chmap[i] = ucontrol->value.integer.value[i];
-       if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
-               return 0;
-       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
-       if (ca < 0)
-               return -EINVAL;
-       if (spec->ops.chmap_validate) {
-               err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
-               if (err)
-                       return err;
-       }
        mutex_lock(&per_pin->lock);
        per_pin->chmap_set = true;
-       memcpy(per_pin->chmap, chmap, sizeof(chmap));
+       memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
        if (prepared)
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        mutex_unlock(&per_pin->lock);
+}
 
-       return 0;
+static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+       return per_pin ? true:false;
 }
 
 static int generic_hdmi_build_pcms(struct hda_codec *codec)
@@ -2126,7 +1878,9 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
                info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
                if (!info)
                        return -ENOMEM;
-               spec->pcm_rec[pin_idx] = info;
+
+               spec->pcm_rec[pin_idx].pcm = info;
+               spec->pcm_used++;
                info->pcm_type = HDA_PCM_TYPE_HDMI;
                info->own_chmap = true;
 
@@ -2139,15 +1893,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static void free_acomp_jack_priv(struct snd_jack *jack)
+static void free_hdmi_jack_priv(struct snd_jack *jack)
 {
-       struct hdmi_spec_per_pin *per_pin = jack->private_data;
+       struct hdmi_pcm *pcm = jack->private_data;
 
-       per_pin->acomp_jack = NULL;
+       pcm->jack = NULL;
 }
 
-static int add_acomp_jack_kctl(struct hda_codec *codec,
-                              struct hdmi_spec_per_pin *per_pin,
+static int add_hdmi_jack_kctl(struct hda_codec *codec,
+                              struct hdmi_spec *spec,
+                              int pcm_idx,
                               const char *name)
 {
        struct snd_jack *jack;
@@ -2157,88 +1912,107 @@ static int add_acomp_jack_kctl(struct hda_codec *codec,
                           true, false);
        if (err < 0)
                return err;
-       per_pin->acomp_jack = jack;
-       jack->private_data = per_pin;
-       jack->private_free = free_acomp_jack_priv;
+
+       spec->pcm_rec[pcm_idx].jack = jack;
+       jack->private_data = &spec->pcm_rec[pcm_idx];
+       jack->private_free = free_hdmi_jack_priv;
        return 0;
 }
 
-static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
 {
        char hdmi_str[32] = "HDMI/DP";
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       int pcmdev = get_pcm_rec(spec, pin_idx)->device;
+       struct hdmi_spec_per_pin *per_pin;
+       struct hda_jack_tbl *jack;
+       int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
        bool phantom_jack;
+       int ret;
 
        if (pcmdev > 0)
                sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
-       if (codec_has_acomp(codec))
-               return add_acomp_jack_kctl(codec, per_pin, hdmi_str);
+
+       if (spec->dyn_pcm_assign)
+               return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
+
+       /* for !dyn_pcm_assign, we still use hda_jack for compatibility */
+       /* if !dyn_pcm_assign, it must be non-MST mode.
+        * This means pcms and pins are statically mapped.
+        * And pcm_idx is pin_idx.
+        */
+       per_pin = get_pin(spec, pcm_idx);
        phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
        if (phantom_jack)
                strncat(hdmi_str, " Phantom",
                        sizeof(hdmi_str) - strlen(hdmi_str) - 1);
-
-       return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
-                                    phantom_jack);
+       ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
+                                   phantom_jack);
+       if (ret < 0)
+               return ret;
+       jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+       if (jack == NULL)
+               return 0;
+       /* assign jack->jack to pcm_rec[].jack to
+        * align with dyn_pcm_assign mode
+        */
+       spec->pcm_rec[pcm_idx].jack = jack->jack;
+       return 0;
 }
 
 static int generic_hdmi_build_controls(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
        int err;
-       int pin_idx;
+       int pin_idx, pcm_idx;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
-               err = generic_hdmi_build_jack(codec, pin_idx);
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+               err = generic_hdmi_build_jack(codec, pcm_idx);
                if (err < 0)
                        return err;
 
-               err = snd_hda_create_dig_out_ctls(codec,
+               /* create the spdif for each pcm
+                * pin will be bound when monitor is connected
+                */
+               if (spec->dyn_pcm_assign)
+                       err = snd_hda_create_dig_out_ctls(codec,
+                                         0, spec->cvt_nids[0],
+                                         HDA_PCM_TYPE_HDMI);
+               else {
+                       struct hdmi_spec_per_pin *per_pin =
+                               get_pin(spec, pcm_idx);
+                       err = snd_hda_create_dig_out_ctls(codec,
                                                  per_pin->pin_nid,
                                                  per_pin->mux_nids[0],
                                                  HDA_PCM_TYPE_HDMI);
+               }
                if (err < 0)
                        return err;
-               snd_hda_spdif_ctls_unassign(codec, pin_idx);
+               snd_hda_spdif_ctls_unassign(codec, pcm_idx);
 
                /* add control for ELD Bytes */
-               err = hdmi_create_eld_ctl(codec, pin_idx,
-                                         get_pcm_rec(spec, pin_idx)->device);
-
+               err = hdmi_create_eld_ctl(codec, pcm_idx,
+                                       get_pcm_rec(spec, pcm_idx)->device);
                if (err < 0)
                        return err;
+       }
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
                hdmi_present_sense(per_pin, 0);
        }
 
        /* add channel maps */
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
                struct hda_pcm *pcm;
-               struct snd_pcm_chmap *chmap;
-               struct snd_kcontrol *kctl;
-               int i;
 
-               pcm = spec->pcm_rec[pin_idx];
+               pcm = get_pcm_rec(spec, pcm_idx);
                if (!pcm || !pcm->pcm)
                        break;
-               err = snd_pcm_add_chmap_ctls(pcm->pcm,
-                                            SNDRV_PCM_STREAM_PLAYBACK,
-                                            NULL, 0, pin_idx, &chmap);
+               err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap);
                if (err < 0)
                        return err;
-               /* override handlers */
-               chmap->private_data = codec;
-               kctl = chmap->kctl;
-               for (i = 0; i < kctl->count; i++)
-                       kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
-               kctl->info = hdmi_chmap_ctl_info;
-               kctl->get = hdmi_chmap_ctl_get;
-               kctl->put = hdmi_chmap_ctl_put;
-               kctl->tlv.c = hdmi_chmap_ctl_tlv;
        }
 
        return 0;
@@ -2293,18 +2067,25 @@ static void hdmi_array_free(struct hdmi_spec *spec)
 static void generic_hdmi_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx;
+       int pin_idx, pcm_idx;
 
        if (codec_has_acomp(codec))
                snd_hdac_i915_register_notifier(NULL);
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-
                cancel_delayed_work_sync(&per_pin->work);
                eld_proc_free(per_pin);
-               if (per_pin->acomp_jack)
-                       snd_device_free(codec->card, per_pin->acomp_jack);
+       }
+
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+               if (spec->pcm_rec[pcm_idx].jack == NULL)
+                       continue;
+               if (spec->dyn_pcm_assign)
+                       snd_device_free(codec->card,
+                                       spec->pcm_rec[pcm_idx].jack);
+               else
+                       spec->pcm_rec[pcm_idx].jack = NULL;
        }
 
        if (spec->i915_bound)
@@ -2343,16 +2124,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
 
 static const struct hdmi_ops generic_standard_hdmi_ops = {
        .pin_get_eld                            = snd_hdmi_get_eld,
-       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
-       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
        .pin_setup_infoframe                    = hdmi_pin_setup_infoframe,
        .pin_hbr_setup                          = hdmi_pin_hbr_setup,
        .setup_stream                           = hdmi_setup_stream,
-       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
-       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
 };
 
-
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
                                             hda_nid_t nid)
 {
@@ -2453,6 +2229,13 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                return -ENOMEM;
 
        spec->ops = generic_standard_hdmi_ops;
+       mutex_init(&spec->pcm_lock);
+       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+       spec->chmap.ops.get_chmap = hdmi_get_chmap;
+       spec->chmap.ops.set_chmap = hdmi_set_chmap;
+       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+
        codec->spec = spec;
        hdmi_array_init(spec, 4);
 
@@ -2496,7 +2279,6 @@ static int patch_generic_hdmi(struct hda_codec *codec)
 
        generic_hdmi_init_per_pins(codec);
 
-       init_channel_allocations();
 
        if (codec_has_acomp(codec)) {
                codec->depop_delay = 0;
@@ -2510,6 +2292,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
        }
 
+       WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
        return 0;
 }
 
@@ -2532,7 +2315,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
        info = snd_hda_codec_pcm_new(codec, "HDMI 0");
        if (!info)
                return -ENOMEM;
-       spec->pcm_rec[0] = info;
+       spec->pcm_rec[0].pcm = info;
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
        *pstr = spec->pcm_playback;
@@ -3043,16 +2826,18 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
  * - 0x10de0015
  * - 0x10de0040
  */
-static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                   int channels)
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels)
 {
        if (cap->ca_index == 0x00 && channels == 2)
                return SNDRV_CTL_TLVT_CHMAP_FIXED;
 
-       return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
+       return chmap->ops.chmap_cea_alloc_validate_get_type(
+                               chmap, cap, channels);
 }
 
-static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+               int ca, int chs, unsigned char *map)
 {
        if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
                return -EINVAL;
@@ -3072,9 +2857,9 @@ static int patch_nvhdmi(struct hda_codec *codec)
        spec = codec->spec;
        spec->dyn_pin_out = true;
 
-       spec->ops.chmap_cea_alloc_validate_get_type =
+       spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                nvhdmi_chmap_cea_alloc_validate_get_type;
-       spec->ops.chmap_validate = nvhdmi_chmap_validate;
+       spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
 
        return 0;
 }
@@ -3322,16 +3107,17 @@ static int atihdmi_paired_swap_fc_lfe(int pos)
        return pos;
 }
 
-static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
+static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
+                       int ca, int chs, unsigned char *map)
 {
-       struct cea_channel_speaker_allocation *cap;
+       struct hdac_cea_channel_speaker_allocation *cap;
        int i, j;
 
        /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
 
-       cap = &channel_allocations[get_channel_allocation_order(ca)];
+       cap = snd_hdac_get_ch_alloc_from_ca(ca);
        for (i = 0; i < chs; ++i) {
-               int mask = to_spk_mask(map[i]);
+               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
                bool ok = false;
                bool companion_ok = false;
 
@@ -3347,7 +3133,7 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
                                if (i % 2 == 0 && i + 1 < chs) {
                                        /* even channel, check the odd companion */
                                        int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
-                                       int comp_mask_req = to_spk_mask(map[i+1]);
+                                       int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]);
                                        int comp_mask_act = cap->speakers[comp_chan_idx];
 
                                        if (comp_mask_req == comp_mask_act)
@@ -3369,9 +3155,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
        return 0;
 }
 
-static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                       int hdmi_slot, int stream_channel)
+static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
+               hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
 {
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        int verb;
        int ati_channel_setup = 0;
 
@@ -3404,9 +3191,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
        return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
 }
 
-static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                       int asp_slot)
+static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
+                               hda_nid_t pin_nid, int asp_slot)
 {
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        bool was_odd = false;
        int ati_asp_slot = asp_slot;
        int verb;
@@ -3433,8 +3221,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
        return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
 }
 
-static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                           int channels)
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
+               struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               int channels)
 {
        int c;
 
@@ -3461,8 +3251,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_s
        return SNDRV_CTL_TLVT_CHMAP_PAIRED;
 }
 
-static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
-                                                 unsigned int *chmap, int channels)
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels)
 {
        /* produce paired maps for pre-rev3 ATI/AMD codecs */
        int count = 0;
@@ -3479,7 +3270,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all
                        continue;
                }
 
-               chmap[count++] = spk_to_chmap(spk);
+               chmap[count++] = snd_hdac_spk_to_chmap(spk);
        }
 
        WARN_ON(count != channels);
@@ -3573,18 +3364,21 @@ static int patch_atihdmi(struct hda_codec *codec)
        spec = codec->spec;
 
        spec->ops.pin_get_eld = atihdmi_pin_get_eld;
-       spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
-       spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
        spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
        spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
        spec->ops.setup_stream = atihdmi_setup_stream;
 
        if (!has_amd_full_remap_support(codec)) {
                /* override to ATI/AMD-specific versions with pairwise mapping */
-               spec->ops.chmap_cea_alloc_validate_get_type =
+               spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                        atihdmi_paired_chmap_cea_alloc_validate_get_type;
-               spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
-               spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
+               spec->chmap.ops.cea_alloc_to_tlv_chmap =
+                               atihdmi_paired_cea_alloc_to_tlv_chmap;
+               spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+               spec->chmap.ops.pin_get_slot_channel =
+                               atihdmi_pin_get_slot_channel;
+               spec->chmap.ops.pin_set_slot_channel =
+                               atihdmi_pin_set_slot_channel;
        }
 
        /* ATI/AMD converters do not advertise all of their capabilities */
@@ -3596,7 +3390,7 @@ static int patch_atihdmi(struct hda_codec *codec)
                per_cvt->maxbps = max(per_cvt->maxbps, 24u);
        }
 
-       spec->channels_max = max(spec->channels_max, 8u);
+       spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
 
        return 0;
 }
index 0a4ad5f..59ab6ce 100644 (file)
 static int (*led_set_func)(int, bool);
 static void (*old_vmaster_hook)(void *, int);
 
-static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
-                                void **rv)
-{
-       bool *found = context;
-       *found = true;
-       return AE_OK;
-}
-
 static bool is_thinkpad(struct hda_codec *codec)
 {
-       bool found = false;
-       if (codec->core.subsystem_id >> 16 != 0x17aa)
-               return false;
-       if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
-               return true;
-       found = false;
-       return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
+       return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+              (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068"));
 }
 
 static void update_tpacpi_mute_led(void *private_data, int enabled)
index 7ea66ee..182d92e 100644 (file)
@@ -6,7 +6,7 @@ menuconfig SND_SOC
        tristate "ALSA for SoC audio support"
        select SND_PCM
        select AC97_BUS if SND_SOC_AC97_BUS
-       select SND_JACK if INPUT=y || INPUT=SND
+       select SND_JACK
        select REGMAP_I2C if I2C
        select REGMAP_SPI if SPI_MASTER
        ---help---
index 1f09d95..3fc6358 100644 (file)
@@ -82,6 +82,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
 static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
 static bool ignore_ctl_error;
 static bool autoclock = true;
+static char *quirk_alias[SNDRV_CARDS];
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -100,6 +101,8 @@ MODULE_PARM_DESC(ignore_ctl_error,
                 "Ignore errors from USB controller for mixer interfaces.");
 module_param(autoclock, bool, 0444);
 MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
+module_param_array(quirk_alias, charp, NULL, 0444);
+MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
 
 /*
  * we keep the snd_usb_audio_t instances by ourselves for merging
@@ -171,8 +174,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
        if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
             altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
            altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
-               int err = snd_usbmidi_create(chip->card, iface,
-                                            &chip->midi_list, NULL);
+               int err = __snd_usbmidi_create(chip->card, iface,
+                                            &chip->midi_list, NULL,
+                                            chip->usb_id);
                if (err < 0) {
                        dev_err(&dev->dev,
                                "%u:%d: cannot create sequencer device\n",
@@ -311,6 +315,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
                snd_usb_endpoint_free(ep);
 
        mutex_destroy(&chip->mutex);
+       dev_set_drvdata(&chip->dev->dev, NULL);
        kfree(chip);
        return 0;
 }
@@ -455,6 +460,48 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        return 0;
 }
 
+/* look for a matching quirk alias id */
+static bool get_alias_id(struct usb_device *dev, unsigned int *id)
+{
+       int i;
+       unsigned int src, dst;
+
+       for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) {
+               if (!quirk_alias[i] ||
+                   sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 ||
+                   src != *id)
+                       continue;
+               dev_info(&dev->dev,
+                        "device (%04x:%04x): applying quirk alias %04x:%04x\n",
+                        USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id),
+                        USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst));
+               *id = dst;
+               return true;
+       }
+
+       return false;
+}
+
+static struct usb_device_id usb_audio_ids[]; /* defined below */
+
+/* look for the corresponding quirk */
+static const struct snd_usb_audio_quirk *
+get_alias_quirk(struct usb_device *dev, unsigned int id)
+{
+       const struct usb_device_id *p;
+
+       for (p = usb_audio_ids; p->match_flags; p++) {
+               /* FIXME: this checks only vendor:product pair in the list */
+               if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) ==
+                   USB_DEVICE_ID_MATCH_DEVICE &&
+                   p->idVendor == USB_ID_VENDOR(id) &&
+                   p->idProduct == USB_ID_PRODUCT(id))
+                       return (const struct snd_usb_audio_quirk *)p->driver_info;
+       }
+
+       return NULL;
+}
+
 /*
  * probe the active usb device
  *
@@ -481,10 +528,12 @@ static int usb_audio_probe(struct usb_interface *intf,
        ifnum = get_iface_desc(alts)->bInterfaceNumber;
        id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
                    le16_to_cpu(dev->descriptor.idProduct));
+       if (get_alias_id(dev, &id))
+               quirk = get_alias_quirk(dev, id);
        if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
                return -ENXIO;
 
-       err = snd_usb_apply_boot_quirk(dev, intf, quirk);
+       err = snd_usb_apply_boot_quirk(dev, intf, quirk, id);
        if (err < 0)
                return err;
 
@@ -503,6 +552,7 @@ static int usb_audio_probe(struct usb_interface *intf,
                                goto __error;
                        }
                        chip = usb_chip[i];
+                       dev_set_drvdata(&dev->dev, chip);
                        atomic_inc(&chip->active); /* avoid autopm */
                        break;
                }
index 007cf58..47de8af 100644 (file)
@@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume);
 /*
  * Creates and registers everything needed for a MIDI streaming interface.
  */
-int snd_usbmidi_create(struct snd_card *card,
-                      struct usb_interface *iface,
-                      struct list_head *midi_list,
-                      const struct snd_usb_audio_quirk *quirk)
+int __snd_usbmidi_create(struct snd_card *card,
+                        struct usb_interface *iface,
+                        struct list_head *midi_list,
+                        const struct snd_usb_audio_quirk *quirk,
+                        unsigned int usb_id)
 {
        struct snd_usb_midi *umidi;
        struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card,
        spin_lock_init(&umidi->disc_lock);
        init_rwsem(&umidi->disc_rwsem);
        mutex_init(&umidi->mutex);
-       umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
+       if (!usb_id)
+               usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
                               le16_to_cpu(umidi->dev->descriptor.idProduct));
+       umidi->usb_id = usb_id;
        setup_timer(&umidi->error_timer, snd_usbmidi_error_timer,
                    (unsigned long)umidi);
 
@@ -2463,4 +2466,4 @@ int snd_usbmidi_create(struct snd_card *card,
        list_add_tail(&umidi->list, midi_list);
        return 0;
 }
-EXPORT_SYMBOL(snd_usbmidi_create);
+EXPORT_SYMBOL(__snd_usbmidi_create);
index ad8a321..5e25a3f 100644 (file)
@@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info {
 
 /* for QUIRK_MIDI_AKAI, data is NULL */
 
-int snd_usbmidi_create(struct snd_card *card,
+int __snd_usbmidi_create(struct snd_card *card,
+                        struct usb_interface *iface,
+                        struct list_head *midi_list,
+                        const struct snd_usb_audio_quirk *quirk,
+                        unsigned int usb_id);
+
+static inline int snd_usbmidi_create(struct snd_card *card,
                       struct usb_interface *iface,
                       struct list_head *midi_list,
-                      const struct snd_usb_audio_quirk *quirk);
+                      const struct snd_usb_audio_quirk *quirk)
+{
+       return __snd_usbmidi_create(card, iface, midi_list, quirk, 0);
+}
+
 void snd_usbmidi_input_stop(struct list_head *p);
 void snd_usbmidi_input_start(struct list_head *p);
 void snd_usbmidi_disconnect(struct list_head *p);
index c458d60..5b03296 100644 (file)
@@ -446,8 +446,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
                const struct snd_usb_audio_quirk *quirk =
                        chip->usb_id == USB_ID(0x0582, 0x002b)
                        ? &ua700_quirk : &uaxx_quirk;
-               return snd_usbmidi_create(chip->card, iface,
-                                         &chip->midi_list, quirk);
+               return __snd_usbmidi_create(chip->card, iface,
+                                         &chip->midi_list, quirk,
+                                         chip->usb_id);
        }
 
        if (altsd->bNumEndpoints != 1)
@@ -974,11 +975,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
 
 int snd_usb_apply_boot_quirk(struct usb_device *dev,
                             struct usb_interface *intf,
-                            const struct snd_usb_audio_quirk *quirk)
+                            const struct snd_usb_audio_quirk *quirk,
+                            unsigned int id)
 {
-       u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                       le16_to_cpu(dev->descriptor.idProduct));
-
        switch (id) {
        case USB_ID(0x041e, 0x3000):
                /* SB Extigy needs special boot-up sequence */
@@ -1184,7 +1183,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
         * "Playback Design" products send bogus feedback data at the start
         * of the stream. Ignore them.
         */
-       if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+       if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba &&
            ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
                ep->skip_packets = 4;
 
@@ -1203,11 +1202,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
 
 void snd_usb_set_interface_quirk(struct usb_device *dev)
 {
+       struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+       if (!chip)
+               return;
        /*
         * "Playback Design" products need a 50ms delay after setting the
         * USB interface.
         */
-       switch (le16_to_cpu(dev->descriptor.idVendor)) {
+       switch (USB_ID_VENDOR(chip->usb_id)) {
        case 0x23ba: /* Playback Design */
        case 0x0644: /* TEAC Corp. */
                mdelay(50);
@@ -1215,15 +1218,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev)
        }
 }
 
+/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */
 void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
                           __u8 request, __u8 requesttype, __u16 value,
                           __u16 index, void *data, __u16 size)
 {
+       struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+       if (!chip)
+               return;
        /*
         * "Playback Design" products need a 20ms delay after each
         * class compliant request
         */
-       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+       if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
 
@@ -1231,23 +1239,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
         * "TEAC Corp." products need a 20ms delay after each
         * class compliant request
         */
-       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) &&
+       if (USB_ID_VENDOR(chip->usb_id) == 0x0644 &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
 
        /* Marantz/Denon devices with USB DAC functionality need a delay
         * after each class compliant request
         */
-       if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                                       le16_to_cpu(dev->descriptor.idProduct)))
+       if (is_marantz_denon_dac(chip->usb_id)
            && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
 
        /* Zoom R16/24 needs a tiny delay here, otherwise requests like
         * get/set frequency return as failed despite actually succeeding.
         */
-       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) &&
-           (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) &&
+       if (chip->usb_id == USB_ID(0x1686, 0x00dd) &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(1);
 }
@@ -1264,7 +1270,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
                                        unsigned int sample_bytes)
 {
        /* Playback Designs */
-       if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
+       if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
                switch (fp->altsetting) {
                case 1:
                        fp->dsd_dop = true;
index 2cd71ed..192ff5c 100644 (file)
@@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
 
 int snd_usb_apply_boot_quirk(struct usb_device *dev,
                             struct usb_interface *intf,
-                            const struct snd_usb_audio_quirk *quirk);
+                            const struct snd_usb_audio_quirk *quirk,
+                            unsigned int usb_id);
 
 void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
                              struct audioformat *fmt);