summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/sound/alsa/ALSA-Configuration.txt9
-rw-r--r--Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt74
-rw-r--r--include/sound/hda_chmap.h76
-rw-r--r--include/sound/jack.h23
-rw-r--r--include/uapi/sound/asequencer.h6
-rw-r--r--include/uapi/sound/asound.h4
-rw-r--r--sound/core/Kconfig9
-rw-r--r--sound/core/compress_offload.c12
-rw-r--r--sound/core/jack.c23
-rw-r--r--sound/core/timer.c268
-rw-r--r--sound/drivers/mts64.c96
-rw-r--r--sound/drivers/portman2x4.c94
-rw-r--r--sound/firewire/bebob/bebob.c21
-rw-r--r--sound/firewire/bebob/bebob.h5
-rw-r--r--sound/firewire/bebob/bebob_midi.c16
-rw-r--r--sound/firewire/bebob/bebob_pcm.c28
-rw-r--r--sound/firewire/bebob/bebob_stream.c58
-rw-r--r--sound/firewire/dice/dice-midi.c23
-rw-r--r--sound/firewire/dice/dice-pcm.c199
-rw-r--r--sound/firewire/dice/dice-stream.c120
-rw-r--r--sound/firewire/dice/dice-transaction.c56
-rw-r--r--sound/firewire/dice/dice.c67
-rw-r--r--sound/firewire/dice/dice.h8
-rw-r--r--sound/firewire/fireworks/fireworks.c3
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c6
-rw-r--r--sound/firewire/oxfw/oxfw-scs1x.c35
-rw-r--r--sound/hda/Makefile2
-rw-r--r--sound/hda/hdmi_chmap.c791
-rw-r--r--sound/mips/Kconfig12
-rw-r--r--sound/mips/Makefile2
-rw-r--r--sound/mips/au1x00.c734
-rw-r--r--sound/pci/Kconfig2
-rw-r--r--sound/pci/hda/Kconfig2
-rw-r--r--sound/pci/hda/hda_eld.c31
-rw-r--r--sound/pci/hda/hda_intel.c2
-rw-r--r--sound/pci/hda/patch_hdmi.c1428
-rw-r--r--sound/pci/hda/thinkpad_helper.c17
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/usb/card.c56
-rw-r--r--sound/usb/midi.c15
-rw-r--r--sound/usb/midi.h14
-rw-r--r--sound/usb/quirks.c36
-rw-r--r--sound/usb/quirks.h3
43 files changed, 2142 insertions, 2346 deletions
diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt
index 48148d6..fc53ccd 100644
--- a/Documentation/sound/alsa/ALSA-Configuration.txt
+++ b/Documentation/sound/alsa/ALSA-Configuration.txt
@@ -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
index 0000000..82744ac
--- /dev/null
+++ b/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt
@@ -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
index 0000000..e20d219
--- /dev/null
+++ b/include/sound/hda_chmap.h
@@ -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 */
diff --git a/include/sound/jack.h b/include/sound/jack.h
index 23bede1..1e84bfb 100644
--- a/include/sound/jack.h
+++ b/include/sound/jack.h
@@ -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
diff --git a/include/uapi/sound/asequencer.h b/include/uapi/sound/asequencer.h
index 5a5fa49..af96f20 100644
--- a/include/uapi/sound/asequencer.h
+++ b/include/uapi/sound/asequencer.h
@@ -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)
diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index a82108e..67bf49d 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -23,7 +23,11 @@
#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>
diff --git a/sound/core/Kconfig b/sound/core/Kconfig
index a2a1e24..6d12ca9 100644
--- a/sound/core/Kconfig
+++ b/sound/core/Kconfig
@@ -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
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 7fac3ca..a9933c0 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -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)
diff --git a/sound/core/jack.c b/sound/core/jack.c
index 7237acb..f652e90 100644
--- a/sound/core/jack.c
+++ b/sound/core/jack.c
@@ -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);
diff --git a/sound/core/timer.c b/sound/core/timer.c
index dca817f..aa1b15c 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -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,35 +528,60 @@ 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.
*
* do not call this from the timer callback!
*/
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);
}
/*
diff --git a/sound/drivers/mts64.c b/sound/drivers/mts64.c
index 2a008a9..fd4d18d 100644
--- a/sound/drivers/mts64.c
+++ b/sound/drivers/mts64.c
@@ -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,
diff --git a/sound/drivers/portman2x4.c b/sound/drivers/portman2x4.c
index 464385a..189e3e7 100644
--- a/sound/drivers/portman2x4.c
+++ b/sound/drivers/portman2x4.c
@@ -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;
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 091290d..3e4e075 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -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);
}
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 4d8fcc7..b50bb33d 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -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);
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 90d95be..868eb0d 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -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;
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index ef224d6..5d7b934 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -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);
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 5022c9b..77cbb02 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -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);
}
/*
diff --git a/sound/firewire/dice/dice-midi.c b/sound/firewire/dice/dice-midi.c
index 151b09f..2461311 100644
--- a/sound/firewire/dice/dice-midi.c
+++ b/sound/firewire/dice/dice-midi.c
@@ -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;
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 9b34319..a5c9b58 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -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)
diff --git a/sound/firewire/dice/dice-stream.c b/sound/firewire/dice/dice-stream.c
index a6a39f7..df035b1 100644
--- a/sound/firewire/dice/dice-stream.c
+++ b/sound/firewire/dice/dice-stream.c
@@ -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;
}
diff --git a/sound/firewire/dice/dice-transaction.c b/sound/firewire/dice/dice-transaction.c
index a4ff4e0..0f03503 100644
--- a/sound/firewire/dice/dice-transaction.c
+++ b/sound/firewire/dice/dice-transaction.c
@@ -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);
}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index b91b373..f7303a6 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -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;
diff --git a/sound/firewire/dice/dice.h b/sound/firewire/dice/dice.h
index 3d5ebeb..423cdba 100644
--- a/sound/firewire/dice/dice.h
+++ b/sound/firewire/dice/dice.h
@@ -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);
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index d5b19bc..8f27b67 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -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)
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 968a40a..425db8d 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -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);
diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c
index bb53eb3..f897c98 100644
--- a/sound/firewire/oxfw/oxfw-scs1x.c
+++ b/sound/firewire/oxfw/oxfw-scs1x.c
@@ -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;
diff --git a/sound/hda/Makefile b/sound/hda/Makefile
index 7e999c9..3b9bede 100644
--- a/sound/hda/Makefile
+++ b/sound/hda/Makefile
@@ -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
index 0000000..d7ec862
--- /dev/null
+++ b/sound/hda/hdmi_chmap.c
@@ -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);
diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig
index 2153d31..4a47050 100644
--- a/sound/mips/Kconfig
+++ b/sound/mips/Kconfig
@@ -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
diff --git a/sound/mips/Makefile b/sound/mips/Makefile
index 861ec0a..b977c44 100644
--- a/sound/mips/Makefile
+++ b/sound/mips/Makefile
@@ -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
index 1e30e84..0000000
--- a/sound/mips/au1x00.c
+++ /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);
diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
index 8f6594a..32151d8 100644
--- a/sound/pci/Kconfig
+++ b/sound/pci/Kconfig
@@ -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,
diff --git a/sound/pci/hda/Kconfig b/sound/pci/hda/Kconfig
index e94cfd5..bb02c2d 100644
--- a/sound/pci/hda/Kconfig
+++ b/sound/pci/hda/Kconfig
@@ -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
diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c
index bc2e082..ba7fe9b 100644
--- a/sound/pci/hda/hda_eld.c
+++ b/sound/pci/hda/hda_eld.c
@@ -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);
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index e5240cb..2624cfe 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -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
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index bcbc4ee..eb7da99 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -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
@@ -196,173 +198,6 @@ union audio_infoframe {
};
/*
- * 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
*/
@@ -625,339 +496,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;
}
diff --git a/sound/pci/hda/thinkpad_helper.c b/sound/pci/hda/thinkpad_helper.c
index 0a4ad5f..59ab6ce 100644
--- a/sound/pci/hda/thinkpad_helper.c
+++ b/sound/pci/hda/thinkpad_helper.c
@@ -10,23 +10,10 @@
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)
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 7ea66ee..182d92e 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -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---
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 1f09d95..3fc6358 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -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;
}
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 007cf58..47de8af 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -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);
diff --git a/sound/usb/midi.h b/sound/usb/midi.h
index ad8a321..5e25a3f 100644
--- a/sound/usb/midi.h
+++ b/sound/usb/midi.h
@@ -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);
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index c458d60..5b03296 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -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;
diff --git a/sound/usb/quirks.h b/sound/usb/quirks.h
index 2cd71ed..192ff5c 100644
--- a/sound/usb/quirks.h
+++ b/sound/usb/quirks.h
@@ -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);