aboutsummaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
authorSteve Pfetsch <spfetsch@google.com>2016-07-13 12:19:16 -0700
committerSteve Pfetsch <spfetsch@google.com>2016-07-13 14:27:02 -0700
commite4622e4329245baa0613416e66fdd8cb8d359589 (patch)
treef0c5016186139487cb34a3879540ab1a9c9167ed /sound
parent0308697d86d79b23a54c56a10f0c4a0a72080815 (diff)
parent719694ad9565811a5d92301cfa04ef440f1d38a8 (diff)
Merge AU_LINUX_ANDROID_LA.HB.1.3.9.06.00.01.213.216 into android-msm-marlin-3.18
The following camera commit was reverted in the branch being merged: f3c656d msm: camera: sensor: power_down eeprom if read fails The following sched commits were reverted in the branch being merged: 556f086 Revert "sched: warn/panic upon excessive scheduling latency" ef07180 Revert "sched: add scheduling latency tracking procfs node" ab273d4 sched: eliminate sched_early_detection_duration knob eb7bc52 sched: Remove the sched heavy task frequency guidance feature 49fe8ea sched: eliminate sched_migration_fixup knob 650cf42 sched: eliminate sched_enable_power_aware knob and parameter f212108 sched: eliminate sched_freq_account_wait_time knob 08f5e5b sched: eliminate sched_account_wait_time knob fa70996 sched: don't assume higher capacity means higher power in lb e62daff sched/cputime: Fix steal_account_process_tick() to always return jiffies c8d69b6 sched: Fix crash in sched_init_numa() 71dc6a3 sched: Fix cpu_active_mask/cpu_online_mask race Conflicts: Makefile arch/arm64/kernel/setup.c arch/arm64/mm/mmap.c arch/x86/boot/Makefile arch/x86/mm/mmap.c drivers/block/zram/zcomp.c drivers/block/zram/zcomp_lz4.c drivers/block/zram/zcomp_lzo.c drivers/leds/leds-qpnp-flash.c drivers/md/dm-crypt.c drivers/staging/android/lowmemorykiller.c drivers/video/msm/mdss/mdss_debug.c fs/notify/inotify/inotify_user.c fs/sdcardfs/dentry.c fs/sdcardfs/packagelist.c include/linux/dcache.h include/linux/mm.h init/do_mounts_dm.c mm/kasan/Makefile mm/mmap.c scripts/Makefile.lib sound/core/control_compat.c sound/soc/msm/msm8996.c Change-Id: Id16ec29883f2797af471a6b05d2b95df1f6a185f
Diffstat (limited to 'sound')
-rw-r--r--sound/core/compress_offload.c12
-rw-r--r--sound/core/control_compat.c87
-rw-r--r--sound/core/oss/pcm_oss.c21
-rw-r--r--sound/core/pcm.c15
-rw-r--r--sound/core/pcm_native.c69
-rw-r--r--sound/core/rawmidi.c134
-rw-r--r--sound/core/rawmidi_compat.c53
-rw-r--r--sound/core/seq/oss/seq_oss.c2
-rw-r--r--sound/core/seq/oss/seq_oss_device.h1
-rw-r--r--sound/core/seq/oss/seq_oss_init.c18
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c2
-rw-r--r--sound/core/seq/seq_clientmgr.c3
-rw-r--r--sound/core/seq/seq_fifo.c4
-rw-r--r--sound/core/seq/seq_memory.c19
-rw-r--r--sound/core/seq/seq_ports.c240
-rw-r--r--sound/core/seq/seq_prioq.c4
-rw-r--r--sound/core/seq/seq_queue.c4
-rw-r--r--sound/core/seq/seq_timer.c91
-rw-r--r--sound/core/seq/seq_virmidi.c23
-rw-r--r--sound/core/timer.c158
-rw-r--r--sound/core/timer_compat.c18
-rw-r--r--sound/drivers/dummy.c35
-rw-r--r--sound/firewire/bebob/bebob_stream.c14
-rw-r--r--sound/pci/hda/hda_generic.c87
-rw-r--r--sound/pci/hda/patch_cirrus.c35
-rw-r--r--sound/pci/hda/patch_hdmi.c12
-rw-r--r--sound/pci/hda/patch_realtek.c456
-rw-r--r--sound/pci/intel8x0.c1
-rw-r--r--sound/pci/rme9652/hdsp.c4
-rw-r--r--sound/pci/rme9652/hdspm.c16
-rw-r--r--sound/soc/codecs/rt5645.c2
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c6
-rw-r--r--sound/soc/codecs/wcd9306.c152
-rw-r--r--sound/soc/codecs/wcd9306.h6
-rw-r--r--sound/soc/codecs/wcd9335.c181
-rw-r--r--sound/soc/codecs/wcd9335.h2
-rw-r--r--sound/soc/codecs/wcd_cpe_core.c147
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c8
-rw-r--r--sound/soc/codecs/wm8994.c4
-rw-r--r--sound/soc/codecs/wm_adsp.c8
-rw-r--r--sound/soc/msm/apq8096-auto.c131
-rw-r--r--sound/soc/msm/mdm9607.c246
-rw-r--r--sound/soc/msm/msm-cpe-lsm.c100
-rw-r--r--sound/soc/msm/msm8996.c54
-rw-r--r--sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h2
-rwxr-xr-xsound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c17
-rw-r--r--sound/soc/msm/qdsp6v2/msm-qti-pp-config.c32
-rw-r--r--sound/soc/msm/qdsp6v2/q6adm.c212
-rw-r--r--sound/soc/soc-pcm.c3
-rw-r--r--sound/usb/midi.c1
-rw-r--r--sound/usb/mixer.c6
-rw-r--r--sound/usb/quirks.c25
52 files changed, 2295 insertions, 688 deletions
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index eee289153bd3..927367a9c1d5 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -44,6 +44,13 @@
#include <sound/compress_offload.h>
#include <sound/compress_driver.h>
+/* struct snd_compr_codec_caps overflows the ioctl bit size for some
+ * architectures, so we need to disable the relevant ioctls.
+ */
+#if _IOC_SIZEBITS < 14
+#define COMPR_CODEC_CAPS_OVERFLOW
+#endif
+
/* TODO:
* - add substream support for multiple devices in case of
* SND_DYNAMIC_MINORS is not used
@@ -445,6 +452,7 @@ out:
return retval;
}
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
static int
snd_compr_get_codec_caps(struct snd_compr_stream *stream, unsigned long arg)
{
@@ -468,6 +476,7 @@ out:
kfree(caps);
return retval;
}
+#endif /* !COMPR_CODEC_CAPS_OVERFLOW */
/* revisit this with snd_pcm_preallocate_xxx */
static int snd_compr_allocate_buffer(struct snd_compr_stream *stream,
@@ -848,10 +857,11 @@ static int snd_compress_simple_ioctls(struct file *file,
retval = snd_compr_get_caps(stream, arg);
break;
+#ifndef COMPR_CODEC_CAPS_OVERFLOW
case _IOC_NR(SNDRV_COMPRESS_GET_CODEC_CAPS):
retval = snd_compr_get_codec_caps(stream, arg);
break;
-
+#endif
case _IOC_NR(SNDRV_COMPRESS_TSTAMP):
retval = snd_compr_tstamp(stream, arg);
diff --git a/sound/core/control_compat.c b/sound/core/control_compat.c
index 3f8094ee7867..a56ce9aab596 100644
--- a/sound/core/control_compat.c
+++ b/sound/core/control_compat.c
@@ -174,6 +174,19 @@ struct snd_ctl_elem_value32 {
unsigned char reserved[128];
};
+#ifdef CONFIG_X86_X32
+/* x32 has a different alignment for 64bit values from ia32 */
+struct snd_ctl_elem_value_x32 {
+ struct snd_ctl_elem_id id;
+ unsigned int indirect; /* bit-field causes misalignment */
+ union {
+ s32 integer[128];
+ unsigned char data[512];
+ s64 integer64[64];
+ } value;
+ unsigned char reserved[128];
+};
+#endif /* CONFIG_X86_X32 */
/* get the value type and count of the control */
static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
@@ -223,9 +236,11 @@ static int get_elem_size(int type, int count)
static int copy_ctl_value_from_user(struct snd_card *card,
struct snd_ctl_elem_value *data,
- struct snd_ctl_elem_value32 __user *data32,
+ void __user *userdata,
+ void __user *valuep,
int *typep, int *countp)
{
+ struct snd_ctl_elem_value32 __user *data32 = userdata;
int i, type, size;
int uninitialized_var(count);
unsigned int indirect;
@@ -243,8 +258,9 @@ static int copy_ctl_value_from_user(struct snd_card *card,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
- if (get_user(val, &data32->value.integer[i]))
+ if (get_user(val, &intp[i]))
return -EFAULT;
data->value.integer.value[i] = val;
}
@@ -254,8 +270,7 @@ static int copy_ctl_value_from_user(struct snd_card *card,
dev_err(card->dev, "snd_ioctl32_ctl_elem_value: unknown type %d\n", type);
return -EINVAL;
}
- if (copy_from_user(data->value.bytes.data,
- data32->value.data, size))
+ if (copy_from_user(data->value.bytes.data, valuep, size))
return -EFAULT;
}
@@ -265,7 +280,8 @@ static int copy_ctl_value_from_user(struct snd_card *card,
}
/* restore the value to 32bit */
-static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
+static int copy_ctl_value_to_user(void __user *userdata,
+ void __user *valuep,
struct snd_ctl_elem_value *data,
int type, int count)
{
@@ -274,22 +290,22 @@ static int copy_ctl_value_to_user(struct snd_ctl_elem_value32 __user *data32,
if (type == SNDRV_CTL_ELEM_TYPE_BOOLEAN ||
type == SNDRV_CTL_ELEM_TYPE_INTEGER) {
for (i = 0; i < count; i++) {
+ s32 __user *intp = valuep;
int val;
val = data->value.integer.value[i];
- if (put_user(val, &data32->value.integer[i]))
+ if (put_user(val, &intp[i]))
return -EFAULT;
}
} else {
size = get_elem_size(type, count);
- if (copy_to_user(data32->value.data,
- data->value.bytes.data, size))
+ if (copy_to_user(valuep, data->value.bytes.data, size))
return -EFAULT;
}
return 0;
}
-static int snd_ctl_elem_read_user_compat(struct snd_card *card,
- struct snd_ctl_elem_value32 __user *data32)
+static int ctl_elem_read_user(struct snd_card *card,
+ void __user *userdata, void __user *valuep)
{
struct snd_ctl_elem_value *data;
int err, type, count;
@@ -298,7 +314,8 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) {
+ if ((err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count)) < 0) {
#ifdef CONFIG_HTC_AUDIO_DEBUG
pr_aud_err("%s: copy from user fail\n", __func__);//HTC_AUDIO
#endif
@@ -317,14 +334,15 @@ static int snd_ctl_elem_read_user_compat(struct snd_card *card,
#endif
snd_power_unlock(card);
if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
+ err = copy_ctl_value_to_user(userdata, valuep, data,
+ type, count);
error:
kfree(data);
return err;
}
-static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
- struct snd_ctl_elem_value32 __user *data32)
+static int ctl_elem_write_user(struct snd_ctl_file *file,
+ void __user *userdata, void __user *valuep)
{
struct snd_ctl_elem_value *data;
struct snd_card *card = file->card;
@@ -338,7 +356,8 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
if (data == NULL)
return -ENOMEM;
- if ((err = copy_ctl_value_from_user(card, data, data32, &type, &count)) < 0) {
+ if ((err = copy_ctl_value_from_user(card, data, userdata, valuep,
+ &type, &count)) < 0) {
#ifdef CONFIG_HTC_AUDIO_DEBUG
pr_aud_err("%s: copy from user fail\n", __func__);//HTC_AUDIO
#endif
@@ -357,7 +376,7 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
#endif
snd_power_unlock(card);
if (err >= 0)
- err = copy_ctl_value_to_user(data32, data, type, count);
+ err = copy_ctl_value_to_user(userdata, valuep, data, type, count);
#ifdef CONFIG_HTC_AUDIO_DEBUG
//HTC_AUD_START
else
@@ -369,6 +388,32 @@ static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
return err;
}
+static int snd_ctl_elem_read_user_compat(struct snd_card *card,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_compat(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+
+#ifdef CONFIG_X86_X32
+static int snd_ctl_elem_read_user_x32(struct snd_card *card,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_read_user(card, data32, &data32->value);
+}
+
+static int snd_ctl_elem_write_user_x32(struct snd_ctl_file *file,
+ struct snd_ctl_elem_value_x32 __user *data32)
+{
+ return ctl_elem_write_user(file, data32, &data32->value);
+}
+#endif /* CONFIG_X86_X32 */
+
/* add or replace a user control */
static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
struct snd_ctl_elem_info32 __user *data32,
@@ -427,6 +472,10 @@ enum {
SNDRV_CTL_IOCTL_ELEM_WRITE32 = _IOWR('U', 0x13, struct snd_ctl_elem_value32),
SNDRV_CTL_IOCTL_ELEM_ADD32 = _IOWR('U', 0x17, struct snd_ctl_elem_info32),
SNDRV_CTL_IOCTL_ELEM_REPLACE32 = _IOWR('U', 0x18, struct snd_ctl_elem_info32),
+#ifdef CONFIG_X86_X32
+ SNDRV_CTL_IOCTL_ELEM_READ_X32 = _IOWR('U', 0x12, struct snd_ctl_elem_value_x32),
+ SNDRV_CTL_IOCTL_ELEM_WRITE_X32 = _IOWR('U', 0x13, struct snd_ctl_elem_value_x32),
+#endif /* CONFIG_X86_X32 */
};
static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -465,6 +514,12 @@ static inline long snd_ctl_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_ctl_elem_add_compat(ctl, argp, 0);
case SNDRV_CTL_IOCTL_ELEM_REPLACE32:
return snd_ctl_elem_add_compat(ctl, argp, 1);
+#ifdef CONFIG_X86_X32
+ case SNDRV_CTL_IOCTL_ELEM_READ_X32:
+ return snd_ctl_elem_read_user_x32(ctl->card, argp);
+ case SNDRV_CTL_IOCTL_ELEM_WRITE_X32:
+ return snd_ctl_elem_write_user_x32(ctl, argp);
+#endif /* CONFIG_X86_X32 */
}
down_read(&snd_ioctl_rwsem);
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index ada69d7a8d70..f29f1ce4a455 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -834,7 +834,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
}
-static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
+static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
+ bool trylock)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_params *params, *sparams;
@@ -848,7 +849,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
struct snd_mask sformat_mask;
struct snd_mask mask;
- if (mutex_lock_interruptible(&runtime->oss.params_lock))
+ if (trylock) {
+ if (!(mutex_trylock(&runtime->oss.params_lock)))
+ return -EAGAIN;
+ } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
return -EINTR;
sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL);
params = kmalloc(sizeof(*params), GFP_KERNEL);
@@ -1093,7 +1097,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
if (asubstream == NULL)
asubstream = substream;
if (substream->runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
if (err < 0)
return err;
}
@@ -1133,7 +1137,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
return 0;
runtime = substream->runtime;
if (runtime->oss.params) {
- err = snd_pcm_oss_change_params(substream);
+ err = snd_pcm_oss_change_params(substream, false);
if (err < 0)
return err;
}
@@ -2164,7 +2168,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
runtime = substream->runtime;
if (runtime->oss.params &&
- (err = snd_pcm_oss_change_params(substream)) < 0)
+ (err = snd_pcm_oss_change_params(substream, false)) < 0)
return err;
info.fragsize = runtime->oss.period_bytes;
@@ -2801,7 +2805,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
return -EIO;
if (runtime->oss.params) {
- if ((err = snd_pcm_oss_change_params(substream)) < 0)
+ /* use mutex_trylock() for params_lock for avoiding a deadlock
+ * between mmap_sem and params_lock taken by
+ * copy_from/to_user() in snd_pcm_oss_write/read()
+ */
+ err = snd_pcm_oss_change_params(substream, true);
+ if (err < 0)
return err;
}
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 55d582f53d2f..4ea4e6ff7bb1 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -220,6 +220,10 @@ static char *snd_pcm_format_names[] = {
FORMAT(DSD_U32_BE),
};
+/**
+ * snd_pcm_format_name - Return a name string for the given PCM format
+ * @format: PCM format
+ */
const char *snd_pcm_format_name(snd_pcm_format_t format)
{
if ((__force unsigned int)format >= ARRAY_SIZE(snd_pcm_format_names))
@@ -709,7 +713,6 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
}
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_new_stream);
static int _snd_pcm_new(struct snd_card *card, const char *id, int device,
@@ -1165,6 +1168,15 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
return 0;
}
+/**
+ * snd_pcm_notify - Add/remove the notify list
+ * @notify: PCM notify list
+ * @nfree: 0 = register, 1 = unregister
+ *
+ * This adds the given notifier to the global list so that the callback is
+ * called for each registered PCM devices. This exists only for PCM OSS
+ * emulation, so far.
+ */
int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
{
struct snd_pcm *pcm;
@@ -1187,7 +1199,6 @@ int snd_pcm_notify(struct snd_pcm_notify *notify, int nfree)
mutex_unlock(&register_mutex);
return 0;
}
-
EXPORT_SYMBOL(snd_pcm_notify);
#ifdef CONFIG_PROC_FS
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index 7be573ed5e5c..07af03c4357b 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -78,6 +78,26 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
static DEFINE_RWLOCK(snd_pcm_link_rwlock);
static DECLARE_RWSEM(snd_pcm_link_rwsem);
+/* Writer in rwsem may block readers even during its waiting in queue,
+ * and this may lead to a deadlock when the code path takes read sem
+ * twice (e.g. one in snd_pcm_action_nonatomic() and another in
+ * snd_pcm_stream_lock()). As a (suboptimal) workaround, let writer to
+ * spin until it gets the lock.
+ */
+static inline void down_write_nonblock(struct rw_semaphore *lock)
+{
+ while (!down_write_trylock(lock))
+ cond_resched();
+}
+
+/**
+ * snd_pcm_stream_lock - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream's spinlock or mutex depending on the nonatomic
+ * flag of the given substream. This also takes the global link rw lock
+ * (or rw sem), too, for avoiding the race with linked streams.
+ */
void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
{
if (substream->pcm->nonatomic) {
@@ -90,6 +110,12 @@ void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
+/**
+ * snd_pcm_stream_lock - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This unlocks the PCM stream that has been locked via snd_pcm_stream_lock().
+ */
void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
{
if (substream->pcm->nonatomic) {
@@ -102,6 +128,14 @@ void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
+/**
+ * snd_pcm_stream_lock_irq - Lock the PCM stream
+ * @substream: PCM substream
+ *
+ * This locks the PCM stream like snd_pcm_stream_lock() and disables the local
+ * IRQ (only when nonatomic is false). In nonatomic case, this is identical
+ * as snd_pcm_stream_lock().
+ */
void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
{
if (!substream->pcm->nonatomic)
@@ -110,6 +144,12 @@ void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
+/**
+ * snd_pcm_stream_unlock_irq - Unlock the PCM stream
+ * @substream: PCM substream
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irq().
+ */
void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
{
snd_pcm_stream_unlock(substream);
@@ -128,6 +168,13 @@ unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
}
EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
+/**
+ * snd_pcm_stream_unlock_irqrestore - Unlock the PCM stream
+ * @substream: PCM substream
+ * @flags: irq flags
+ *
+ * This is a counter-part of snd_pcm_stream_lock_irqsave().
+ */
void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
unsigned long flags)
{
@@ -1777,7 +1824,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
res = -ENOMEM;
goto _nolock;
}
- down_write(&snd_pcm_link_rwsem);
+ down_write_nonblock(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock);
if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
substream->runtime->status->state != substream1->runtime->status->state ||
@@ -1824,7 +1871,7 @@ static int snd_pcm_unlink(struct snd_pcm_substream *substream)
struct snd_pcm_substream *s;
int res = 0;
- down_write(&snd_pcm_link_rwsem);
+ down_write_nonblock(&snd_pcm_link_rwsem);
write_lock_irq(&snd_pcm_link_rwlock);
if (!snd_pcm_stream_linked(substream)) {
res = -EALREADY;
@@ -3380,6 +3427,15 @@ static const struct vm_operations_struct snd_pcm_vm_ops_data_fault = {
/*
* mmap the DMA buffer on RAM
*/
+
+/**
+ * snd_pcm_lib_default_mmap - Default PCM data mmap function
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * This is the default mmap handler for PCM data. When mmap pcm_ops is NULL,
+ * this function is invoked implicitly.
+ */
int snd_pcm_lib_default_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
@@ -3415,6 +3471,15 @@ EXPORT_SYMBOL_GPL(snd_pcm_lib_default_mmap);
* mmap the DMA buffer on I/O memory area
*/
#if SNDRV_PCM_INFO_MMAP_IOMEM
+/**
+ * snd_pcm_lib_mmap_iomem - Default PCM data mmap function for I/O mem
+ * @substream: PCM substream
+ * @area: VMA
+ *
+ * When your hardware uses the iomapped pages as the hardware buffer and
+ * wants to mmap it, pass this function as mmap pcm_ops. Note that this
+ * is supposed to work only on limited architectures.
+ */
int snd_pcm_lib_mmap_iomem(struct snd_pcm_substream *substream,
struct vm_area_struct *area)
{
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index e0212ca29b6b..4fac4db79c15 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -952,31 +952,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long result = 0, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
+ spin_lock_irqsave(&runtime->lock, flags);
while (count > 0 && runtime->avail) {
count1 = runtime->buffer_size - runtime->appl_ptr;
if (count1 > count)
count1 = count;
- spin_lock_irqsave(&runtime->lock, flags);
if (count1 > (int)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
+ memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
if (copy_to_user(userbuf + result,
- runtime->buffer + runtime->appl_ptr, count1)) {
+ runtime->buffer + appl_ptr, count1)) {
return result > 0 ? result : -EFAULT;
}
spin_lock_irqsave(&runtime->lock, flags);
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
- spin_unlock_irqrestore(&runtime->lock, flags);
result += count1;
count -= count1;
}
+ spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
@@ -1065,23 +1070,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
/**
- * snd_rawmidi_transmit_peek - copy data from the internal buffer
+ * __snd_rawmidi_transmit_peek - copy data from the internal buffer
* @substream: the rawmidi substream
* @buffer: the buffer pointer
* @count: data size to transfer
*
- * Copies data from the internal output buffer to the given buffer.
- *
- * Call this in the interrupt handler when the midi output is ready,
- * and call snd_rawmidi_transmit_ack() after the transmission is
- * finished.
- *
- * Return: The size of copied data, or a negative error code on failure.
+ * This is a variant of snd_rawmidi_transmit_peek() without spinlock.
*/
-int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
- unsigned long flags;
int result, count1;
struct snd_rawmidi_runtime *runtime = substream->runtime;
@@ -1091,7 +1089,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
return -EINVAL;
}
result = 0;
- spin_lock_irqsave(&runtime->lock, flags);
if (runtime->avail >= runtime->buffer_size) {
/* warning: lowlevel layer MUST trigger down the hardware */
goto __skip;
@@ -1116,25 +1113,47 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
}
}
__skip:
+ return result;
+}
+EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
+
+/**
+ * snd_rawmidi_transmit_peek - copy data from the internal buffer
+ * @substream: the rawmidi substream
+ * @buffer: the buffer pointer
+ * @count: data size to transfer
+ *
+ * Copies data from the internal output buffer to the given buffer.
+ *
+ * Call this in the interrupt handler when the midi output is ready,
+ * and call snd_rawmidi_transmit_ack() after the transmission is
+ * finished.
+ *
+ * Return: The size of copied data, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
+ unsigned char *buffer, int count)
+{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&runtime->lock, flags);
+ result = __snd_rawmidi_transmit_peek(substream, buffer, count);
spin_unlock_irqrestore(&runtime->lock, flags);
return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
/**
- * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * __snd_rawmidi_transmit_ack - acknowledge the transmission
* @substream: the rawmidi substream
* @count: the transferred count
*
- * Advances the hardware pointer for the internal output buffer with
- * the given size and updates the condition.
- * Call after the transmission is finished.
- *
- * Return: The advanced size if successful, or a negative error code on failure.
+ * This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
*/
-int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
{
- unsigned long flags;
struct snd_rawmidi_runtime *runtime = substream->runtime;
if (runtime->buffer == NULL) {
@@ -1142,7 +1161,6 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
"snd_rawmidi_transmit_ack: output is not active!!!\n");
return -EINVAL;
}
- spin_lock_irqsave(&runtime->lock, flags);
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
runtime->hw_ptr += count;
runtime->hw_ptr %= runtime->buffer_size;
@@ -1152,9 +1170,32 @@ int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
if (runtime->drain || snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
}
- spin_unlock_irqrestore(&runtime->lock, flags);
return count;
}
+EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
+
+/**
+ * snd_rawmidi_transmit_ack - acknowledge the transmission
+ * @substream: the rawmidi substream
+ * @count: the transferred count
+ *
+ * Advances the hardware pointer for the internal output buffer with
+ * the given size and updates the condition.
+ * Call after the transmission is finished.
+ *
+ * Return: The advanced size if successful, or a negative error code on failure.
+ */
+int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
+{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&runtime->lock, flags);
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ return result;
+}
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
/**
@@ -1170,12 +1211,22 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
unsigned char *buffer, int count)
{
+ struct snd_rawmidi_runtime *runtime = substream->runtime;
+ int result;
+ unsigned long flags;
+
+ spin_lock_irqsave(&runtime->lock, flags);
if (!substream->opened)
- return -EBADFD;
- count = snd_rawmidi_transmit_peek(substream, buffer, count);
- if (count < 0)
- return count;
- return snd_rawmidi_transmit_ack(substream, count);
+ result = -EBADFD;
+ else {
+ count = __snd_rawmidi_transmit_peek(substream, buffer, count);
+ if (count <= 0)
+ result = count;
+ else
+ result = __snd_rawmidi_transmit_ack(substream, count);
+ }
+ spin_unlock_irqrestore(&runtime->lock, flags);
+ return result;
}
EXPORT_SYMBOL(snd_rawmidi_transmit);
@@ -1187,8 +1238,9 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
unsigned long flags;
long count1, result;
struct snd_rawmidi_runtime *runtime = substream->runtime;
+ unsigned long appl_ptr;
- if (snd_BUG_ON(!kernelbuf && !userbuf))
+ if (!kernelbuf && !userbuf)
return -EINVAL;
if (snd_BUG_ON(!runtime->buffer))
return -EINVAL;
@@ -1207,12 +1259,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
count1 = count;
if (count1 > (long)runtime->avail)
count1 = runtime->avail;
+
+ /* update runtime->appl_ptr before unlocking for userbuf */
+ appl_ptr = runtime->appl_ptr;
+ runtime->appl_ptr += count1;
+ runtime->appl_ptr %= runtime->buffer_size;
+ runtime->avail -= count1;
+
if (kernelbuf)
- memcpy(runtime->buffer + runtime->appl_ptr,
+ memcpy(runtime->buffer + appl_ptr,
kernelbuf + result, count1);
else if (userbuf) {
spin_unlock_irqrestore(&runtime->lock, flags);
- if (copy_from_user(runtime->buffer + runtime->appl_ptr,
+ if (copy_from_user(runtime->buffer + appl_ptr,
userbuf + result, count1)) {
spin_lock_irqsave(&runtime->lock, flags);
result = result > 0 ? result : -EFAULT;
@@ -1220,9 +1279,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
}
spin_lock_irqsave(&runtime->lock, flags);
}
- runtime->appl_ptr += count1;
- runtime->appl_ptr %= runtime->buffer_size;
- runtime->avail -= count1;
result += count1;
count -= count1;
}
diff --git a/sound/core/rawmidi_compat.c b/sound/core/rawmidi_compat.c
index 5268c1f58c25..09a89094dcf7 100644
--- a/sound/core/rawmidi_compat.c
+++ b/sound/core/rawmidi_compat.c
@@ -94,9 +94,58 @@ static int snd_rawmidi_ioctl_status_compat(struct snd_rawmidi_file *rfile,
return 0;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has 64bit timespec and 64bit alignment */
+struct snd_rawmidi_status_x32 {
+ s32 stream;
+ u32 rsvd; /* alignment */
+ struct timespec tstamp;
+ u32 avail;
+ u32 xruns;
+ unsigned char reserved[16];
+} __attribute__((packed));
+
+#define put_timespec(src, dst) copy_to_user(dst, src, sizeof(*dst))
+
+static int snd_rawmidi_ioctl_status_x32(struct snd_rawmidi_file *rfile,
+ struct snd_rawmidi_status_x32 __user *src)
+{
+ int err;
+ struct snd_rawmidi_status status;
+
+ if (rfile->output == NULL)
+ return -EINVAL;
+ if (get_user(status.stream, &src->stream))
+ return -EFAULT;
+
+ switch (status.stream) {
+ case SNDRV_RAWMIDI_STREAM_OUTPUT:
+ err = snd_rawmidi_output_status(rfile->output, &status);
+ break;
+ case SNDRV_RAWMIDI_STREAM_INPUT:
+ err = snd_rawmidi_input_status(rfile->input, &status);
+ break;
+ default:
+ return -EINVAL;
+ }
+ if (err < 0)
+ return err;
+
+ if (put_timespec(&status.tstamp, &src->tstamp) ||
+ put_user(status.avail, &src->avail) ||
+ put_user(status.xruns, &src->xruns))
+ return -EFAULT;
+
+ return 0;
+}
+#endif /* CONFIG_X86_X32 */
+
enum {
SNDRV_RAWMIDI_IOCTL_PARAMS32 = _IOWR('W', 0x10, struct snd_rawmidi_params32),
SNDRV_RAWMIDI_IOCTL_STATUS32 = _IOWR('W', 0x20, struct snd_rawmidi_status32),
+#ifdef CONFIG_X86_X32
+ SNDRV_RAWMIDI_IOCTL_STATUS_X32 = _IOWR('W', 0x20, struct snd_rawmidi_status_x32),
+#endif /* CONFIG_X86_X32 */
};
static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -115,6 +164,10 @@ static long snd_rawmidi_ioctl_compat(struct file *file, unsigned int cmd, unsign
return snd_rawmidi_ioctl_params_compat(rfile, argp);
case SNDRV_RAWMIDI_IOCTL_STATUS32:
return snd_rawmidi_ioctl_status_compat(rfile, argp);
+#ifdef CONFIG_X86_X32
+ case SNDRV_RAWMIDI_IOCTL_STATUS_X32:
+ return snd_rawmidi_ioctl_status_x32(rfile, argp);
+#endif /* CONFIG_X86_X32 */
}
return -ENOIOCTLCMD;
}
diff --git a/sound/core/seq/oss/seq_oss.c b/sound/core/seq/oss/seq_oss.c
index 16d42679e43f..bb032d7593e3 100644
--- a/sound/core/seq/oss/seq_oss.c
+++ b/sound/core/seq/oss/seq_oss.c
@@ -144,8 +144,6 @@ odev_release(struct inode *inode, struct file *file)
if ((dp = file->private_data) == NULL)
return 0;
- snd_seq_oss_drain_write(dp);
-
mutex_lock(&register_mutex);
snd_seq_oss_release(dp);
mutex_unlock(&register_mutex);
diff --git a/sound/core/seq/oss/seq_oss_device.h b/sound/core/seq/oss/seq_oss_device.h
index b43924325249..d7b4d016b547 100644
--- a/sound/core/seq/oss/seq_oss_device.h
+++ b/sound/core/seq/oss/seq_oss_device.h
@@ -127,7 +127,6 @@ int snd_seq_oss_write(struct seq_oss_devinfo *dp, const char __user *buf, int co
unsigned int snd_seq_oss_poll(struct seq_oss_devinfo *dp, struct file *file, poll_table * wait);
void snd_seq_oss_reset(struct seq_oss_devinfo *dp);
-void snd_seq_oss_drain_write(struct seq_oss_devinfo *dp);
/* */
void snd_seq_oss_process_queue(struct seq_oss_devinfo *dp, abstime_t time);
diff --git a/sound/core/seq/oss/seq_oss_init.c b/sound/core/seq/oss/seq_oss_init.c
index b9184d20c39f..acefecb1a47a 100644
--- a/sound/core/seq/oss/seq_oss_init.c
+++ b/sound/core/seq/oss/seq_oss_init.c
@@ -204,7 +204,7 @@ snd_seq_oss_open(struct file *file, int level)
dp->index = i;
if (i >= SNDRV_SEQ_OSS_MAX_CLIENTS) {
- pr_err("ALSA: seq_oss: too many applications\n");
+ pr_debug("ALSA: seq_oss: too many applications\n");
rc = -ENOMEM;
goto _error;
}
@@ -441,22 +441,6 @@ snd_seq_oss_release(struct seq_oss_devinfo *dp)
/*
- * Wait until the queue is empty (if we don't have nonblock)
- */
-void
-snd_seq_oss_drain_write(struct seq_oss_devinfo *dp)
-{
- if (! dp->timer->running)
- return;
- if (is_write_mode(dp->file_mode) && !is_nonblock_mode(dp->file_mode) &&
- dp->writeq) {
- while (snd_seq_oss_writeq_sync(dp->writeq))
- ;
- }
-}
-
-
-/*
* reset sequencer devices
*/
void
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index 701feb71b700..ac3d7d2125e9 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -308,7 +308,7 @@ snd_seq_oss_synth_cleanup(struct seq_oss_devinfo *dp)
struct seq_oss_synth *rec;
struct seq_oss_synthinfo *info;
- if (snd_BUG_ON(dp->max_synthdev >= SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
+ if (snd_BUG_ON(dp->max_synthdev > SNDRV_SEQ_OSS_MAX_SYNTH_DEVS))
return;
for (i = 0; i < dp->max_synthdev; i++) {
info = &dp->synths[i];
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index 225c73152ee9..a4dabd55e397 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
else
down_read(&grp->list_mutex);
list_for_each_entry(subs, &grp->list_head, src_list) {
+ /* both ports ready? */
+ if (atomic_read(&subs->ref_count) != 2)
+ continue;
event->dest = subs->info.dest;
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
/* convert time according to flag with subscription */
diff --git a/sound/core/seq/seq_fifo.c b/sound/core/seq/seq_fifo.c
index 53a403e17c5b..1d5acbe0c08b 100644
--- a/sound/core/seq/seq_fifo.c
+++ b/sound/core/seq/seq_fifo.c
@@ -33,10 +33,8 @@ struct snd_seq_fifo *snd_seq_fifo_new(int poolsize)
struct snd_seq_fifo *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (f == NULL) {
- pr_debug("ALSA: seq: malloc failed for snd_seq_fifo_new() \n");
+ if (!f)
return NULL;
- }
f->pool = snd_seq_pool_new(poolsize);
if (f->pool == NULL) {
diff --git a/sound/core/seq/seq_memory.c b/sound/core/seq/seq_memory.c
index ba8e4a64e13e..c850345c43b5 100644
--- a/sound/core/seq/seq_memory.c
+++ b/sound/core/seq/seq_memory.c
@@ -383,17 +383,20 @@ int snd_seq_pool_init(struct snd_seq_pool *pool)
if (snd_BUG_ON(!pool))
return -EINVAL;
- if (pool->ptr) /* should be atomic? */
- return 0;
- pool->ptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
- if (pool->ptr == NULL) {
- pr_debug("ALSA: seq: malloc for sequencer events failed\n");
+ cellptr = vmalloc(sizeof(struct snd_seq_event_cell) * pool->size);
+ if (!cellptr)
return -ENOMEM;
- }
/* add new cells to the free cell list */
spin_lock_irqsave(&pool->lock, flags);
+ if (pool->ptr) {
+ spin_unlock_irqrestore(&pool->lock, flags);
+ vfree(cellptr);
+ return 0;
+ }
+
+ pool->ptr = cellptr;
pool->free = NULL;
for (cell = 0; cell < pool->size; cell++) {
@@ -463,10 +466,8 @@ struct snd_seq_pool *snd_seq_pool_new(int poolsize)
/* create pool block */
pool = kzalloc(sizeof(*pool), GFP_KERNEL);
- if (pool == NULL) {
- pr_debug("ALSA: seq: malloc failed for pool\n");
+ if (!pool)
return NULL;
- }
spin_lock_init(&pool->lock);
pool->ptr = NULL;
pool->free = NULL;
diff --git a/sound/core/seq/seq_ports.c b/sound/core/seq/seq_ports.c
index 794a341bf0e5..9c1c8d50f593 100644
--- a/sound/core/seq/seq_ports.c
+++ b/sound/core/seq/seq_ports.c
@@ -141,10 +141,8 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
/* create a new port */
new_port = kzalloc(sizeof(*new_port), GFP_KERNEL);
- if (! new_port) {
- pr_debug("ALSA: seq: malloc failed for registering client port\n");
+ if (!new_port)
return NULL; /* failure, out of memory */
- }
/* init port data */
new_port->addr.client = client->number;
new_port->addr.port = -1;
@@ -175,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
}
/* */
-enum group_type {
- SRC_LIST, DEST_LIST
-};
-
static int subscribe_port(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
@@ -205,6 +199,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
return NULL;
}
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack);
+
+static inline struct snd_seq_subscribers *
+get_subscriber(struct list_head *p, bool is_src)
+{
+ if (is_src)
+ return list_entry(p, struct snd_seq_subscribers, src_list);
+ else
+ return list_entry(p, struct snd_seq_subscribers, dest_list);
+}
+
/*
* remove all subscribers on the list
* this is called from port_delete, for each src and dest list.
@@ -212,7 +220,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client_port *port,
struct snd_seq_port_subs_info *grp,
- int grptype)
+ int is_src)
{
struct list_head *p, *n;
@@ -221,15 +229,13 @@ static void clear_subscriber_list(struct snd_seq_client *client,
struct snd_seq_client *c;
struct snd_seq_client_port *aport;
- if (grptype == SRC_LIST) {
- subs = list_entry(p, struct snd_seq_subscribers, src_list);
+ subs = get_subscriber(p, is_src);
+ if (is_src)
aport = get_client_port(&subs->info.dest, &c);
- } else {
- subs = list_entry(p, struct snd_seq_subscribers, dest_list);
+ else
aport = get_client_port(&subs->info.sender, &c);
- }
- list_del(p);
- unsubscribe_port(client, port, grp, &subs->info, 0);
+ delete_and_unsubscribe_port(client, port, subs, is_src, false);
+
if (!aport) {
/* looks like the connected port is being deleted.
* we decrease the counter, and when both ports are deleted
@@ -237,21 +243,14 @@ static void clear_subscriber_list(struct snd_seq_client *client,
*/
if (atomic_dec_and_test(&subs->ref_count))
kfree(subs);
- } else {
- /* ok we got the connected port */
- struct snd_seq_port_subs_info *agrp;
- agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
- down_write(&agrp->list_mutex);
- if (grptype == SRC_LIST)
- list_del(&subs->dest_list);
- else
- list_del(&subs->src_list);
- up_write(&agrp->list_mutex);
- unsubscribe_port(c, aport, agrp, &subs->info, 1);
- kfree(subs);
- snd_seq_port_unlock(aport);
- snd_seq_client_unlock(c);
+ continue;
}
+
+ /* ok we got the connected port */
+ delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
+ kfree(subs);
+ snd_seq_port_unlock(aport);
+ snd_seq_client_unlock(c);
}
}
@@ -264,8 +263,8 @@ static int port_delete(struct snd_seq_client *client,
snd_use_lock_sync(&port->use_lock);
/* clear subscribers info */
- clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
- clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
+ clear_subscriber_list(client, port, &port->c_src, true);
+ clear_subscriber_list(client, port, &port->c_dest, false);
if (port->private_free)
port->private_free(port->private_data);
@@ -484,85 +483,123 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
return 0;
}
-
-/* connect two ports */
-int snd_seq_port_connect(struct snd_seq_client *connector,
- struct snd_seq_client *src_client,
- struct snd_seq_client_port *src_port,
- struct snd_seq_client *dest_client,
- struct snd_seq_client_port *dest_port,
- struct snd_seq_port_subscribe *info)
+static int check_and_subscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool exclusive, bool ack)
{
- struct snd_seq_port_subs_info *src = &src_port->c_src;
- struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
- struct snd_seq_subscribers *subs, *s;
- int err, src_called = 0;
- unsigned long flags;
- int exclusive;
-
- subs = kzalloc(sizeof(*subs), GFP_KERNEL);
- if (! subs)
- return -ENOMEM;
-
- subs->info = *info;
- atomic_set(&subs->ref_count, 2);
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *p;
+ struct snd_seq_subscribers *s;
+ int err;
- down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
-
- exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
+ grp = is_src ? &port->c_src : &port->c_dest;
err = -EBUSY;
+ down_write(&grp->list_mutex);
if (exclusive) {
- if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
+ if (!list_empty(&grp->list_head))
goto __error;
} else {
- if (src->exclusive || dest->exclusive)
+ if (grp->exclusive)
goto __error;
/* check whether already exists */
- list_for_each_entry(s, &src->list_head, src_list) {
- if (match_subs_info(info, &s->info))
- goto __error;
- }
- list_for_each_entry(s, &dest->list_head, dest_list) {
- if (match_subs_info(info, &s->info))
+ list_for_each(p, &grp->list_head) {
+ s = get_subscriber(p, is_src);
+ if (match_subs_info(&subs->info, &s->info))
goto __error;
}
}
- if ((err = subscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number)) < 0)
- goto __error;
- src_called = 1;
-
- if ((err = subscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number)) < 0)
+ err = subscribe_port(client, port, grp, &subs->info, ack);
+ if (err < 0) {
+ grp->exclusive = 0;
goto __error;
+ }
/* add to list */
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no other lock yet
- list_add_tail(&subs->src_list, &src->list_head);
- list_add_tail(&subs->dest_list, &dest->list_head);
- // write_unlock(&dest->list_lock); // no other lock yet
- write_unlock_irqrestore(&src->list_lock, flags);
+ write_lock_irq(&grp->list_lock);
+ if (is_src)
+ list_add_tail(&subs->src_list, &grp->list_head);
+ else
+ list_add_tail(&subs->dest_list, &grp->list_head);
+ grp->exclusive = exclusive;
+ atomic_inc(&subs->ref_count);
+ write_unlock_irq(&grp->list_lock);
+ err = 0;
- src->exclusive = dest->exclusive = exclusive;
+ __error:
+ up_write(&grp->list_mutex);
+ return err;
+}
+
+static void delete_and_unsubscribe_port(struct snd_seq_client *client,
+ struct snd_seq_client_port *port,
+ struct snd_seq_subscribers *subs,
+ bool is_src, bool ack)
+{
+ struct snd_seq_port_subs_info *grp;
+ struct list_head *list;
+ bool empty;
+
+ grp = is_src ? &port->c_src : &port->c_dest;
+ list = is_src ? &subs->src_list : &subs->dest_list;
+ down_write(&grp->list_mutex);
+ write_lock_irq(&grp->list_lock);
+ empty = list_empty(list);
+ if (!empty)
+ list_del_init(list);
+ grp->exclusive = 0;
+ write_unlock_irq(&grp->list_lock);
+ up_write(&grp->list_mutex);
+
+ if (!empty)
+ unsubscribe_port(client, port, grp, &subs->info, ack);
+}
+
+/* connect two ports */
+int snd_seq_port_connect(struct snd_seq_client *connector,
+ struct snd_seq_client *src_client,
+ struct snd_seq_client_port *src_port,
+ struct snd_seq_client *dest_client,
+ struct snd_seq_client_port *dest_port,
+ struct snd_seq_port_subscribe *info)
+{
+ struct snd_seq_subscribers *subs;
+ bool exclusive;
+ int err;
+
+ subs = kzalloc(sizeof(*subs), GFP_KERNEL);
+ if (!subs)
+ return -ENOMEM;
+
+ subs->info = *info;
+ atomic_set(&subs->ref_count, 0);
+ INIT_LIST_HEAD(&subs->src_list);
+ INIT_LIST_HEAD(&subs->dest_list);
+
+ exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
+
+ err = check_and_subscribe_port(src_client, src_port, subs, true,
+ exclusive,
+ connector->number != src_client->number);
+ if (err < 0)
+ goto error;
+ err = check_and_subscribe_port(dest_client, dest_port, subs, false,
+ exclusive,
+ connector->number != dest_client->number);
+ if (err < 0)
+ goto error_dest;
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return 0;
- __error:
- if (src_called)
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
+ error_dest:
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ error:
kfree(subs);
- up_write(&dest->list_mutex);
- up_write(&src->list_mutex);
return err;
}
-
/* remove the connection */
int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_client *src_client,
@@ -572,37 +609,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
struct snd_seq_port_subscribe *info)
{
struct snd_seq_port_subs_info *src = &src_port->c_src;
- struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
struct snd_seq_subscribers *subs;
int err = -ENOENT;
- unsigned long flags;
down_write(&src->list_mutex);
- down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
-
/* look for the connection */
list_for_each_entry(subs, &src->list_head, src_list) {
if (match_subs_info(info, &subs->info)) {
- write_lock_irqsave(&src->list_lock, flags);
- // write_lock(&dest->list_lock); // no lock yet
- list_del(&subs->src_list);
- list_del(&subs->dest_list);
- // write_unlock(&dest->list_lock);
- write_unlock_irqrestore(&src->list_lock, flags);
- src->exclusive = dest->exclusive = 0;
- unsubscribe_port(src_client, src_port, src, info,
- connector->number != src_client->number);
- unsubscribe_port(dest_client, dest_port, dest, info,
- connector->number != dest_client->number);
- kfree(subs);
+ atomic_dec(&subs->ref_count); /* mark as not ready */
err = 0;
break;
}
}
-
- up_write(&dest->list_mutex);
up_write(&src->list_mutex);
- return err;
+ if (err < 0)
+ return err;
+
+ delete_and_unsubscribe_port(src_client, src_port, subs, true,
+ connector->number != src_client->number);
+ delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
+ connector->number != dest_client->number);
+ kfree(subs);
+ return 0;
}
diff --git a/sound/core/seq/seq_prioq.c b/sound/core/seq/seq_prioq.c
index 021b02bc9330..bc1c8488fc2a 100644
--- a/sound/core/seq/seq_prioq.c
+++ b/sound/core/seq/seq_prioq.c
@@ -59,10 +59,8 @@ struct snd_seq_prioq *snd_seq_prioq_new(void)
struct snd_seq_prioq *f;
f = kzalloc(sizeof(*f), GFP_KERNEL);
- if (f == NULL) {
- pr_debug("ALSA: seq: malloc failed for snd_seq_prioq_new()\n");
+ if (!f)
return NULL;
- }
spin_lock_init(&f->lock);
f->head = NULL;
diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c
index aad4878cee55..a0cda38205b9 100644
--- a/sound/core/seq/seq_queue.c
+++ b/sound/core/seq/seq_queue.c
@@ -111,10 +111,8 @@ static struct snd_seq_queue *queue_new(int owner, int locked)
struct snd_seq_queue *q;
q = kzalloc(sizeof(*q), GFP_KERNEL);
- if (q == NULL) {
- pr_debug("ALSA: seq: malloc failed for snd_seq_queue_new()\n");
+ if (!q)
return NULL;
- }
spin_lock_init(&q->owner_lock);
spin_lock_init(&q->check_lock);
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index e73605393eee..a2468f1101d1 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -56,10 +56,8 @@ struct snd_seq_timer *snd_seq_timer_new(void)
struct snd_seq_timer *tmr;
tmr = kzalloc(sizeof(*tmr), GFP_KERNEL);
- if (tmr == NULL) {
- pr_debug("ALSA: seq: malloc failed for snd_seq_timer_new() \n");
+ if (!tmr)
return NULL;
- }
spin_lock_init(&tmr->lock);
/* reset setup to defaults */
@@ -92,6 +90,9 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
{
+ unsigned long flags;
+
+ spin_lock_irqsave(&tmr->lock, flags);
/* setup defaults */
tmr->ppq = 96; /* 96 PPQ */
tmr->tempo = 500000; /* 120 BPM */
@@ -107,21 +108,25 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
tmr->preferred_resolution = seq_default_timer_resolution;
tmr->skew = tmr->skew_base = SKEW_BASE;
+ spin_unlock_irqrestore(&tmr->lock, flags);
}
-void snd_seq_timer_reset(struct snd_seq_timer * tmr)
+static void seq_timer_reset(struct snd_seq_timer *tmr)
{
- unsigned long flags;
-
- spin_lock_irqsave(&tmr->lock, flags);
-
/* reset time & songposition */
tmr->cur_time.tv_sec = 0;
tmr->cur_time.tv_nsec = 0;
tmr->tick.cur_tick = 0;
tmr->tick.fraction = 0;
+}
+
+void snd_seq_timer_reset(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&tmr->lock, flags);
+ seq_timer_reset(tmr);
spin_unlock_irqrestore(&tmr->lock, flags);
}
@@ -140,8 +145,11 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
tmr = q->timer;
if (tmr == NULL)
return;
- if (!tmr->running)
+ spin_lock_irqsave(&tmr->lock, flags);
+ if (!tmr->running) {
+ spin_unlock_irqrestore(&tmr->lock, flags);
return;
+ }
resolution *= ticks;
if (tmr->skew != tmr->skew_base) {
@@ -150,8 +158,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
(((resolution & 0xffff) * tmr->skew) >> 16);
}
- spin_lock_irqsave(&tmr->lock, flags);
-
/* update timer */
snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
@@ -298,26 +304,30 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
t->callback = snd_seq_timer_interrupt;
t->callback_data = q;
t->flags |= SNDRV_TIMER_IFLG_AUTO;
+ spin_lock_irq(&tmr->lock);
tmr->timeri = t;
+ spin_unlock_irq(&tmr->lock);
return 0;
}
int snd_seq_timer_close(struct snd_seq_queue *q)
{
struct snd_seq_timer *tmr;
+ struct snd_timer_instance *t;
tmr = q->timer;
if (snd_BUG_ON(!tmr))
return -EINVAL;
- if (tmr->timeri) {
- snd_timer_stop(tmr->timeri);
- snd_timer_close(tmr->timeri);
- tmr->timeri = NULL;
- }
+ spin_lock_irq(&tmr->lock);
+ t = tmr->timeri;
+ tmr->timeri = NULL;
+ spin_unlock_irq(&tmr->lock);
+ if (t)
+ snd_timer_close(t);
return 0;
}
-int snd_seq_timer_stop(struct snd_seq_timer * tmr)
+static int seq_timer_stop(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
@@ -328,6 +338,17 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr)
return 0;
}
+int snd_seq_timer_stop(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_stop(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
static int initialize_timer(struct snd_seq_timer *tmr)
{
struct snd_timer *t;
@@ -360,13 +381,13 @@ static int initialize_timer(struct snd_seq_timer *tmr)
return 0;
}
-int snd_seq_timer_start(struct snd_seq_timer * tmr)
+static int seq_timer_start(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
- snd_seq_timer_stop(tmr);
- snd_seq_timer_reset(tmr);
+ seq_timer_stop(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
snd_timer_start(tmr->timeri, tmr->ticks);
@@ -375,14 +396,25 @@ int snd_seq_timer_start(struct snd_seq_timer * tmr)
return 0;
}
-int snd_seq_timer_continue(struct snd_seq_timer * tmr)
+int snd_seq_timer_start(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_start(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
+static int seq_timer_continue(struct snd_seq_timer *tmr)
{
if (! tmr->timeri)
return -EINVAL;
if (tmr->running)
return -EBUSY;
if (! tmr->initialized) {
- snd_seq_timer_reset(tmr);
+ seq_timer_reset(tmr);
if (initialize_timer(tmr) < 0)
return -EINVAL;
}
@@ -392,11 +424,24 @@ int snd_seq_timer_continue(struct snd_seq_timer * tmr)
return 0;
}
+int snd_seq_timer_continue(struct snd_seq_timer *tmr)
+{
+ unsigned long flags;
+ int err;
+
+ spin_lock_irqsave(&tmr->lock, flags);
+ err = seq_timer_continue(tmr);
+ spin_unlock_irqrestore(&tmr->lock, flags);
+ return err;
+}
+
/* return current 'real' time. use timeofday() to get better granularity. */
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
{
snd_seq_real_time_t cur_time;
+ unsigned long flags;
+ spin_lock_irqsave(&tmr->lock, flags);
cur_time = tmr->cur_time;
if (tmr->running) {
struct timeval tm;
@@ -412,7 +457,7 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
}
snd_seq_sanity_real_time(&cur_time);
}
-
+ spin_unlock_irqrestore(&tmr->lock, flags);
return cur_time;
}
diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c
index 56e0f4cd3f82..81134e067184 100644
--- a/sound/core/seq/seq_virmidi.c
+++ b/sound/core/seq/seq_virmidi.c
@@ -155,21 +155,26 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
struct snd_virmidi *vmidi = substream->runtime->private_data;
int count, res;
unsigned char buf[32], *pbuf;
+ unsigned long flags;
if (up) {
vmidi->trigger = 1;
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
- snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
- return; /* ignored */
+ while (snd_rawmidi_transmit(substream, buf,
+ sizeof(buf)) > 0) {
+ /* ignored */
+ }
+ return;
}
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
return;
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
}
+ spin_lock_irqsave(&substream->runtime->lock, flags);
while (1) {
- count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
+ count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
if (count <= 0)
break;
pbuf = buf;
@@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
snd_midi_event_reset_encode(vmidi->parser);
continue;
}
- snd_rawmidi_transmit_ack(substream, res);
+ __snd_rawmidi_transmit_ack(substream, res);
pbuf += res;
count -= res;
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
- return;
+ goto out;
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
}
}
}
+ out:
+ spin_unlock_irqrestore(&substream->runtime->lock, flags);
} else {
vmidi->trigger = 0;
}
@@ -254,9 +261,13 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
*/
static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
{
+ struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
struct snd_virmidi *vmidi = substream->runtime->private_data;
- snd_midi_event_free(vmidi->parser);
+
+ write_lock_irq(&rdev->filelist_lock);
list_del(&vmidi->list);
+ write_unlock_irq(&rdev->filelist_lock);
+ snd_midi_event_free(vmidi->parser);
substream->runtime->private_data = NULL;
kfree(vmidi);
return 0;
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 777a45e08e53..4927a3c88340 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -65,6 +65,7 @@ struct snd_timer_user {
int qtail;
int qused;
int queue_size;
+ bool disconnected;
struct snd_timer_read *queue;
struct snd_timer_tread *tqueue;
spinlock_t qlock;
@@ -215,11 +216,13 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
slave->slave_id == master->slave_id) {
list_move_tail(&slave->open_list, &master->slave_list_head);
spin_lock_irq(&slave_active_lock);
+ spin_lock(&master->timer->lock);
slave->master = master;
slave->timer = master->timer;
if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
list_add_tail(&slave->active_list,
&master->slave_active_head);
+ spin_unlock(&master->timer->lock);
spin_unlock_irq(&slave_active_lock);
}
}
@@ -288,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
mutex_unlock(&register_mutex);
return -ENOMEM;
}
+ /* take a card refcount for safe disconnection */
+ if (timer->card)
+ get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
if (list_empty(&timer->open_list_head) && timer->hw.open)
@@ -299,8 +305,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
return 0;
}
-static int _snd_timer_stop(struct snd_timer_instance *timeri,
- int keep_flag, int event);
+static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
/*
* close a timer instance
@@ -342,19 +347,25 @@ int snd_timer_close(struct snd_timer_instance *timeri)
spin_unlock_irq(&timer->lock);
mutex_lock(&register_mutex);
list_del(&timeri->open_list);
- if (timer && list_empty(&timer->open_list_head) &&
+ 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);
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
open_list) {
- spin_lock_irq(&slave_active_lock);
- _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);
list_move_tail(&slave->open_list, &snd_timer_slave_list);
slave->master = NULL;
slave->timer = NULL;
- spin_unlock_irq(&slave_active_lock);
+ list_del_init(&slave->ack_list);
+ list_del_init(&slave->active_list);
}
+ 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);
}
out:
@@ -411,7 +422,7 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
spin_lock_irqsave(&timer->lock, flags);
list_for_each_entry(ts, &ti->slave_active_head, active_list)
if (ts->ccallback)
- ts->ccallback(ti, event + 100, &tstamp, resolution);
+ ts->ccallback(ts, event + 100, &tstamp, resolution);
spin_unlock_irqrestore(&timer->lock, flags);
}
@@ -440,10 +451,17 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
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->master)
+ if (timeri->master && timeri->timer) {
+ spin_lock(&timeri->timer->lock);
list_add_tail(&timeri->active_list,
&timeri->master->slave_active_head);
+ spin_unlock(&timeri->timer->lock);
+ }
spin_unlock_irqrestore(&slave_active_lock, flags);
return 1; /* delayed start */
}
@@ -461,23 +479,32 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
return -EINVAL;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
result = snd_timer_start_slave(timeri);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+ 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);
- snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
+ if (result >= 0)
+ snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
return result;
}
-static int _snd_timer_stop(struct snd_timer_instance * timeri,
- int keep_flag, int event)
+static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
{
struct snd_timer *timer;
unsigned long flags;
@@ -486,19 +513,36 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
return -ENXIO;
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
- if (!keep_flag) {
- spin_lock_irqsave(&slave_active_lock, flags);
- timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+ 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;
+ }
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 ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
!(--timer->running)) {
timer->hw.stop(timer);
@@ -511,9 +555,7 @@ static int _snd_timer_stop(struct snd_timer_instance * timeri,
}
}
}
- if (!keep_flag)
- timeri->flags &=
- ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
spin_unlock_irqrestore(&timer->lock, flags);
__end:
if (event != SNDRV_TIMER_EVENT_RESOLUTION)
@@ -532,7 +574,7 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
unsigned long flags;
int err;
- err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);
+ err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
if (err < 0)
return err;
timer = timeri->timer;
@@ -561,11 +603,18 @@ int snd_timer_continue(struct snd_timer_instance *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;
@@ -576,7 +625,7 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
*/
int snd_timer_pause(struct snd_timer_instance * timeri)
{
- return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);
+ return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
}
/*
@@ -624,6 +673,9 @@ static void snd_timer_tasklet(unsigned long arg)
unsigned long resolution, ticks;
unsigned long flags;
+ if (timer->card && timer->card->shutdown)
+ return;
+
spin_lock_irqsave(&timer->lock, flags);
/* now process all callbacks */
while (!list_empty(&timer->sack_list_head)) {
@@ -664,6 +716,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
if (timer == NULL)
return;
+ if (timer->card && timer->card->shutdown)
+ return;
+
spin_lock_irqsave(&timer->lock, flags);
/* remember the current resolution */
@@ -693,8 +748,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
ti->cticks = ti->ticks;
} else {
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
- if (--timer->running)
- list_del(&ti->active_list);
+ --timer->running;
+ list_del_init(&ti->active_list);
}
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
@@ -876,11 +931,28 @@ static int snd_timer_dev_register(struct snd_device *dev)
return 0;
}
+/* just for reference in snd_timer_dev_disconnect() below */
+static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
+ int event, struct timespec *tstamp,
+ unsigned long resolution);
+
static int snd_timer_dev_disconnect(struct snd_device *device)
{
struct snd_timer *timer = device->device_data;
+ struct snd_timer_instance *ti;
+
mutex_lock(&register_mutex);
list_del_init(&timer->device_list);
+ /* wake up pending sleepers */
+ list_for_each_entry(ti, &timer->open_list_head, open_list) {
+ /* FIXME: better to have a ti.disconnect() op */
+ if (ti->ccallback == snd_timer_user_ccallback) {
+ struct snd_timer_user *tu = ti->callback_data;
+
+ tu->disconnected = true;
+ wake_up(&tu->qchange_sleep);
+ }
+ }
mutex_unlock(&register_mutex);
return 0;
}
@@ -891,6 +963,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
unsigned long resolution = 0;
struct snd_timer_instance *ti, *ts;
+ if (timer->card && timer->card->shutdown)
+ return;
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
return;
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
@@ -1051,6 +1125,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
mutex_lock(&register_mutex);
list_for_each_entry(timer, &snd_timer_list, device_list) {
+ if (timer->card && timer->card->shutdown)
+ continue;
switch (timer->tmr_class) {
case SNDRV_TIMER_CLASS_GLOBAL:
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
@@ -1846,6 +1922,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
{
struct snd_timer_user *tu;
long result = 0, unit;
+ int qhead;
int err = 0;
tu = file->private_data;
@@ -1857,7 +1934,7 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
err = -EAGAIN;
- break;
+ goto _error;
}
set_current_state(TASK_INTERRUPTIBLE);
@@ -1870,40 +1947,39 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
remove_wait_queue(&tu->qchange_sleep, &wait);
+ if (tu->disconnected) {
+ err = -ENODEV;
+ goto _error;
+ }
if (signal_pending(current)) {
err = -ERESTARTSYS;
- break;
+ goto _error;
}
}
+ qhead = tu->qhead++;
+ tu->qhead %= tu->queue_size;
spin_unlock_irq(&tu->qlock);
- if (err < 0)
- goto _error;
if (tu->tread) {
- if (copy_to_user(buffer, &tu->tqueue[tu->qhead++],
- sizeof(struct snd_timer_tread))) {
+ if (copy_to_user(buffer, &tu->tqueue[qhead],
+ sizeof(struct snd_timer_tread)))
err = -EFAULT;
- goto _error;
- }
} else {
- if (copy_to_user(buffer, &tu->queue[tu->qhead++],
- sizeof(struct snd_timer_read))) {
+ if (copy_to_user(buffer, &tu->queue[qhead],
+ sizeof(struct snd_timer_read)))
err = -EFAULT;
- goto _error;
- }
}
- tu->qhead %= tu->queue_size;
-
- result += unit;
- buffer += unit;
-
spin_lock_irq(&tu->qlock);
tu->qused--;
+ if (err < 0)
+ goto _error;
+ result += unit;
+ buffer += unit;
}
- spin_unlock_irq(&tu->qlock);
_error:
+ spin_unlock_irq(&tu->qlock);
return result > 0 ? result : err;
}
@@ -1919,6 +1995,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
mask = 0;
if (tu->qused)
mask |= POLLIN | POLLRDNORM;
+ if (tu->disconnected)
+ mask |= POLLERR;
return mask;
}
diff --git a/sound/core/timer_compat.c b/sound/core/timer_compat.c
index e05802ae6e1b..2e908225d754 100644
--- a/sound/core/timer_compat.c
+++ b/sound/core/timer_compat.c
@@ -70,13 +70,14 @@ static int snd_timer_user_status_compat(struct file *file,
struct snd_timer_status32 __user *_status)
{
struct snd_timer_user *tu;
- struct snd_timer_status status;
+ struct snd_timer_status32 status;
tu = file->private_data;
if (snd_BUG_ON(!tu->timeri))
return -ENXIO;
memset(&status, 0, sizeof(status));
- status.tstamp = tu->tstamp;
+ status.tstamp.tv_sec = tu->tstamp.tv_sec;
+ status.tstamp.tv_nsec = tu->tstamp.tv_nsec;
status.resolution = snd_timer_resolution(tu->timeri);
status.lost = tu->timeri->lost;
status.overrun = tu->overrun;
@@ -88,12 +89,21 @@ static int snd_timer_user_status_compat(struct file *file,
return 0;
}
+#ifdef CONFIG_X86_X32
+/* X32 ABI has the same struct as x86-64 */
+#define snd_timer_user_status_x32(file, s) \
+ snd_timer_user_status(file, s)
+#endif /* CONFIG_X86_X32 */
+
/*
*/
enum {
SNDRV_TIMER_IOCTL_INFO32 = _IOR('T', 0x11, struct snd_timer_info32),
SNDRV_TIMER_IOCTL_STATUS32 = _IOW('T', 0x14, struct snd_timer_status32),
+#ifdef CONFIG_X86_X32
+ SNDRV_TIMER_IOCTL_STATUS_X32 = _IOW('T', 0x14, struct snd_timer_status),
+#endif /* CONFIG_X86_X32 */
};
static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, unsigned long arg)
@@ -122,6 +132,10 @@ static long snd_timer_user_ioctl_compat(struct file *file, unsigned int cmd, uns
return snd_timer_user_info_compat(file, argp);
case SNDRV_TIMER_IOCTL_STATUS32:
return snd_timer_user_status_compat(file, argp);
+#ifdef CONFIG_X86_X32
+ case SNDRV_TIMER_IOCTL_STATUS_X32:
+ return snd_timer_user_status_x32(file, argp);
+#endif /* CONFIG_X86_X32 */
}
return -ENOIOCTLCMD;
}
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index fab90bd2bd51..387bb8f603ac 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -109,6 +109,9 @@ struct dummy_timer_ops {
snd_pcm_uframes_t (*pointer)(struct snd_pcm_substream *);
};
+#define get_dummy_ops(substream) \
+ (*(const struct dummy_timer_ops **)(substream)->runtime->private_data)
+
struct dummy_model {
const char *name;
int (*playback_constraints)(struct snd_pcm_runtime *runtime);
@@ -137,7 +140,6 @@ struct snd_dummy {
int iobox;
struct snd_kcontrol *cd_volume_ctl;
struct snd_kcontrol *cd_switch_ctl;
- const struct dummy_timer_ops *timer_ops;
};
/*
@@ -231,6 +233,8 @@ struct dummy_model *dummy_models[] = {
*/
struct dummy_systimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
spinlock_t lock;
struct timer_list timer;
unsigned long base_time;
@@ -368,6 +372,8 @@ static struct dummy_timer_ops dummy_systimer_ops = {
*/
struct dummy_hrtimer_pcm {
+ /* ops must be the first item */
+ const struct dummy_timer_ops *timer_ops;
ktime_t base_time;
ktime_t period_time;
atomic_t running;
@@ -494,31 +500,25 @@ static struct dummy_timer_ops dummy_hrtimer_ops = {
static int dummy_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
- return dummy->timer_ops->start(substream);
+ return get_dummy_ops(substream)->start(substream);
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
- return dummy->timer_ops->stop(substream);
+ return get_dummy_ops(substream)->stop(substream);
}
return -EINVAL;
}
static int dummy_pcm_prepare(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->prepare(substream);
+ return get_dummy_ops(substream)->prepare(substream);
}
static snd_pcm_uframes_t dummy_pcm_pointer(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
-
- return dummy->timer_ops->pointer(substream);
+ return get_dummy_ops(substream)->pointer(substream);
}
static struct snd_pcm_hardware dummy_pcm_hardware = {
@@ -564,17 +564,19 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
struct dummy_model *model = dummy->model;
struct snd_pcm_runtime *runtime = substream->runtime;
+ const struct dummy_timer_ops *ops;
int err;
- dummy->timer_ops = &dummy_systimer_ops;
+ ops = &dummy_systimer_ops;
#ifdef CONFIG_HIGH_RES_TIMERS
if (hrtimer)
- dummy->timer_ops = &dummy_hrtimer_ops;
+ ops = &dummy_hrtimer_ops;
#endif
- err = dummy->timer_ops->create(substream);
+ err = ops->create(substream);
if (err < 0)
return err;
+ get_dummy_ops(substream) = ops;
runtime->hw = dummy->pcm_hw;
if (substream->pcm->device & 1) {
@@ -596,7 +598,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
err = model->capture_constraints(substream->runtime);
}
if (err < 0) {
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return err;
}
return 0;
@@ -604,8 +606,7 @@ static int dummy_pcm_open(struct snd_pcm_substream *substream)
static int dummy_pcm_close(struct snd_pcm_substream *substream)
{
- struct snd_dummy *dummy = snd_pcm_substream_chip(substream);
- dummy->timer_ops->free(substream);
+ get_dummy_ops(substream)->free(substream);
return 0;
}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 1aab0a32870c..c05b0f6c1bc2 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -47,14 +47,16 @@ static const unsigned int bridgeco_freq_table[] = {
[6] = 0x07,
};
-static unsigned int
-get_formation_index(unsigned int rate)
+static int
+get_formation_index(unsigned int rate, unsigned int *index)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(snd_bebob_rate_table); i++) {
- if (snd_bebob_rate_table[i] == rate)
- return i;
+ if (snd_bebob_rate_table[i] == rate) {
+ *index = i;
+ return 0;
+ }
}
return -EINVAL;
}
@@ -367,7 +369,9 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
goto end;
/* confirm params for both streams */
- index = get_formation_index(rate);
+ err = get_formation_index(rate, &index);
+ if (err < 0)
+ goto end;
pcm_channels = bebob->tx_stream_formations[index].pcm;
midi_channels = bebob->tx_stream_formations[index].midi;
amdtp_stream_set_parameters(&bebob->tx_stream,
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 6c6e35aba989..e5d39ed42e85 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -705,9 +705,6 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
unsigned int caps;
unsigned int mask, val;
- if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
- return;
-
caps = query_amp_caps(codec, nid, dir);
val = get_amp_val_to_activate(codec, nid, dir, caps, enable);
mask = get_amp_mask_to_modify(codec, nid, dir, idx_to_check, caps);
@@ -718,12 +715,22 @@ static void activate_amp(struct hda_codec *codec, hda_nid_t nid, int dir,
update_amp(codec, nid, dir, idx, mask, val);
}
+static void check_and_activate_amp(struct hda_codec *codec, hda_nid_t nid,
+ int dir, int idx, int idx_to_check,
+ bool enable)
+{
+ /* check whether the given amp is still used by others */
+ if (!enable && is_active_nid(codec, nid, dir, idx_to_check))
+ return;
+ activate_amp(codec, nid, dir, idx, idx_to_check, enable);
+}
+
static void activate_amp_out(struct hda_codec *codec, struct nid_path *path,
int i, bool enable)
{
hda_nid_t nid = path->path[i];
init_amp(codec, nid, HDA_OUTPUT, 0);
- activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
+ check_and_activate_amp(codec, nid, HDA_OUTPUT, 0, 0, enable);
}
static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
@@ -751,9 +758,16 @@ static void activate_amp_in(struct hda_codec *codec, struct nid_path *path,
* when aa-mixer is available, we need to enable the path as well
*/
for (n = 0; n < nums; n++) {
- if (n != idx && (!add_aamix || conn[n] != spec->mixer_merge_nid))
- continue;
- activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
+ if (n != idx) {
+ if (conn[n] != spec->mixer_merge_nid)
+ continue;
+ /* when aamix is disabled, force to off */
+ if (!add_aamix) {
+ activate_amp(codec, nid, HDA_INPUT, n, n, false);
+ continue;
+ }
+ }
+ check_and_activate_amp(codec, nid, HDA_INPUT, n, idx, enable);
}
}
@@ -1473,6 +1487,12 @@ static bool map_singles(struct hda_codec *codec, int outs,
return found;
}
+static inline bool has_aamix_out_paths(struct hda_gen_spec *spec)
+{
+ return spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
+ spec->aamix_out_paths[2];
+}
+
/* create a new path including aamix if available, and return its index */
static int check_aamix_out_path(struct hda_codec *codec, int path_idx)
{
@@ -2315,25 +2335,51 @@ static void update_aamix_paths(struct hda_codec *codec, bool do_mix,
}
}
+/* re-initialize the output paths; only called from loopback_mixing_put() */
+static void update_output_paths(struct hda_codec *codec, int num_outs,
+ const int *paths)
+{
+ struct hda_gen_spec *spec = codec->spec;
+ struct nid_path *path;
+ int i;
+
+ for (i = 0; i < num_outs; i++) {
+ path = snd_hda_get_path_from_idx(codec, paths[i]);
+ if (path)
+ snd_hda_activate_path(codec, path, path->active,
+ spec->aamix_mode);
+ }
+}
+
static int loopback_mixing_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec;
+ const struct auto_pin_cfg *cfg = &spec->autocfg;
unsigned int val = ucontrol->value.enumerated.item[0];
if (val == spec->aamix_mode)
return 0;
spec->aamix_mode = val;
- update_aamix_paths(codec, val, spec->out_paths[0],
- spec->aamix_out_paths[0],
- spec->autocfg.line_out_type);
- update_aamix_paths(codec, val, spec->hp_paths[0],
- spec->aamix_out_paths[1],
- AUTO_PIN_HP_OUT);
- update_aamix_paths(codec, val, spec->speaker_paths[0],
- spec->aamix_out_paths[2],
- AUTO_PIN_SPEAKER_OUT);
+ if (has_aamix_out_paths(spec)) {
+ update_aamix_paths(codec, val, spec->out_paths[0],
+ spec->aamix_out_paths[0],
+ cfg->line_out_type);
+ update_aamix_paths(codec, val, spec->hp_paths[0],
+ spec->aamix_out_paths[1],
+ AUTO_PIN_HP_OUT);
+ update_aamix_paths(codec, val, spec->speaker_paths[0],
+ spec->aamix_out_paths[2],
+ AUTO_PIN_SPEAKER_OUT);
+ } else {
+ update_output_paths(codec, cfg->line_outs, spec->out_paths);
+ if (cfg->line_out_type != AUTO_PIN_HP_OUT)
+ update_output_paths(codec, cfg->hp_outs, spec->hp_paths);
+ if (cfg->line_out_type != AUTO_PIN_SPEAKER_OUT)
+ update_output_paths(codec, cfg->speaker_outs,
+ spec->speaker_paths);
+ }
return 1;
}
@@ -2351,12 +2397,13 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
if (!spec->mixer_nid)
return 0;
- if (!(spec->aamix_out_paths[0] || spec->aamix_out_paths[1] ||
- spec->aamix_out_paths[2]))
- return 0;
if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
return -ENOMEM;
spec->have_aamix_ctl = 1;
+ /* if no explicit aamix path is present (e.g. for Realtek codecs),
+ * enable aamix as default -- just for compatibility
+ */
+ spec->aamix_mode = !has_aamix_out_paths(spec);
return 0;
}
@@ -5236,6 +5283,8 @@ static void init_aamix_paths(struct hda_codec *codec)
if (!spec->have_aamix_ctl)
return;
+ if (!has_aamix_out_paths(spec))
+ return;
update_aamix_paths(codec, spec->aamix_mode, spec->out_paths[0],
spec->aamix_out_paths[0],
spec->autocfg.line_out_type);
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 1b3b38d025fc..087228a0bcea 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec)
snd_hda_gen_update_outputs(codec);
if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
- spec->gpio_data = spec->gen.hp_jack_present ?
- spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ if (spec->gen.automute_speaker)
+ spec->gpio_data = spec->gen.hp_jack_present ?
+ spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+ else
+ spec->gpio_data =
+ spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
snd_hda_codec_write(codec, 0x01, 0,
AC_VERB_SET_GPIO_DATA, spec->gpio_data);
}
@@ -614,6 +618,7 @@ enum {
CS4208_MAC_AUTO,
CS4208_MBA6,
CS4208_MBP11,
+ CS4208_MACMINI,
CS4208_GPIO0,
};
@@ -621,6 +626,7 @@ static const struct hda_model_fixup cs4208_models[] = {
{ .id = CS4208_GPIO0, .name = "gpio0" },
{ .id = CS4208_MBA6, .name = "mba6" },
{ .id = CS4208_MBP11, .name = "mbp11" },
+ { .id = CS4208_MACMINI, .name = "macmini" },
{}
};
@@ -632,6 +638,7 @@ static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
/* codec SSID matching */
static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
+ SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI),
SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
@@ -666,6 +673,24 @@ static void cs4208_fixup_mac(struct hda_codec *codec,
snd_hda_apply_fixup(codec, action);
}
+/* MacMini 7,1 has the inverted jack detection */
+static void cs4208_fixup_macmini(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ static const struct hda_pintbl pincfgs[] = {
+ { 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */
+ { 0x21, 0x004be140 }, /* SPDIF: disable detect */
+ { }
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* HP pin (0x10) has an inverted detection */
+ codec->inv_jack_detect = 1;
+ /* disable the bogus Mic and SPDIF jack detections */
+ snd_hda_apply_pincfgs(codec, pincfgs);
+ }
+}
+
static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -709,6 +734,12 @@ static const struct hda_fixup cs4208_fixups[] = {
.chained = true,
.chain_id = CS4208_GPIO0,
},
+ [CS4208_MACMINI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cs4208_fixup_macmini,
+ .chained = true,
+ .chain_id = CS4208_GPIO0,
+ },
[CS4208_GPIO0] = {
.type = HDA_FIXUP_FUNC,
.v.func = cs4208_fixup_gpio0,
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index b422e406a9cb..f6c5cbbbcfea 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -48,8 +48,9 @@ MODULE_PARM_DESC(static_hdmi_pcm, "Don't restrict PCM parameters per ELD info");
#define is_haswell(codec) ((codec)->vendor_id == 0x80862807)
#define is_broadwell(codec) ((codec)->vendor_id == 0x80862808)
#define is_skylake(codec) ((codec)->vendor_id == 0x80862809)
+#define is_broxton(codec) ((codec)->vendor_id == 0x8086280a)
#define is_haswell_plus(codec) (is_haswell(codec) || is_broadwell(codec) \
- || is_skylake(codec))
+ || is_skylake(codec) || is_broxton(codec))
#define is_valleyview(codec) ((codec)->vendor_id == 0x80862882)
#define is_cherryview(codec) ((codec)->vendor_id == 0x80862883)
@@ -434,7 +435,8 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
eld = &per_pin->sink_eld;
mutex_lock(&per_pin->lock);
- if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data)) {
+ if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
+ eld->eld_size > ELD_MAX_SIZE) {
mutex_unlock(&per_pin->lock);
snd_BUG();
return -EINVAL;
@@ -1553,6 +1555,8 @@ static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
mutex_lock(&per_pin->lock);
pin_eld->monitor_present = !!(present & AC_PINSENSE_PRESENCE);
+ eld->monitor_present = pin_eld->monitor_present;
+
if (pin_eld->monitor_present)
eld->eld_valid = !!(present & AC_PINSENSE_ELDV);
else
@@ -3354,6 +3358,9 @@ static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0082, .name = "GPU 82 HDMI/DP", .patch = patch_nvhdmi },
+{ .id = 0x10de0083, .name = "GPU 83 HDMI/DP", .patch = patch_nvhdmi },
{ .id = 0x10de8001, .name = "MCP73 HDMI", .patch = patch_nvhdmi_2ch },
{ .id = 0x11069f80, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
{ .id = 0x11069f81, .name = "VX900 HDMI/DP", .patch = patch_via_hdmi },
@@ -3415,6 +3422,7 @@ MODULE_ALIAS("snd-hda-codec-id:10de0067");
MODULE_ALIAS("snd-hda-codec-id:10de0070");
MODULE_ALIAS("snd-hda-codec-id:10de0071");
MODULE_ALIAS("snd-hda-codec-id:10de0072");
+MODULE_ALIAS("snd-hda-codec-id:10de007d");
MODULE_ALIAS("snd-hda-codec-id:10de8001");
MODULE_ALIAS("snd-hda-codec-id:11069f80");
MODULE_ALIAS("snd-hda-codec-id:11069f81");
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d36cdb27a02c..1bc0be907ead 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -29,6 +29,7 @@
#include <linux/pci.h>
#include <linux/dmi.h>
#include <linux/module.h>
+#include <linux/input.h>
#include <sound/core.h>
#include <sound/jack.h>
#include "hda_codec.h"
@@ -96,6 +97,8 @@ struct alc_spec {
hda_nid_t cap_mute_led_nid;
unsigned int gpio_led; /* used for alc269_fixup_hp_gpio_led() */
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
hda_nid_t headset_mic_pin;
hda_nid_t headphone_mic_pin;
@@ -118,6 +121,9 @@ struct alc_spec {
hda_nid_t pll_nid;
unsigned int pll_coef_idx, pll_coef_bit;
unsigned int coef0;
+#if IS_ENABLED(CONFIG_INPUT)
+ struct input_dev *kb_dev;
+#endif
};
/*
@@ -2198,6 +2204,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x9047, "Sony Vaio TT", ALC889_FIXUP_VAIO_TT),
SND_PCI_QUIRK(0x104d, 0x905a, "Sony Vaio Z", ALC882_FIXUP_NO_PRIMARY_HP),
SND_PCI_QUIRK(0x104d, 0x9043, "Sony Vaio VGC-LN51JGB", ALC882_FIXUP_NO_PRIMARY_HP),
+ SND_PCI_QUIRK(0x104d, 0x9044, "Sony VAIO AiO", ALC882_FIXUP_NO_PRIMARY_HP),
/* All Apple entries are in codec SSIDs */
SND_PCI_QUIRK(0x106b, 0x00a0, "MacBookPro 3,1", ALC889_FIXUP_MBP_VREF),
@@ -3321,41 +3328,45 @@ static void alc269_fixup_hp_mute_led_mic2(struct hda_codec *codec,
}
}
-/* turn on/off mute LED per vmaster hook */
-static void alc269_fixup_hp_gpio_mute_hook(void *private_data, int enabled)
+/* update LED status via GPIO */
+static void alc_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ bool enabled)
{
- struct hda_codec *codec = private_data;
struct alc_spec *spec = codec->spec;
unsigned int oldval = spec->gpio_led;
+ if (spec->mute_led_polarity)
+ enabled = !enabled;
+
if (enabled)
- spec->gpio_led &= ~0x08;
+ spec->gpio_led &= ~mask;
else
- spec->gpio_led |= 0x08;
+ spec->gpio_led |= mask;
if (spec->gpio_led != oldval)
snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
spec->gpio_led);
}
-/* turn on/off mic-mute LED per capture hook */
-static void alc269_fixup_hp_gpio_mic_mute_hook(struct hda_codec *codec,
- struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+/* turn on/off mute LED via GPIO per vmaster hook */
+static void alc_fixup_gpio_mute_hook(void *private_data, int enabled)
{
+ struct hda_codec *codec = private_data;
struct alc_spec *spec = codec->spec;
- unsigned int oldval = spec->gpio_led;
- if (!ucontrol)
- return;
+ alc_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
+}
- if (ucontrol->value.integer.value[0] ||
- ucontrol->value.integer.value[1])
- spec->gpio_led &= ~0x10;
- else
- spec->gpio_led |= 0x10;
- if (spec->gpio_led != oldval)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_led);
+/* turn on/off mic-mute LED via GPIO per capture hook */
+static void alc_fixup_gpio_mic_mute_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (ucontrol)
+ alc_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ ucontrol->value.integer.value[0] ||
+ ucontrol->value.integer.value[1]);
}
static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
@@ -3369,9 +3380,33 @@ static void alc269_fixup_hp_gpio_led(struct hda_codec *codec,
};
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
- spec->gen.cap_sync_hook = alc269_fixup_hp_gpio_mic_mute_hook;
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x08;
+ spec->gpio_mic_led_mask = 0x10;
+ snd_hda_add_verbs(codec, gpio_init);
+ }
+}
+
+static void alc286_fixup_hp_gpio_led(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x22 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x22 },
+ {}
+ };
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x02;
+ spec->gpio_mic_led_mask = 0x20;
snd_hda_add_verbs(codec, gpio_init);
}
}
@@ -3413,9 +3448,11 @@ static void alc269_fixup_hp_gpio_mic1_led(struct hda_codec *codec,
};
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x08;
spec->cap_mute_led_nid = 0x18;
snd_hda_add_verbs(codec, gpio_init);
codec->power_filter = led_power_filter;
@@ -3434,15 +3471,147 @@ static void alc280_fixup_hp_gpio4(struct hda_codec *codec,
};
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc269_fixup_hp_gpio_mute_hook;
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
spec->gen.cap_sync_hook = alc269_fixup_hp_cap_mic_mute_hook;
spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x08;
spec->cap_mute_led_nid = 0x18;
snd_hda_add_verbs(codec, gpio_init);
codec->power_filter = led_power_filter;
}
}
+#if IS_ENABLED(CONFIG_INPUT)
+static void gpio2_mic_hotkey_event(struct hda_codec *codec,
+ struct hda_jack_callback *event)
+{
+ struct alc_spec *spec = codec->spec;
+
+ /* GPIO2 just toggles on a keypress/keyrelease cycle. Therefore
+ send both key on and key off event for every interrupt. */
+ input_report_key(spec->kb_dev, KEY_MICMUTE, 1);
+ input_sync(spec->kb_dev);
+ input_report_key(spec->kb_dev, KEY_MICMUTE, 0);
+ input_sync(spec->kb_dev);
+}
+#endif
+
+static int alc_register_micmute_input_device(struct hda_codec *codec)
+{
+ struct alc_spec *spec = codec->spec;
+
+ spec->kb_dev = input_allocate_device();
+ if (!spec->kb_dev) {
+ codec_err(codec, "Out of memory (input_allocate_device)\n");
+ return -ENOMEM;
+ }
+ spec->kb_dev->name = "Microphone Mute Button";
+ spec->kb_dev->evbit[0] = BIT_MASK(EV_KEY);
+ spec->kb_dev->keybit[BIT_WORD(KEY_MICMUTE)] = BIT_MASK(KEY_MICMUTE);
+
+ if (input_register_device(spec->kb_dev)) {
+ codec_err(codec, "input_register_device failed\n");
+ input_free_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void alc280_fixup_hp_gpio2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+#if IS_ENABLED(CONFIG_INPUT)
+ /* GPIO1 = set according to SKU external amp
+ GPIO2 = mic mute hotkey
+ GPIO3 = mute LED
+ GPIO4 = mic mute LED */
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x1e },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x1a },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x02 },
+ {}
+ };
+
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ snd_hda_add_verbs(codec, gpio_init);
+ snd_hda_codec_write_cache(codec, codec->afg, 0,
+ AC_VERB_SET_GPIO_UNSOLICITED_RSP_MASK, 0x04);
+ snd_hda_jack_detect_enable_callback(codec, codec->afg,
+ gpio2_mic_hotkey_event);
+
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x08;
+ spec->gpio_mic_led_mask = 0x10;
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ input_free_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+#endif
+}
+
+static void alc233_fixup_lenovo_line2_mic_hotkey(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ /* Line2 = mic mute hotkey
+ GPIO2 = mic mute LED */
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
+ {}
+ };
+
+ struct alc_spec *spec = codec->spec;
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ if (alc_register_micmute_input_device(codec) != 0)
+ return;
+
+ snd_hda_add_verbs(codec, gpio_init);
+ snd_hda_jack_detect_enable_callback(codec, 0x1b,
+ gpio2_mic_hotkey_event);
+
+ spec->gen.cap_sync_hook = alc_fixup_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mic_led_mask = 0x04;
+ return;
+ }
+
+ if (!spec->kb_dev)
+ return;
+
+ switch (action) {
+ case HDA_FIXUP_ACT_PROBE:
+ spec->init_amp = ALC_INIT_DEFAULT;
+ break;
+ case HDA_FIXUP_ACT_FREE:
+ input_unregister_device(spec->kb_dev);
+ spec->kb_dev = NULL;
+ }
+}
+
static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -3628,6 +3797,10 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
static void alc_headset_mode_default(struct hda_codec *codec)
{
+ static struct coef_fw coef0225[] = {
+ UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
+ {}
+ };
static struct coef_fw coef0255[] = {
WRITE_COEF(0x45, 0xc089),
WRITE_COEF(0x45, 0xc489),
@@ -3669,6 +3842,9 @@ static void alc_headset_mode_default(struct hda_codec *codec)
};
switch (codec->vendor_id) {
+ case 0x10ec0225:
+ alc_process_coef_fw(codec, coef0225);
+ break;
case 0x10ec0255:
case 0x10ec0256:
alc_process_coef_fw(codec, coef0255);
@@ -4073,6 +4249,29 @@ static void alc_fixup_headset_mode_alc255_no_hp_mic(struct hda_codec *codec,
alc_fixup_headset_mode(codec, fix, action);
}
+static void alc288_update_headset_jack_cb(struct hda_codec *codec,
+ struct hda_jack_callback *jack)
+{
+ struct alc_spec *spec = codec->spec;
+ int present;
+
+ alc_update_headset_jack_cb(codec, jack);
+ /* Headset Mic enable or disable, only for Dell Dino */
+ present = spec->gen.hp_jack_present ? 0x40 : 0;
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ present);
+}
+
+static void alc_fixup_headset_mode_dell_alc288(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ alc_fixup_headset_mode(codec, fix, action);
+ if (action == HDA_FIXUP_ACT_PROBE) {
+ struct alc_spec *spec = codec->spec;
+ spec->gen.hp_automute_hook = alc288_update_headset_jack_cb;
+ }
+}
+
static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
@@ -4419,6 +4618,7 @@ enum {
ALC290_FIXUP_SUBWOOFER,
ALC290_FIXUP_SUBWOOFER_HSJACK,
ALC269_FIXUP_THINKPAD_ACPI,
+ ALC269_FIXUP_DMIC_THINKPAD_ACPI,
ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE,
@@ -4430,6 +4630,20 @@ enum {
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
ALC280_FIXUP_HP_GPIO4,
+ ALC286_FIXUP_HP_GPIO_LED,
+ ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY,
+ ALC280_FIXUP_HP_DOCK_PINS,
+ ALC288_FIXUP_DELL_HEADSET_MODE,
+ ALC288_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC288_FIXUP_DELL_XPS_13_GPIO6,
+ ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC275_FIXUP_DELL_XPS,
+ ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE,
+ ALC293_FIXUP_LENOVO_SPK_NOISE,
+ ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY,
+ ALC255_FIXUP_DELL_SPK_NOISE,
+ ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC280_FIXUP_HP_HEADSET_MIC,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -4839,6 +5053,12 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = hda_fixup_thinkpad_acpi,
},
+ [ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_inv_dmic,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI,
+ },
[ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -4925,6 +5145,117 @@ static const struct hda_fixup alc269_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc280_fixup_hp_gpio4,
},
+ [ALC286_FIXUP_HP_GPIO_LED] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc286_fixup_hp_gpio_led,
+ },
+ [ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc280_fixup_hp_gpio2_mic_hotkey,
+ },
+ [ALC280_FIXUP_HP_DOCK_PINS] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x21011020 }, /* line-out */
+ { 0x1a, 0x01a1903c }, /* headset mic */
+ { 0x18, 0x2181103f }, /* line-in */
+ { },
+ },
+ .chained = true,
+ .chain_id = ALC280_FIXUP_HP_GPIO4
+ },
+ [ALC288_FIXUP_DELL_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_dell_alc288,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED
+ },
+ [ALC288_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DELL_HEADSET_MODE
+ },
+ [ALC288_FIXUP_DELL_XPS_13_GPIO6] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ {0x01, AC_VERB_SET_GPIO_MASK, 0x40},
+ {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x40},
+ {0x01, AC_VERB_SET_GPIO_DATA, 0x00},
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC288_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC298_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x18, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { 0x1a, 0x01a1913d }, /* use as headphone mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MODE
+ },
+ [ALC275_FIXUP_DELL_XPS] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x1f},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00c0},
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x30},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x00b1},
+ {}
+ }
+ },
+ [ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x36},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x1737},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC293_FIXUP_LENOVO_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_THINKPAD_ACPI
+ },
+ [ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_fixup_lenovo_line2_mic_hotkey,
+ },
+ [ALC255_FIXUP_DELL_SPK_NOISE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC225_FIXUP_DELL1_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Disable pass-through path for FRONT 14h */
+ { 0x20, AC_VERB_SET_COEF_INDEX, 0x36 },
+ { 0x20, AC_VERB_SET_PROC_COEF, 0x57d7 },
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
+ [ALC280_FIXUP_HP_HEADSET_MIC] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_disable_aamix,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -4936,9 +5267,11 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x080d, "Acer Aspire V5-122P", ALC269_FIXUP_ASPIRE_HEADSET_MIC),
SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+ SND_PCI_QUIRK(0x1025, 0x0762, "Acer Aspire E1-472", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
SND_PCI_QUIRK(0x1025, 0x0775, "Acer Aspire E1-572", ALC271_FIXUP_HP_GATE_MIC_JACK_E1_572),
SND_PCI_QUIRK(0x1025, 0x079b, "Acer Aspire V5-573G", ALC282_FIXUP_ASPIRE_V5_PINS),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
+ SND_PCI_QUIRK(0x1028, 0x054b, "Dell XPS one 2710", ALC275_FIXUP_DELL_XPS),
SND_PCI_QUIRK(0x1028, 0x05da, "Dell Vostro 5460", ALC290_FIXUP_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x05f4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05f5, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -4951,11 +5284,14 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x06c7, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x218b, "HP", ALC269_FIXUP_LIMIT_INT_MIC_BOOST_MUTE_LED),
+ SND_PCI_QUIRK(0x103c, 0x225f, "HP", ALC280_FIXUP_HP_GPIO2_MIC_HOTKEY),
/* ALC282 */
SND_PCI_QUIRK(0x103c, 0x21f9, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2210, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -4969,6 +5305,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x226a, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x226b, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x226e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x2271, "HP", ALC286_FIXUP_HP_GPIO_LED),
+ SND_PCI_QUIRK(0x103c, 0x2272, "HP", ALC280_FIXUP_HP_DOCK_PINS),
SND_PCI_QUIRK(0x103c, 0x229e, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22b2, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x22b7, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
@@ -5006,6 +5344,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x2335, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2336, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x2337, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+ SND_PCI_QUIRK(0x103c, 0x221c, "HP EliteBook 755 G2", ALC280_FIXUP_HP_HEADSET_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -5052,6 +5391,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+ SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -5061,6 +5404,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x5034, "Thinkpad T450", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x5036, "Thinkpad T450s", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
@@ -5141,6 +5485,9 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{.id = ALC292_FIXUP_TPT440_DOCK, .name = "tpt440-dock"},
{}
};
+#define ALC225_STANDARD_PINS \
+ {0x12, 0xb7a60130}, \
+ {0x21, 0x04211020}
#define ALC255_STANDARD_PINS \
{0x18, 0x411111f0}, \
@@ -5166,6 +5513,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x1b, 0x411111f0}, \
{0x1e, 0x411111f0}
+#define ALC288_STANDARD_PINS \
+ {0x17, 0x411111f0}, \
+ {0x18, 0x411111f0}, \
+ {0x19, 0x411111f0}, \
+ {0x1a, 0x411111f0}, \
+ {0x1e, 0x411111f0}
+
#define ALC290_STANDARD_PINS \
{0x12, 0x99a30130}, \
{0x13, 0x40000000}, \
@@ -5183,7 +5537,26 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x1d, 0x40700001}, \
{0x1e, 0x411111f0}
+#define ALC298_STANDARD_PINS \
+ {0x18, 0x411111f0}, \
+ {0x19, 0x411111f0}, \
+ {0x1a, 0x411111f0}, \
+ {0x1e, 0x411111f0}, \
+ {0x1f, 0x411111f0}
+
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x14, 0x901701b0}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_STANDARD_PINS,
{0x12, 0x40300000},
@@ -5364,6 +5737,13 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x19, 0x03a11020},
{0x1d, 0x40e00001},
{0x21, 0x0321101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0288, 0x1028, "Dell", ALC288_FIXUP_DELL_XPS_13_GPIO6,
+ ALC288_STANDARD_PINS,
+ {0x12, 0x90a60120},
+ {0x13, 0x40000000},
+ {0x14, 0x90170110},
+ {0x1d, 0x4076832d},
+ {0x21, 0x0321101f}),
SND_HDA_PIN_QUIRK(0x10ec0290, 0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC1,
ALC290_STANDARD_PINS,
{0x14, 0x411111f0},
@@ -5448,6 +5828,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x16, 0x411111f0},
{0x18, 0x411111f0},
{0x19, 0x411111f0}),
+ SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC298_STANDARD_PINS,
+ {0x12, 0x90a60130},
+ {0x13, 0x40000000},
+ {0x14, 0x411111f0},
+ {0x17, 0x90170140},
+ {0x1d, 0x4068a36d},
+ {0x21, 0x03211020}),
{}
};
@@ -5904,22 +6292,6 @@ static void alc_fixup_bass_chmap(struct hda_codec *codec,
}
}
-/* turn on/off mute LED per vmaster hook */
-static void alc662_led_gpio1_mute_hook(void *private_data, int enabled)
-{
- struct hda_codec *codec = private_data;
- struct alc_spec *spec = codec->spec;
- unsigned int oldval = spec->gpio_led;
-
- if (enabled)
- spec->gpio_led |= 0x01;
- else
- spec->gpio_led &= ~0x01;
- if (spec->gpio_led != oldval)
- snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
- spec->gpio_led);
-}
-
/* avoid D3 for keeping GPIO up */
static unsigned int gpio_led_power_filter(struct hda_codec *codec,
hda_nid_t nid,
@@ -5942,8 +6314,10 @@ static void alc662_fixup_led_gpio1(struct hda_codec *codec,
};
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
- spec->gen.vmaster_mute.hook = alc662_led_gpio1_mute_hook;
+ spec->gen.vmaster_mute.hook = alc_fixup_gpio_mute_hook;
spec->gpio_led = 0;
+ spec->mute_led_polarity = 1;
+ spec->gpio_mute_led_mask = 0x01;
snd_hda_add_verbs(codec, gpio_init);
codec->power_filter = gpio_led_power_filter;
}
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 4a28252a42b9..b85ca1a2f75e 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -2894,6 +2894,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
static struct snd_pci_quirk intel8x0_clock_list[] = {
SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+ SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 7646ba1664eb..799fe9a81552 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -2927,7 +2927,7 @@ static int snd_hdsp_get_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
{
struct hdsp *hdsp = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hdsp_dds_offset(hdsp);
+ ucontrol->value.integer.value[0] = hdsp_dds_offset(hdsp);
return 0;
}
@@ -2939,7 +2939,7 @@ static int snd_hdsp_put_dds_offset(struct snd_kcontrol *kcontrol, struct snd_ctl
if (!snd_hdsp_use_is_exclusive(hdsp))
return -EBUSY;
- val = ucontrol->value.enumerated.item[0];
+ val = ucontrol->value.integer.value[0];
spin_lock_irq(&hdsp->lock);
if (val != hdsp_dds_offset(hdsp))
change = (hdsp_set_dds_offset(hdsp, val) == 0) ? 1 : 0;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index fcf91ee0328d..120fb18dd92b 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -1602,6 +1602,9 @@ static void hdspm_set_dds_value(struct hdspm *hdspm, int rate)
{
u64 n;
+ if (snd_BUG_ON(rate <= 0))
+ return;
+
if (rate >= 112000)
rate /= 4;
else if (rate >= 56000)
@@ -2220,6 +2223,8 @@ static int hdspm_get_system_sample_rate(struct hdspm *hdspm)
} else {
/* slave mode, return external sample rate */
rate = hdspm_external_sample_rate(hdspm);
+ if (!rate)
+ rate = hdspm->system_sample_rate;
}
}
@@ -2265,8 +2270,11 @@ static int snd_hdspm_put_system_sample_rate(struct snd_kcontrol *kcontrol,
ucontrol)
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+ int rate = ucontrol->value.integer.value[0];
- hdspm_set_dds_value(hdspm, ucontrol->value.enumerated.item[0]);
+ if (rate < 27000 || rate > 207000)
+ return -EINVAL;
+ hdspm_set_dds_value(hdspm, ucontrol->value.integer.value[0]);
return 0;
}
@@ -4465,7 +4473,7 @@ static int snd_hdspm_get_tco_word_term(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- ucontrol->value.enumerated.item[0] = hdspm->tco->term;
+ ucontrol->value.integer.value[0] = hdspm->tco->term;
return 0;
}
@@ -4476,8 +4484,8 @@ static int snd_hdspm_put_tco_word_term(struct snd_kcontrol *kcontrol,
{
struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
- if (hdspm->tco->term != ucontrol->value.enumerated.item[0]) {
- hdspm->tco->term = ucontrol->value.enumerated.item[0];
+ if (hdspm->tco->term != ucontrol->value.integer.value[0]) {
+ hdspm->tco->term = ucontrol->value.integer.value[0];
hdspm_tco_write(hdspm);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index d16331e0b64d..35a27281ed86 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -474,7 +474,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
/* IN1/IN2 Control */
SOC_SINGLE_TLV("IN1 Boost", RT5645_IN1_CTRL1,
- RT5645_BST_SFT1, 8, 0, bst_tlv),
+ RT5645_BST_SFT1, 12, 0, bst_tlv),
SOC_SINGLE_TLV("IN2 Boost", RT5645_IN2_CTRL,
RT5645_BST_SFT2, 8, 0, bst_tlv),
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index 1304cf20a98a..417e56cf0311 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -1515,7 +1515,8 @@ correct_plug_type:
mbhc->hs_detect_work_stop);
wcd_enable_curr_micbias(mbhc,
WCD_MBHC_EN_NONE);
- if (mbhc->micbias_enable) {
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+ mbhc->micbias_enable) {
mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
mbhc->codec, MIC_BIAS_2, false);
if (mbhc->mbhc_cb->set_micbias_value)
@@ -1540,7 +1541,8 @@ correct_plug_type:
mbhc->hs_detect_work_stop);
wcd_enable_curr_micbias(mbhc,
WCD_MBHC_EN_NONE);
- if (mbhc->micbias_enable) {
+ if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+ mbhc->micbias_enable) {
mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
mbhc->codec, MIC_BIAS_2, false);
if (mbhc->mbhc_cb->set_micbias_value)
diff --git a/sound/soc/codecs/wcd9306.c b/sound/soc/codecs/wcd9306.c
index 356774593bd4..5a8436c295fc 100644
--- a/sound/soc/codecs/wcd9306.c
+++ b/sound/soc/codecs/wcd9306.c
@@ -52,10 +52,6 @@
/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
#define TAPAN_WG_TIME_FACTOR_US 240
-#define TAPAN_SB_PGD_PORT_RX_BASE 0x40
-#define TAPAN_SB_PGD_PORT_TX_BASE 0x50
-#define TAPAN_REGISTER_START_OFFSET 0x800
-
#define CODEC_REG_CFG_MINOR_VER 1
#define BUS_DOWN 1
@@ -3538,139 +3534,9 @@ static const struct snd_soc_dapm_route wcd9302_map[] = {
{"RDAC5 MUX", "DEM3_INV", "RDAC4 MUX"},
};
-static int tapan_readable(struct snd_soc_codec *ssc, unsigned int reg)
-{
- return tapan_reg_readable[reg];
-}
-
-static bool tapan_is_digital_gain_register(unsigned int reg)
-{
- bool rtn = false;
-
- switch (reg) {
- case TAPAN_A_CDC_RX1_VOL_CTL_B2_CTL:
- case TAPAN_A_CDC_RX2_VOL_CTL_B2_CTL:
- case TAPAN_A_CDC_RX3_VOL_CTL_B2_CTL:
- case TAPAN_A_CDC_RX4_VOL_CTL_B2_CTL:
- case TAPAN_A_CDC_TX1_VOL_CTL_GAIN:
- case TAPAN_A_CDC_TX2_VOL_CTL_GAIN:
- case TAPAN_A_CDC_TX3_VOL_CTL_GAIN:
- case TAPAN_A_CDC_TX4_VOL_CTL_GAIN:
- rtn = true;
- break;
- default:
- break;
- }
- return rtn;
-}
-
-static int tapan_volatile(struct snd_soc_codec *ssc, unsigned int reg)
-{
-
- int i = 0;
-
- /* Registers lower than 0x100 are top level registers which can be
- * written by the Tapan core driver.
- */
-
- if ((reg >= TAPAN_A_CDC_MBHC_EN_CTL) || (reg < 0x100))
- return 1;
-
- /* IIR Coeff registers are not cacheable */
- if ((reg >= TAPAN_A_CDC_IIR1_COEF_B1_CTL) &&
- (reg <= TAPAN_A_CDC_IIR2_COEF_B2_CTL))
- return 1;
-
- /* ANC filter registers are not cacheable */
- if ((reg >= TAPAN_A_CDC_ANC1_IIR_B1_CTL) &&
- (reg <= TAPAN_A_CDC_ANC1_LPF_B2_CTL))
- return 1;
- if ((reg >= TAPAN_A_CDC_ANC2_IIR_B1_CTL) &&
- (reg <= TAPAN_A_CDC_ANC2_LPF_B2_CTL))
- return 1;
-
- /* Digital gain register is not cacheable so we have to write
- * the setting even it is the same
- */
- if (tapan_is_digital_gain_register(reg))
- return 1;
-
- /* HPH status registers */
- if (reg == TAPAN_A_RX_HPH_L_STATUS || reg == TAPAN_A_RX_HPH_R_STATUS)
- return 1;
-
- if (reg == TAPAN_A_MBHC_INSERT_DET_STATUS)
- return 1;
-
- for (i = 0; i < ARRAY_SIZE(audio_reg_cfg); i++)
- if (audio_reg_cfg[i].reg_logical_addr -
- TAPAN_REGISTER_START_OFFSET == reg)
- return 1;
-
- return 0;
-}
-
#define TAPAN_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
#define TAPAN_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FORMAT_S24_LE)
-static int tapan_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
-{
- int ret;
- struct wcd9xxx *wcd9xxx = codec->control_data;
- struct tapan_priv *tapan_p = snd_soc_codec_get_drvdata(codec);
-
- if (reg == SND_SOC_NOPM)
- return 0;
-
- BUG_ON(reg > TAPAN_MAX_REGISTER);
-
- if (!tapan_volatile(codec, reg)) {
- ret = snd_soc_cache_write(codec, reg, value);
- if (ret != 0)
- dev_err(codec->dev, "Cache write to %x failed: %d\n",
- reg, ret);
- }
-
- if (unlikely(test_bit(BUS_DOWN, &tapan_p->status_mask))) {
- printk_ratelimited("write 0x%02x while offline\n", reg);
- return -ENODEV;
- } else {
- return wcd9xxx_reg_write(&wcd9xxx->core_res, reg, value);
- }
-}
-
-static unsigned int tapan_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- unsigned int val;
- int ret;
- struct wcd9xxx *wcd9xxx = codec->control_data;
- struct tapan_priv *tapan_p = snd_soc_codec_get_drvdata(codec);
-
- if (reg == SND_SOC_NOPM)
- return 0;
-
- BUG_ON(reg > TAPAN_MAX_REGISTER);
-
- if (!tapan_volatile(codec, reg) && tapan_readable(codec, reg) &&
- reg < codec->driver->reg_cache_size) {
- ret = snd_soc_cache_read(codec, reg, &val);
- if (ret >= 0)
- return val;
-
- dev_err(codec->dev, "Cache read from %x failed: %d\n",
- reg, ret);
- }
-
- if (unlikely(test_bit(BUS_DOWN, &tapan_p->status_mask))) {
- printk_ratelimited("write 0x%02x while offline\n", reg);
- return -ENODEV;
- }
-
- val = wcd9xxx_reg_read(&wcd9xxx->core_res, reg);
- return val;
-}
static int tapan_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
@@ -6638,26 +6504,24 @@ static int tapan_codec_remove(struct snd_soc_codec *codec)
return 0;
}
+static struct regmap *tapan_get_regmap(struct device *dev)
+{
+ struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+ return control->regmap;
+}
+
static struct snd_soc_codec_driver soc_codec_dev_tapan = {
.probe = tapan_codec_probe,
.remove = tapan_codec_remove,
- .read = tapan_read,
- .write = tapan_write,
-
- .readable_register = tapan_readable,
- .volatile_register = tapan_volatile,
-
- .reg_cache_size = TAPAN_CACHE_SIZE,
- .reg_cache_default = tapan_reset_reg_defaults,
- .reg_word_size = 1,
-
.controls = tapan_common_snd_controls,
.num_controls = ARRAY_SIZE(tapan_common_snd_controls),
.dapm_widgets = tapan_common_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(tapan_common_dapm_widgets),
.dapm_routes = audio_map,
.num_dapm_routes = ARRAY_SIZE(audio_map),
+ .get_regmap = tapan_get_regmap,
};
#ifdef CONFIG_PM
diff --git a/sound/soc/codecs/wcd9306.h b/sound/soc/codecs/wcd9306.h
index 3df73a81e5e7..1b4b032815fa 100644
--- a/sound/soc/codecs/wcd9306.h
+++ b/sound/soc/codecs/wcd9306.h
@@ -20,16 +20,10 @@
#include "wcd9xxx-resmgr.h"
#include "wcd9xxx-common.h"
-#define TAPAN_NUM_REGISTERS 0x400
-#define TAPAN_MAX_REGISTER (TAPAN_NUM_REGISTERS-1)
-#define TAPAN_CACHE_SIZE TAPAN_NUM_REGISTERS
-
#define TAPAN_REG_VAL(reg, val) {reg, 0, val}
#define TAPAN_CDC_ZDET_SUPPORTED true
-extern const u8 tapan_reg_readable[TAPAN_CACHE_SIZE];
-extern const u8 tapan_reset_reg_defaults[TAPAN_CACHE_SIZE];
struct tapan_codec_dai_data {
u32 rate;
u32 *ch_num;
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index d668bf823a33..f082e1d64e98 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -378,6 +378,7 @@ enum {
AIF_MIX1_PB,
AIF4_MAD_TX,
AIF4_VIFEED,
+ AIF5_CPE_TX,
NUM_CODEC_DAIS,
};
@@ -486,15 +487,18 @@ static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = {
};
static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
- 0, /* AIF1_PB */
- BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF1_CAP */
- 0, /* AIF2_PB */
- BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX), /* AIF2_CAP */
- 0, /* AIF3_PB */
- BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX), /* AIF3_CAP */
- 0, /* AIF4_PB */
- 0, /* AIF_MIX1_PB */
- BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP), /* AIF4_MAD_TX */
+ /* Needs to define in the same order of DAI enum definitions */
+ 0,
+ BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+ 0,
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX),
+ 0,
+ BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX),
};
static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = {
@@ -561,6 +565,8 @@ static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv);
static int tasha_config_compander(struct snd_soc_codec *, int, int);
static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool);
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+ bool enable);
/* Hold instance to soundwire platform device */
struct tasha_swr_ctrl_data {
@@ -817,6 +823,8 @@ struct tasha_priv {
int hph_r_gain;
int rx_7_count;
int rx_8_count;
+ bool clk_mode;
+ bool clk_internal;
};
static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
@@ -2104,6 +2112,30 @@ static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
return 0;
}
+static int tasha_get_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.enumerated.item[0] = tasha->clk_mode;
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
+static int tasha_put_clkmode(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+ tasha->clk_mode = ucontrol->value.enumerated.item[0];
+ dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+ return 0;
+}
+
static int tasha_get_iir_enable_audio_mixer(
struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -2339,6 +2371,7 @@ static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
}
break;
case AIF4_MAD_TX:
+ case AIF5_CPE_TX:
break;
default:
pr_err("Unknown AIF %d\n", dai_id);
@@ -5632,6 +5665,18 @@ static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec,
tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ :
WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+
+ /*
+ * Check for ECPP path selection and DEC1 not connected to
+ * any other audio path to apply ECPP DMIC sample rate
+ */
+ if ((adc_mux_index == 1) &&
+ ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG)
+ & 0x0F) == 0x0A) &&
+ ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)
+ & 0x0C) == 0x00)) {
+ dmic_fs = pdata->ecpp_dmic_sample_rate;
+ }
} else {
dmic_fs = pdata->dmic_sample_rate;
}
@@ -5928,6 +5973,9 @@ static const char *const tasha_anc_func_text[] = {"OFF", "ON"};
static const struct soc_enum tasha_anc_func_enum =
SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text);
+static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"};
+static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text);
+
/* Cutoff frequency for high pass filter */
static const char * const cf_text[] = {
"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
@@ -6049,6 +6097,9 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
{"AIF4 MAD", NULL, "AIF4"},
+ {"EC BUF MUX INP", "DEC1", "ADC MUX1"},
+ {"AIF5 CPE", NULL, "EC BUF MUX INP"},
+
/* SLIMBUS Connections */
{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
@@ -8052,6 +8103,9 @@ static const struct snd_kcontrol_new tasha_snd_controls[] = {
SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func,
tasha_put_anc_func),
+ SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode,
+ tasha_put_clkmode),
+
SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
@@ -9808,6 +9862,45 @@ static const struct snd_kcontrol_new anc0_fb_mux =
static const struct snd_kcontrol_new anc1_fb_mux =
SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+
+ dev_dbg(codec->dev, "%s: event = %d name = %s\n",
+ __func__, event, w->name);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x28);
+ snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+ 0x08, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+ 0x08, 0x00);
+ snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x68, 0x40);
+ snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00);
+ break;
+ }
+
+ return 0;
+};
+
+static const char * const ec_buf_mux_text[] = {
+ "ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3",
+ "I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R",
+ "DEC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG,
+ 0, ec_buf_mux_text);
+
+static const struct snd_kcontrol_new ec_buf_mux =
+ SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum);
+
static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
SND_SOC_DAPM_OUTPUT("EAR"),
SND_SOC_DAPM_OUTPUT("ANC EAR"),
@@ -10297,6 +10390,14 @@ static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
SND_SOC_DAPM_INPUT("VIINPUT"),
+
+ SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM,
+ AIF5_CPE_TX, 0),
+
+ SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux,
+ tasha_codec_ec_buf_mux_enable,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
/* Digital Mic Inputs */
SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
@@ -11280,6 +11381,19 @@ static struct snd_soc_dai_driver tasha_dai[] = {
},
.ops = &tasha_dai_ops,
},
+ {
+ .name = "tasha_cpe",
+ .id = AIF5_CPE_TX,
+ .capture = {
+ .stream_name = "AIF5 CPE TX",
+ .rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+ .formats = TASHA_FORMATS_S16_S24_S32_LE,
+ .rate_min = 16000,
+ .rate_max = 48000,
+ .channels_min = 1,
+ .channels_max = 1,
+ },
+ },
};
static struct snd_soc_dai_driver tasha_i2s_dai[] = {
@@ -11529,6 +11643,44 @@ int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm)
}
EXPORT_SYMBOL(tasha_cdc_mclk_enable);
+int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+ struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n",
+ __func__, tasha->clk_mode, enable, tasha->clk_internal);
+ if (tasha->clk_mode || tasha->clk_internal) {
+ if (enable) {
+ tasha_cdc_sido_ccl_enable(tasha, true);
+ wcd_resmgr_enable_master_bias(tasha->resmgr);
+ tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+ 0x01, 0x01);
+ snd_soc_update_bits(codec,
+ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+ 0x01, 0x01);
+ set_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ SIDO_VOLTAGE_NOMINAL_MV);
+ tasha->clk_internal = true;
+ } else {
+ tasha->clk_internal = false;
+ clear_bit(CPE_NOMINAL, &tasha->status_mask);
+ tasha_codec_update_sido_voltage(tasha,
+ sido_buck_svs_voltage);
+ tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+ wcd_resmgr_disable_master_bias(tasha->resmgr);
+ tasha_cdc_sido_ccl_enable(tasha, false);
+ }
+ } else {
+ ret = __tasha_cdc_mclk_enable(tasha, enable);
+ }
+ return ret;
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable);
+
static ssize_t tasha_codec_version_read(struct snd_info_entry *entry,
void *file_private_data, struct file *file,
char __user *buf, size_t count, loff_t pos)
@@ -12183,6 +12335,17 @@ static int tasha_handle_pdata(struct tasha_priv *tasha,
*/
pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
}
+ if (pdata->ecpp_dmic_sample_rate ==
+ WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+ dev_info(codec->dev,
+ "%s: ecpp_dmic_rate invalid default = %d\n",
+ __func__, def_dmic_rate);
+ /*
+ * use dmic_sample_rate as the default for ECPP DMIC
+ * if ecpp dmic sample rate is undefined
+ */
+ pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate;
+ }
if (pdata->dmic_clk_drv ==
WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) {
diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h
index 85ee1b45f409..b9d87371778e 100644
--- a/sound/soc/codecs/wcd9335.h
+++ b/sound/soc/codecs/wcd9335.h
@@ -141,6 +141,8 @@ extern void *tasha_get_afe_config(struct snd_soc_codec *codec,
enum afe_config_type config_type);
extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable,
bool dapm);
+extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable,
+ bool dapm);
extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec);
extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
struct wcd_mbhc_config *mbhc_cfg);
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
index 58fca9131b3c..acf6df089a1b 100644
--- a/sound/soc/codecs/wcd_cpe_core.c
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -35,9 +35,10 @@
#include "wcd_cmi_api.h"
#define CMI_CMD_TIMEOUT (10 * HZ)
-#define WCD_CPE_LSM_MAX_SESSIONS 1
+#define WCD_CPE_LSM_MAX_SESSIONS 2
#define WCD_CPE_AFE_MAX_PORTS 4
#define AFE_SVC_EXPLICIT_PORT_START 1
+#define WCD_CPE_EC_PP_BUF_SIZE 480 /* 5 msec buffer */
#define ELF_FLAG_EXECUTE (1 << 0)
#define ELF_FLAG_WRITE (1 << 1)
@@ -1638,7 +1639,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
core->cpe_users);
if (enable) {
- if (core->cpe_users == 0) {
+ core->cpe_users++;
+ if (core->cpe_users == 1) {
ret = wcd_cpe_enable(core, enable);
if (ret) {
dev_err(core->dev,
@@ -1646,7 +1648,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
__func__, ret);
goto done;
}
- core->cpe_users++;
} else {
dev_dbg(core->dev,
"%s: cpe already enabled, users = %u\n",
@@ -1654,7 +1655,8 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
goto done;
}
} else {
- if (core->cpe_users == 1) {
+ core->cpe_users--;
+ if (core->cpe_users == 0) {
ret = wcd_cpe_enable(core, enable);
if (ret) {
dev_err(core->dev,
@@ -1662,7 +1664,6 @@ static int wcd_cpe_vote(struct wcd_cpe_core *core,
__func__, ret);
goto done;
}
- core->cpe_users--;
} else {
dev_dbg(core->dev,
"%s: %u valid users on cpe\n",
@@ -3335,7 +3336,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle,
{
struct cmi_hdr cmd_lsm_start;
struct wcd_cpe_core *core = core_handle;
- struct cpe_lsm_ids ids;
int ret = 0;
ret = wcd_cpe_is_valid_lsm_session(core, session,
@@ -3343,30 +3343,6 @@ static int wcd_cpe_cmd_lsm_start(void *core_handle,
if (ret)
return ret;
- /* Send connect to port (input) */
- ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP;
- ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT;
- ret = wcd_cpe_send_param_connectport(core, session,
- NULL, &ids, CPE_AFE_PORT_1_TX);
- if (ret) {
- dev_err(core->dev,
- "%s: Failed to set connectPort, err=%d\n",
- __func__, ret);
- return ret;
- }
-
- /* Send connect to port (output) */
- ids.module_id = CPE_LSM_MODULE_FRAMEWORK;
- ids.param_id = CPE_LSM_PARAM_ID_CONNECT_TO_PORT;
- ret = wcd_cpe_send_param_connectport(core, session,
- NULL, &ids, session->afe_out_port_id);
- if (ret) {
- dev_err(core->dev,
- "%s: Failed to set connectPort, err=%d\n",
- __func__, ret);
- return ret;
- }
-
WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr));
@@ -3449,9 +3425,12 @@ static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session(
if (!wcd_cpe_lsm_session_active())
afe_register_service = true;
- for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++)
- if (!lsm_sessions[i])
+ for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) {
+ if (!lsm_sessions[i]) {
session_id = i;
+ break;
+ }
+ }
if (session_id < 0) {
dev_err(core->dev,
@@ -3875,6 +3854,83 @@ static void wcd_cpe_snd_model_offset(void *core_handle,
*offset = sizeof(struct cpe_param_data);
}
+static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle,
+ struct cpe_lsm_session *session,
+ struct lsm_hw_params *param)
+{
+ struct cpe_lsm_media_fmt_param media_fmt;
+ struct cmi_hdr *msg_hdr = &media_fmt.hdr;
+ struct wcd_cpe_core *core = core_handle;
+ struct cpe_param_data *param_d = &media_fmt.param;
+ struct cpe_lsm_ids ids;
+ int ret;
+
+ memset(&media_fmt, 0, sizeof(media_fmt));
+ if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+ session->id,
+ CPE_MEDIA_FMT_PLD_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = CPE_LSM_MODULE_FRAMEWORK;
+ ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT;
+
+ wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE,
+ CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+ media_fmt.minor_version = 1;
+ media_fmt.sample_rate = param->sample_rate;
+ media_fmt.num_channels = param->num_chs;
+ media_fmt.bit_width = param->bit_width;
+
+ WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+ ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt);
+ if (ret)
+ dev_err(core->dev,
+ "%s: Set_param(media_format) failed, err=%d\n",
+ __func__, ret);
+ WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+ return ret;
+}
+
+static int wcd_cpe_lsm_set_port(void *core_handle,
+ struct cpe_lsm_session *session, void *data)
+{
+ u32 port_id;
+ int ret;
+ struct cpe_lsm_ids ids;
+ struct wcd_cpe_core *core = core_handle;
+
+ ret = wcd_cpe_is_valid_lsm_session(core, session, __func__);
+ if (ret)
+ goto done;
+
+ if (!data) {
+ dev_err(core->dev, "%s: data is NULL\n", __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+ port_id = *(u32 *)data;
+ dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id);
+
+ memset(&ids, 0, sizeof(ids));
+ ids.module_id = LSM_MODULE_ID_FRAMEWORK;
+ ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+ ret = wcd_cpe_send_param_connectport(core, session, NULL,
+ &ids, port_id);
+ if (ret)
+ dev_err(core->dev,
+ "%s: send_param_connectport failed, err %d\n",
+ __func__, ret);
+done:
+ return ret;
+}
+
/*
* wcd_cpe_get_lsm_ops: register lsm driver to codec
* @lsm_ops: structure with lsm callbacks
@@ -3899,6 +3955,9 @@ int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops)
lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg;
lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param;
lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset;
+ lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params;
+ lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port;
+
return 0;
}
EXPORT_SYMBOL(wcd_cpe_get_lsm_ops);
@@ -4129,10 +4188,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle,
goto rel_cal_mutex;
}
- rc = fill_cmi_header(hdr, port_d->port_id,
- CMI_CPE_AFE_SERVICE_ID,
- 0, 20, CPE_AFE_CMD_SET_PARAM,
- true);
+ rc = fill_afe_cmd_header(hdr, port_d->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ CPE_AFE_PARAM_PAYLOAD_SIZE,
+ true);
if (rc) {
dev_err(core->dev,
"%s: invalid params for header, err = %d\n",
@@ -4163,10 +4222,10 @@ static int wcd_cpe_send_afe_cal(void *core_handle,
hdr = (struct cmi_hdr *) inb_msg;
- rc = fill_cmi_header(hdr, port_d->port_id,
- CMI_CPE_AFE_SERVICE_ID,
- 0, afe_cal->cal_data.size,
- CPE_AFE_CMD_SET_PARAM, false);
+ rc = fill_afe_cmd_header(hdr, port_d->port_id,
+ CPE_AFE_CMD_SET_PARAM,
+ CPE_AFE_PARAM_PAYLOAD_SIZE,
+ false);
if (rc) {
dev_err(core->dev,
"%s: invalid params for header, err = %d\n",
@@ -4299,8 +4358,12 @@ static int wcd_cpe_afe_cmd_port_cfg(void *core_handle,
port_cfg_cmd.bit_width = afe_cfg->bit_width;
port_cfg_cmd.num_channels = afe_cfg->num_channels;
port_cfg_cmd.sample_rate = afe_cfg->sample_rate;
- port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width,
- afe_cfg->sample_rate);
+
+ if (afe_port_d->port_id == CPE_AFE_PORT_3_TX)
+ port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE;
+ else
+ port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width,
+ afe_cfg->sample_rate);
ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd);
if (ret)
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index 0dada7f0105e..92c3fa57b600 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -459,7 +459,7 @@ static int wm8958_put_mbc_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
@@ -549,7 +549,7 @@ static int wm8958_put_vss_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
@@ -582,7 +582,7 @@ static int wm8958_put_vss_hpf_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
@@ -749,7 +749,7 @@ static int wm8958_put_enh_eq_enum(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
struct wm8994 *control = wm8994->wm8994;
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
int reg;
/* Don't allow on the fly reconfiguration */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index e5f14c878636..ba7d8e95ba78 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -360,7 +360,7 @@ static int wm8994_put_drc_enum(struct snd_kcontrol *kcontrol,
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int drc = wm8994_get_drc(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (drc < 0)
return drc;
@@ -467,7 +467,7 @@ static int wm8994_put_retune_mobile_enum(struct snd_kcontrol *kcontrol,
struct wm8994 *control = wm8994->wm8994;
struct wm8994_pdata *pdata = &control->pdata;
int block = wm8994_get_retune_mobile_block(kcontrol->id.name);
- int value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.enumerated.item[0];
if (block < 0)
return block;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 67124783558a..7f2f661c6453 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -246,7 +246,7 @@ static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
- ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+ ucontrol->value.enumerated.item[0] = adsp[e->shift_l].fw;
return 0;
}
@@ -258,16 +258,16 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
- if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+ if (ucontrol->value.enumerated.item[0] == adsp[e->shift_l].fw)
return 0;
- if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+ if (ucontrol->value.enumerated.item[0] >= WM_ADSP_NUM_FW)
return -EINVAL;
if (adsp[e->shift_l].running)
return -EBUSY;
- adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+ adsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
return 0;
}
diff --git a/sound/soc/msm/apq8096-auto.c b/sound/soc/msm/apq8096-auto.c
index 0442a546455a..5207fedce826 100644
--- a/sound/soc/msm/apq8096-auto.c
+++ b/sound/soc/msm/apq8096-auto.c
@@ -28,6 +28,7 @@
#include <sound/pcm.h>
#include <sound/jack.h>
#include <sound/q6afe-v2.h>
+#include <sound/q6adm-v2.h>
#include <sound/q6core.h>
#include <sound/pcm_params.h>
#include <sound/info.h>
@@ -43,6 +44,7 @@
#define SAMPLING_RATE_48KHZ 48000
#define SAMPLING_RATE_96KHZ 96000
#define SAMPLING_RATE_192KHZ 192000
+#define SAMPLING_RATE_384KHZ 384000
static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_auxpcm_rate = SAMPLING_RATE_8KHZ;
@@ -96,6 +98,11 @@ static int msm_quat_tdm_tx_1_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_quat_tdm_tx_2_bit_format = SNDRV_PCM_FORMAT_S16_LE;
static int msm_quat_tdm_tx_3_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+/* EC Reference default values are set in mixer_paths.xml */
+static int msm_ec_ref_ch = 4;
+static int msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+
enum {
QUATERNARY_TDM_RX_0,
QUATERNARY_TDM_RX_1,
@@ -242,6 +249,14 @@ static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE"};
static char const *mi2s_bit_format_text[] = {"S16_LE", "S24_LE"};
+static const char *const ec_ref_ch_text[] = {"Zero", "One", "Two", "Three",
+ "Four", "Five", "Six", "Seven", "Eight"};
+
+static char const *ec_ref_bit_format_text[] = {"0", "S16_LE", "S24_LE"};
+
+static const char *const ec_ref_rate_text[] = {"0", "8000", "16000",
+ "32000", "44100", "48000", "96000", "192000", "384000"};
+
static struct afe_clk_set mi2s_tx_clk = {
AFE_API_VERSION_I2S_CONFIG,
Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT,
@@ -1340,6 +1355,113 @@ static int msm_quat_tdm_tx_3_bit_format_put(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm_ec_ref_ch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_ch;
+ pr_debug("%s: msm_ec_ref_ch = %ld\n", __func__,
+ ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_ch_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ msm_ec_ref_ch = ucontrol->value.integer.value[0];
+ pr_debug("%s: msm_ec_ref_ch = %d\n", __func__, msm_ec_ref_ch);
+ adm_num_ec_ref_rx_chans(msm_ec_ref_ch);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (msm_ec_ref_bit_format) {
+ case SNDRV_PCM_FORMAT_S24_LE:
+ ucontrol->value.integer.value[0] = 2;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ucontrol->value.integer.value[0] = 1;
+ break;
+ default:
+ ucontrol->value.integer.value[0] = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_bit_format_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 2:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+ break;
+ case 1:
+ msm_ec_ref_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+ break;
+ default:
+ msm_ec_ref_bit_format = 0;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_bit_format = %d\n",
+ __func__, msm_ec_ref_bit_format);
+ adm_ec_ref_rx_bit_width(msm_ec_ref_bit_format);
+ return 0;
+}
+
+static int msm_ec_ref_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_ec_ref_sampling_rate;
+ pr_debug("%s: msm_ec_ref_sampling_rate = %ld\n",
+ __func__, ucontrol->value.integer.value[0]);
+ return 0;
+}
+
+static int msm_ec_ref_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ switch (ucontrol->value.integer.value[0]) {
+ case 0:
+ msm_ec_ref_sampling_rate = 0;
+ break;
+ case 1:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_8KHZ;
+ break;
+ case 2:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_16KHZ;
+ break;
+ case 3:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_32KHZ;
+ break;
+ case 4:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_44P1KHZ;
+ break;
+ case 5:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+ break;
+ case 6:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_96KHZ;
+ break;
+ case 7:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_192KHZ;
+ break;
+ case 8:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_384KHZ;
+ break;
+ default:
+ msm_ec_ref_sampling_rate = SAMPLING_RATE_48KHZ;
+ break;
+ }
+ pr_debug("%s: msm_ec_ref_sampling_rate = %d\n",
+ __func__, msm_ec_ref_sampling_rate);
+ adm_ec_ref_rx_sampling_rate(msm_ec_ref_sampling_rate);
+ return 0;
+}
+
static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
@@ -1880,6 +2002,9 @@ static const struct soc_enum msm_snd_enum[] = {
SOC_ENUM_SINGLE_EXT(8, tdm_ch_text),
SOC_ENUM_SINGLE_EXT(2, tdm_bit_format_text),
SOC_ENUM_SINGLE_EXT(2, mi2s_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_ch_text),
+ SOC_ENUM_SINGLE_EXT(3, ec_ref_bit_format_text),
+ SOC_ENUM_SINGLE_EXT(9, ec_ref_rate_text),
};
static const struct snd_kcontrol_new msm_snd_controls[] = {
@@ -1979,6 +2104,12 @@ static const struct snd_kcontrol_new msm_snd_controls[] = {
SOC_ENUM_EXT("TERT_MI2S_TX Bit Format", msm_snd_enum[7],
msm_tert_mi2s_tx_bit_format_get,
msm_tert_mi2s_tx_bit_format_put),
+ SOC_ENUM_EXT("EC Reference Channels", msm_snd_enum[8],
+ msm_ec_ref_ch_get, msm_ec_ref_ch_put),
+ SOC_ENUM_EXT("EC Reference Bit Format", msm_snd_enum[9],
+ msm_ec_ref_bit_format_get, msm_ec_ref_bit_format_put),
+ SOC_ENUM_EXT("EC Reference SampleRate", msm_snd_enum[10],
+ msm_ec_ref_rate_get, msm_ec_ref_rate_put),
};
static int apq8096_get_ll_qos_val(struct snd_pcm_runtime *runtime)
diff --git a/sound/soc/msm/mdm9607.c b/sound/soc/msm/mdm9607.c
index 594f4f9e5e65..1d37c606d9b4 100644
--- a/sound/soc/msm/mdm9607.c
+++ b/sound/soc/msm/mdm9607.c
@@ -28,6 +28,7 @@
#include <sound/q6core.h>
#include "../codecs/wcd9xxx-common.h"
#include "../codecs/wcd9330.h"
+#include "../codecs/wcd9306.h"
/* Spk control */
#define MDM_SPK_ON 1
@@ -76,7 +77,7 @@
/* Currently enabling only SCLK because
* MCLK is routed from PLL test pad
*/
-#define PRI_TLMM_CLKS_EN_MASTER 0x4
+#define PRI_TLMM_CLKS_EN_MASTER 0x00020004
#define SEC_TLMM_CLKS_EN_MASTER 0x2
#define PRI_TLMM_CLKS_EN_SLAVE 0x100000
#define SEC_TLMM_CLKS_EN_SLAVE 0x800000
@@ -84,13 +85,21 @@
#define CLOCK_OFF 0
/* Machine driver Name*/
-#define DRV_NAME "mdm9607-asoc-tomtom"
+#define DRV_NAME "mdm9607-asoc-snd"
enum mi2s_pcm_mux {
PRI_MI2S_PCM,
SEC_MI2S_PCM,
MI2S_PCM_MAX_INTF
};
+
+struct mdm9607_codec {
+ int (*mclk_enable_fn)(struct snd_soc_codec *codec,
+ int mclk_enable, bool dapm);
+ int (*mbhc_hs_detect)(struct snd_soc_codec *codec,
+ struct wcd9xxx_mbhc_config *mbhc_cfg);
+};
+
struct mdm_machine_data {
u32 mclk_freq;
atomic_t prim_clk_usrs;
@@ -105,6 +114,7 @@ struct mdm_machine_data {
void *lpass_mux_mic_ctl_virt_addr;
void *gcc_debug_clk_ctl_virt_addr;
void *gcc_plltest_pad_cfg_virt_addr;
+ struct mdm9607_codec mdm9607_codec_fn;
};
static const struct afe_clk_cfg lpass_default = {
@@ -806,7 +816,7 @@ static int mdm_enable_codec_ext_clk(struct snd_soc_codec *codec,
}
}
atomic_inc(&pdata->prim_clk_usrs);
- tomtom_mclk_enable(codec, 1, dapm);
+ pdata->mdm9607_codec_fn.mclk_enable_fn(codec, 1, dapm);
} else {
if (atomic_read(&pdata->prim_clk_usrs) > 0)
atomic_dec(&pdata->prim_clk_usrs);
@@ -821,7 +831,7 @@ static int mdm_enable_codec_ext_clk(struct snd_soc_codec *codec,
goto err;
}
}
- tomtom_mclk_enable(codec, 0, dapm);
+ pdata->mdm9607_codec_fn.mclk_enable_fn(codec, 0, dapm);
}
pr_debug("%s clk2 %x mode %x\n", __func__, lpass_clk->clk_val2,
lpass_clk->clk_set_mode);
@@ -1152,35 +1162,36 @@ static int mdm_mi2s_audrx_init(struct snd_soc_pcm_runtime *rtd)
mbhc_cfg.calibration = def_codec_mbhc_cal();
if (mbhc_cfg.calibration) {
- ret = tomtom_hs_detect(codec, &mbhc_cfg);
+ ret = pdata->mdm9607_codec_fn.mbhc_hs_detect(codec, &mbhc_cfg);
if (ret < 0) {
pr_err("%s: Failed to intialise mbhc %d\n",
__func__, ret);
kfree(mbhc_cfg.calibration);
}
- }
- else
+ } else {
ret = -ENOMEM;
+ }
- tomtom_register_ext_clk_cb(mdm_enable_codec_ext_clk,
- msm_snd_get_ext_clk_cnt,
- rtd->codec);
+ if (!strcmp(card->name, "mdm9607-tomtom-i2s-snd-card")) {
+ tomtom_register_ext_clk_cb(mdm_enable_codec_ext_clk,
+ msm_snd_get_ext_clk_cnt,
+ rtd->codec);
- ret = mdm_enable_codec_ext_clk(rtd->codec, 1, false);
- if (IS_ERR_VALUE(ret)) {
+ ret = mdm_enable_codec_ext_clk(rtd->codec, 1, false);
+ if (IS_ERR_VALUE(ret)) {
pr_err("%s: Failed to enable mclk, err = 0x%x\n",
__func__, ret);
goto done;
- }
+ }
- tomtom_enable_qfuse_sensing(rtd->codec);
+ tomtom_enable_qfuse_sensing(rtd->codec);
- ret = mdm_enable_codec_ext_clk(rtd->codec, 0, false);
- if (IS_ERR_VALUE(ret)) {
- pr_err("%s: Failed to disable mclk, err = 0x%x\n",
+ ret = mdm_enable_codec_ext_clk(rtd->codec, 0, false);
+ if (IS_ERR_VALUE(ret)) {
+ pr_err("%s: Failed to disable mclk, err = 0x%x\n",
__func__, ret);
+ }
}
-
done:
return ret;
}
@@ -1628,37 +1639,6 @@ static struct snd_soc_dai_link mdm_dai[] = {
},
/* Backend DAI Links */
{
- .name = LPASS_BE_PRI_MI2S_RX,
- .stream_name = "Primary MI2S Playback",
- .cpu_dai_name = "msm-dai-q6-mi2s.0",
- .platform_name = "msm-pcm-routing",
- .codec_name = "tomtom_codec",
- .codec_dai_name = "tomtom_i2s_rx1",
- .no_pcm = 1,
- .dpcm_playback = 1,
- .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
- .init = &mdm_mi2s_audrx_init,
- .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup,
- .ops = &mdm_mi2s_be_ops,
- .ignore_pmdown_time = 1,
- .ignore_suspend = 1,
- },
- {
- .name = LPASS_BE_PRI_MI2S_TX,
- .stream_name = "Primary MI2S Capture",
- .cpu_dai_name = "msm-dai-q6-mi2s.0",
- .platform_name = "msm-pcm-routing",
- .codec_name = "tomtom_codec",
- .codec_dai_name = "tomtom_i2s_tx1",
- .no_pcm = 1,
- .dpcm_capture = 1,
- .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
- .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup,
- .ops = &mdm_mi2s_be_ops,
- .ignore_pmdown_time = 1,
- .ignore_suspend = 1,
- },
- {
.name = LPASS_BE_AFE_PCM_RX,
.stream_name = "AFE Playback",
.cpu_dai_name = "msm-dai-q6-dev.224",
@@ -1814,10 +1794,102 @@ static struct snd_soc_dai_link mdm_dai[] = {
},
};
-static struct snd_soc_card snd_soc_card_mdm = {
+static struct snd_soc_dai_link mdm_9330_dai[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_PRI_MI2S_RX,
+ .stream_name = "Primary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tomtom_codec",
+ .codec_dai_name = "tomtom_i2s_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+ .init = &mdm_mi2s_audrx_init,
+ .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup,
+ .ops = &mdm_mi2s_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tomtom_codec",
+ .codec_dai_name = "tomtom_i2s_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+ .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup,
+ .ops = &mdm_mi2s_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link mdm_9306_dai[] = {
+ /* Backend DAI Links */
+ {
+ .name = LPASS_BE_PRI_MI2S_RX,
+ .stream_name = "Primary MI2S Playback",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tapan_codec",
+ .codec_dai_name = "tapan_i2s_rx1",
+ .no_pcm = 1,
+ .dpcm_playback = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+ .init = &mdm_mi2s_audrx_init,
+ .be_hw_params_fixup = &mdm_mi2s_rx_be_hw_params_fixup,
+ .ops = &mdm_mi2s_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+ {
+ .name = LPASS_BE_PRI_MI2S_TX,
+ .stream_name = "Primary MI2S Capture",
+ .cpu_dai_name = "msm-dai-q6-mi2s.0",
+ .platform_name = "msm-pcm-routing",
+ .codec_name = "tapan_codec",
+ .codec_dai_name = "tapan_i2s_tx1",
+ .no_pcm = 1,
+ .dpcm_capture = 1,
+ .be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+ .be_hw_params_fixup = &mdm_mi2s_tx_be_hw_params_fixup,
+ .ops = &mdm_mi2s_be_ops,
+ .ignore_pmdown_time = 1,
+ .ignore_suspend = 1,
+ },
+};
+
+static struct snd_soc_dai_link mdm_tomtom_dai_links[
+ ARRAY_SIZE(mdm_dai) +
+ ARRAY_SIZE(mdm_9330_dai)];
+
+static struct snd_soc_dai_link mdm_tapan_dai_links[
+ ARRAY_SIZE(mdm_dai) +
+ ARRAY_SIZE(mdm_9306_dai)];
+
+static struct snd_soc_card snd_soc_card_mdm_9330 = {
.name = "mdm9607-tomtom-i2s-snd-card",
- .dai_link = mdm_dai,
- .num_links = ARRAY_SIZE(mdm_dai),
+ .dai_link = mdm_tomtom_dai_links,
+ .num_links = ARRAY_SIZE(mdm_tomtom_dai_links),
+};
+
+static struct snd_soc_card snd_soc_card_mdm_9306 = {
+ .name = "mdm9607-tapan-i2s-snd-card",
+ .dai_link = mdm_tapan_dai_links,
+ .num_links = ARRAY_SIZE(mdm_tapan_dai_links),
+};
+
+static const struct of_device_id mdm_asoc_machine_of_match[] = {
+ { .compatible = "qcom,mdm9607-audio-tomtom",
+ .data = "tomtom_codec"},
+ { .compatible = "qcom,mdm9607-audio-tapan",
+ .data = "tapan_codec"},
+ {},
};
static int mdm_populate_dai_link_component_of_node(
@@ -1993,10 +2065,55 @@ err:
return ret;
}
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+ struct snd_soc_card *card = NULL;
+ struct snd_soc_dai_link *dailink;
+ const struct of_device_id *match;
+ int len_1, len_2;
+
+ match = of_match_node(mdm_asoc_machine_of_match, dev->of_node);
+ if (!match) {
+ dev_err(dev, "%s: No DT match found for sound card\n",
+ __func__);
+ return NULL;
+ }
+
+ if (!strcmp(match->data, "tomtom_codec")) {
+ card = &snd_soc_card_mdm_9330;
+ len_1 = ARRAY_SIZE(mdm_dai);
+ len_2 = len_1 + ARRAY_SIZE(mdm_9330_dai);
+
+ memcpy(mdm_tomtom_dai_links, mdm_dai,
+ sizeof(mdm_dai));
+ memcpy(mdm_tomtom_dai_links + len_1, mdm_9330_dai,
+ sizeof(mdm_9330_dai));
+ dailink = mdm_tomtom_dai_links;
+
+ } else if (!strcmp(match->data, "tapan_codec")) {
+ card = &snd_soc_card_mdm_9306;
+ len_1 = ARRAY_SIZE(mdm_dai);
+ len_2 = len_1 + ARRAY_SIZE(mdm_9306_dai);
+
+ memcpy(mdm_tapan_dai_links, mdm_dai,
+ sizeof(mdm_dai));
+ memcpy(mdm_tapan_dai_links + len_1, mdm_9306_dai,
+ sizeof(mdm_9306_dai));
+ dailink = mdm_tapan_dai_links;
+ }
+
+ if (card) {
+ card->dai_link = dailink;
+ card->num_links = len_2;
+ }
+
+ return card;
+}
+
static int mdm_asoc_machine_probe(struct platform_device *pdev)
{
int ret;
- struct snd_soc_card *card = &snd_soc_card_mdm;
+ struct snd_soc_card *card;
struct mdm_machine_data *pdata;
enum apr_subsys_state q6_state;
@@ -2024,12 +2141,12 @@ static int mdm_asoc_machine_probe(struct platform_device *pdev)
return -ENOMEM;
ret = of_property_read_u32(pdev->dev.of_node,
- "qcom,tomtom-mclk-clk-freq",
+ "qcom,codec-mclk-clk-freq",
&pdata->mclk_freq);
if (ret) {
dev_err(&pdev->dev,
"%s Looking up %s property in node %s failed",
- __func__, "qcom,tomtom-mclk-clk-freq",
+ __func__, "qcom,codec-mclk-clk-freq",
pdev->dev.of_node->full_name);
goto err;
@@ -2051,6 +2168,20 @@ static int mdm_asoc_machine_probe(struct platform_device *pdev)
atomic_set(&pdata->prim_clk_usrs, 0);
atomic_set(&pdata->sec_clk_usrs, 0);
+ card = populate_snd_card_dailinks(&pdev->dev);
+ if (!card) {
+ dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+ ret = -EINVAL;
+ goto err;
+ }
+ if (!strcmp(card->name, "mdm9607-tapan-i2s-snd-card")) {
+ pdata->mdm9607_codec_fn.mclk_enable_fn = tapan_mclk_enable;
+ pdata->mdm9607_codec_fn.mbhc_hs_detect = tapan_hs_detect;
+ } else if (!strcmp(card->name, "mdm9607-tomtom-i2s-snd-card")) {
+ pdata->mdm9607_codec_fn.mclk_enable_fn = tomtom_mclk_enable;
+ pdata->mdm9607_codec_fn.mbhc_hs_detect = tomtom_hs_detect;
+ }
+
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
snd_soc_card_set_drvdata(card, pdata);
@@ -2168,11 +2299,6 @@ static int mdm_asoc_machine_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id mdm_asoc_machine_of_match[] = {
- { .compatible = "qcom,mdm9607-audio-tomtom", },
- {},
-};
-
static struct platform_driver mdm_asoc_machine_driver = {
.driver = {
.name = DRV_NAME,
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
index 3b356517ca9c..8588a4bcb4be 100644
--- a/sound/soc/msm/msm-cpe-lsm.c
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -28,7 +28,11 @@
#include <sound/pcm_params.h>
#include <sound/msm-slim-dma.h>
+#define SAMPLE_RATE_48KHZ 48000
+#define SAMPLE_RATE_16KHZ 16000
#define LSM_VOICE_WAKEUP_APP_V2 2
+#define AFE_PORT_ID_1 1
+#define AFE_PORT_ID_3 3
#define AFE_OUT_PORT_2 2
#define LISTEN_MIN_NUM_PERIODS 2
#define LISTEN_MAX_NUM_PERIODS 12
@@ -136,6 +140,7 @@ struct cpe_priv {
struct wcd_cpe_lsm_ops lsm_ops;
struct wcd_cpe_afe_ops afe_ops;
bool afe_mad_ctl;
+ u32 input_port_id;
};
struct cpe_lsm_data {
@@ -1191,12 +1196,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream,
__func__, rc);
return rc;
}
- rc = lsm_ops->lsm_lab_control(cpe->core_handle,
- session, false);
- if (IS_ERR_VALUE(rc))
- dev_err(rtd->dev,
- "%s: Lab Disable Failed rc %d\n",
- __func__, rc);
/*
* Buffer has to be de-allocated even if
* lab_control failed.
@@ -1425,14 +1424,6 @@ static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream,
dev_dbg(rtd->dev,
"%s: %s\n",
__func__, "SNDRV_LSM_START");
- rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle,
- session);
- if (rc != 0) {
- dev_err(rtd->dev,
- "%s: failed to get port id, err = %d\n",
- __func__, rc);
- return rc;
- }
rc = lsm_ops->lsm_start(cpe->core_handle, session);
if (rc != 0) {
dev_err(rtd->dev,
@@ -2808,6 +2799,8 @@ static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream)
struct cpe_lsm_session *lsm_session;
struct cpe_lsm_lab *lab_d = &lsm_d->lab;
struct snd_pcm_runtime *runtime = substream->runtime;
+ struct lsm_hw_params lsm_param;
+ struct wcd_cpe_lsm_ops *lsm_ops;
if (!cpe || !cpe->core_handle) {
dev_err(rtd->dev,
@@ -2840,23 +2833,74 @@ static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream)
return 0;
}
+ lsm_ops = &cpe->lsm_ops;
afe_ops = &cpe->afe_ops;
afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
- afe_cfg->port_id = 1;
- afe_cfg->bit_width = 16;
- afe_cfg->num_channels = 1;
- afe_cfg->sample_rate = 16000;
+ switch (cpe->input_port_id) {
+ case AFE_PORT_ID_3:
+ afe_cfg->port_id = AFE_PORT_ID_3;
+ afe_cfg->bit_width = 16;
+ afe_cfg->num_channels = 1;
+ afe_cfg->sample_rate = SAMPLE_RATE_48KHZ;
+ rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg);
+ break;
+ case AFE_PORT_ID_1:
+ default:
+ afe_cfg->port_id = AFE_PORT_ID_1;
+ afe_cfg->bit_width = 16;
+ afe_cfg->num_channels = 1;
+ afe_cfg->sample_rate = SAMPLE_RATE_16KHZ;
+ rc = afe_ops->afe_set_params(cpe->core_handle,
+ afe_cfg, cpe->afe_mad_ctl);
+ break;
+ }
- rc = afe_ops->afe_set_params(cpe->core_handle,
- afe_cfg, cpe->afe_mad_ctl);
if (rc != 0) {
dev_err(rtd->dev,
- "%s: cpe afe params failed, err = %d\n",
- __func__, rc);
+ "%s: cpe afe params failed for port = %d, err = %d\n",
+ __func__, afe_cfg->port_id, rc);
+ return rc;
+ }
+ lsm_param.sample_rate = afe_cfg->sample_rate;
+ lsm_param.num_chs = afe_cfg->num_channels;
+ lsm_param.bit_width = afe_cfg->bit_width;
+ rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session,
+ &lsm_param);
+ if (rc)
+ dev_dbg(rtd->dev,
+ "%s: failed to set lsm media fmt params, err = %d\n",
+ __func__, rc);
+
+ /* Send connect to port (input) */
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+ &cpe->input_port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set connect input port, err=%d\n",
+ __func__, rc);
return rc;
}
+ if (cpe->input_port_id != 3) {
+ rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle,
+ lsm_session);
+ if (rc != 0) {
+ dev_err(rtd->dev,
+ "%s: failed to get port id, err = %d\n",
+ __func__, rc);
+ return rc;
+ }
+ /* Send connect to port (output) */
+ rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+ &lsm_session->afe_out_port_id);
+ if (rc) {
+ dev_err(rtd->dev,
+ "%s: Failed to set connect output port, err=%d\n",
+ __func__, rc);
+ return rc;
+ }
+ }
rc = msm_cpe_afe_port_cntl(substream,
cpe->core_handle,
afe_ops, afe_cfg,
@@ -3106,6 +3150,9 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
struct cpe_priv *cpe_priv;
const struct snd_kcontrol_new *kcontrol;
bool found_runtime = false;
+ const char *cpe_dev_id = "qcom,msm-cpe-lsm-id";
+ u32 port_id = 0;
+ int ret = 0;
int i;
if (!platform || !platform->component.card) {
@@ -3135,6 +3182,14 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
return -EINVAL;
}
+ ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id,
+ &port_id);
+ if (ret) {
+ dev_dbg(platform->dev,
+ "%s: missing 0x%x in dt node\n", __func__, port_id);
+ port_id = 1;
+ }
+
codec = rtd->codec;
cpe_priv = kzalloc(sizeof(struct cpe_priv),
@@ -3147,6 +3202,7 @@ static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
}
cpe_priv->codec = codec;
+ cpe_priv->input_port_id = port_id;
wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops);
wcd_cpe_get_afe_ops(&cpe_priv->afe_ops);
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
index 570b9eda49f9..ac83649b48d4 100644
--- a/sound/soc/msm/msm8996.c
+++ b/sound/soc/msm/msm8996.c
@@ -732,6 +732,35 @@ static int msm8996_mclk_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+ int enable, bool dapm)
+{
+ int ret = 0;
+
+ if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+ ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+ else {
+ dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+ __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int msm8996_mclk_tx_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ pr_debug("%s: event = %d\n", __func__, event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return msm_snd_enable_codec_ext_tx_clk(w->codec, 1, true);
+ case SND_SOC_DAPM_POST_PMD:
+ return msm_snd_enable_codec_ext_tx_clk(w->codec, 0, true);
+ }
+ return 0;
+}
+
static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
@@ -809,6 +838,9 @@ static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("MCLK", SND_SOC_NOPM, 0, 0,
msm8996_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY("MCLK TX", SND_SOC_NOPM, 0, 0,
+ msm8996_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
/*HTC_AUD_START*/
SND_SOC_DAPM_SPK("Lineout_1 amp", msm_ext_rcv_event),
/*HTC_AUD_END*/
@@ -836,10 +868,10 @@ static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = {
};
static struct snd_soc_dapm_route wcd9335_audio_paths[] = {
- {"MIC BIAS1", NULL, "MCLK"},
- {"MIC BIAS2", NULL, "MCLK"},
- {"MIC BIAS3", NULL, "MCLK"},
- {"MIC BIAS4", NULL, "MCLK"},
+ {"MIC BIAS1", NULL, "MCLK TX"},
+ {"MIC BIAS2", NULL, "MCLK TX"},
+ {"MIC BIAS3", NULL, "MCLK TX"},
+ {"MIC BIAS4", NULL, "MCLK TX"},
};
static int slim5_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
@@ -4789,6 +4821,20 @@ static struct snd_soc_dai_link msm8996_tasha_fe_dai_links[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
},
+ /* CPE LSM EC PP direct dai-link */
+ {
+ .name = "CPE Listen service ECPP",
+ .stream_name = "CPE Listen Audio Service ECPP",
+ .cpu_dai_name = "CPE_LSM_NOHOST",
+ .platform_name = "msm-cpe-lsm.3",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+ .ignore_suspend = 1,
+ .ignore_pmdown_time = 1,
+ .codec_dai_name = "tasha_cpe",
+ .codec_name = "tasha_codec",
+ },
};
static struct snd_soc_dai_link msm8996_common_be_dai_links[] = {
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
index 06a7b2ab88d5..100f80cb842c 100644
--- a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -59,7 +59,7 @@ struct msm_audio_in_frame_info {
#define PLAYBACK_MIN_NUM_PERIODS 2
#define PLAYBACK_MAX_NUM_PERIODS 8
-#define PLAYBACK_MAX_PERIOD_SIZE 12288
+#define PLAYBACK_MAX_PERIOD_SIZE 61440
#define PLAYBACK_MIN_PERIOD_SIZE 128
#define CAPTURE_MIN_NUM_PERIODS 2
#define CAPTURE_MAX_NUM_PERIODS 8
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
index 0f293b82c07a..77433384a139 100755
--- a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -4910,6 +4910,12 @@ static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
msm_routing_put_voice_mixer),
};
+static const struct snd_kcontrol_new quat_tdm_rx_2_voice_mixer_controls[] = {
+ SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+ MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+ msm_routing_put_voice_mixer),
+};
+
static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_EXTPROC_RX,
MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
@@ -5097,6 +5103,9 @@ static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = {
SOC_SINGLE_EXT("TERT_MI2S_TX_MMode1",
MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1,
1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+ SOC_SINGLE_EXT("QUAT_TDM_TX_0_MMode1",
+ MSM_BACKEND_DAI_QUAT_TDM_TX_0, MSM_FRONTEND_DAI_VOICEMMODE1,
+ 1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
};
static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = {
@@ -7754,6 +7763,10 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_NOPM, 0, 0,
quin_mi2s_rx_voice_mixer_controls,
ARRAY_SIZE(quin_mi2s_rx_voice_mixer_controls)),
+ SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2_Voice Mixer",
+ SND_SOC_NOPM, 0, 0,
+ quat_tdm_rx_2_voice_mixer_controls,
+ ARRAY_SIZE(quat_tdm_rx_2_voice_mixer_controls)),
SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
ARRAY_SIZE(tx_voice_mixer_controls)),
@@ -8746,6 +8759,9 @@ static const struct snd_soc_dapm_route intercon[] = {
{"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
{"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"},
+ {"QUAT_TDM_RX_2_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+ {"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_Voice Mixer"},
+
{"VOC_EXT_EC MUX", "PRI_MI2S_TX" , "PRI_MI2S_TX"},
{"VOC_EXT_EC MUX", "SEC_MI2S_TX" , "SEC_MI2S_TX"},
{"VOC_EXT_EC MUX", "TERT_MI2S_TX" , "TERT_MI2S_TX"},
@@ -8862,6 +8878,7 @@ static const struct snd_soc_dapm_route intercon[] = {
{"VoiceMMode1_Tx Mixer", "AFE_PCM_TX_MMode1", "PCM_TX"},
{"VoiceMMode1_Tx Mixer", "AUX_PCM_TX_MMode1", "AUX_PCM_TX"},
{"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"},
+ {"VoiceMMode1_Tx Mixer", "QUAT_TDM_TX_0_MMode1", "QUAT_TDM_TX_0"},
{"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"},
{"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"},
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
index c9488e474960..5887c1f72ade 100644
--- a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
@@ -81,6 +81,10 @@ static int msm_route_hfp_vol_control;
static const DECLARE_TLV_DB_LINEAR(hfp_rx_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
+static int msm_route_icc_vol_control;
+static const DECLARE_TLV_DB_LINEAR(icc_rx_vol_gain, 0,
+ INT_RX_VOL_MAX_STEPS);
+
static int msm_route_pri_auxpcm_lb_vol_ctrl;
static const DECLARE_TLV_DB_LINEAR(pri_auxpcm_lb_vol_gain, 0,
INT_RX_VOL_MAX_STEPS);
@@ -466,6 +470,23 @@ static int msm_qti_pp_set_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
return 0;
}
+static int msm_qti_pp_get_icc_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ ucontrol->value.integer.value[0] = msm_route_icc_vol_control;
+ return 0;
+}
+
+static int msm_qti_pp_set_icc_vol_mixer(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ adm_set_mic_gain(AFE_PORT_ID_QUATERNARY_TDM_TX,
+ adm_get_default_copp_idx(AFE_PORT_ID_QUATERNARY_TDM_TX),
+ ucontrol->value.integer.value[0]);
+ msm_route_icc_vol_control = ucontrol->value.integer.value[0];
+ return 0;
+}
+
static int msm_qti_pp_get_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -774,6 +795,12 @@ static const struct snd_kcontrol_new int_hfp_vol_mixer_controls[] = {
msm_qti_pp_set_hfp_vol_mixer, hfp_rx_vol_gain),
};
+static const struct snd_kcontrol_new int_icc_vol_mixer_controls[] = {
+ SOC_SINGLE_EXT_TLV("Internal ICC Volume", SND_SOC_NOPM, 0,
+ INT_RX_VOL_GAIN, 0, msm_qti_pp_get_icc_vol_mixer,
+ msm_qti_pp_set_icc_vol_mixer, icc_rx_vol_gain),
+};
+
static const struct snd_kcontrol_new pri_auxpcm_lb_vol_mixer_controls[] = {
SOC_SINGLE_EXT_TLV("PRI AUXPCM LOOPBACK Volume",
AFE_PORT_ID_PRIMARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0,
@@ -962,6 +989,9 @@ void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
snd_soc_add_platform_controls(platform, int_hfp_vol_mixer_controls,
ARRAY_SIZE(int_hfp_vol_mixer_controls));
+ snd_soc_add_platform_controls(platform, int_icc_vol_mixer_controls,
+ ARRAY_SIZE(int_icc_vol_mixer_controls));
+
snd_soc_add_platform_controls(platform,
pri_auxpcm_lb_vol_mixer_controls,
ARRAY_SIZE(pri_auxpcm_lb_vol_mixer_controls));
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
index 0109e692a22f..b9be401d4f9a 100644
--- a/sound/soc/msm/qdsp6v2/q6adm.c
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -100,6 +100,9 @@ struct adm_ctl {
int set_custom_topology;
int ec_ref_rx;
+ int num_ec_ref_rx_chans;
+ int ec_ref_rx_bit_width;
+ int ec_ref_rx_sampling_rate;
};
static struct adm_ctl this_adm;
@@ -1429,6 +1432,7 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv)
*/
case ADM_CMD_DEVICE_OPEN_V5:
case ADM_CMD_DEVICE_CLOSE_V5:
+ case ADM_CMD_DEVICE_OPEN_V6:
pr_debug("%s: Basic callback received, wake up.\n",
__func__);
atomic_set(&this_adm.copp.stat[port_idx]
@@ -1524,7 +1528,8 @@ static int32_t adm_callback(struct apr_client_data *data, void *priv)
}
switch (data->opcode) {
- case ADM_CMDRSP_DEVICE_OPEN_V5: {
+ case ADM_CMDRSP_DEVICE_OPEN_V5:
+ case ADM_CMDRSP_DEVICE_OPEN_V6: {
struct adm_cmd_rsp_device_open_v5 *open =
(struct adm_cmd_rsp_device_open_v5 *)data->payload;
@@ -2336,10 +2341,64 @@ inval_ch_mod:
return rc;
}
+int adm_arrange_mch_ep2_map(struct adm_cmd_device_open_v6 *open_v6,
+ int channel_mode)
+{
+ int rc = 0;
+
+ memset(open_v6->dev_channel_mapping_eid2, 0,
+ PCM_FORMAT_MAX_NUM_CHANNEL);
+
+ if (channel_mode == 1) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 2) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ } else if (channel_mode == 3) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC;
+ } else if (channel_mode == 4) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 5) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_FC;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 6) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC;
+ open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS;
+ } else if (channel_mode == 8) {
+ open_v6->dev_channel_mapping_eid2[0] = PCM_CHANNEL_FL;
+ open_v6->dev_channel_mapping_eid2[1] = PCM_CHANNEL_FR;
+ open_v6->dev_channel_mapping_eid2[2] = PCM_CHANNEL_LFE;
+ open_v6->dev_channel_mapping_eid2[3] = PCM_CHANNEL_FC;
+ open_v6->dev_channel_mapping_eid2[4] = PCM_CHANNEL_LS;
+ open_v6->dev_channel_mapping_eid2[5] = PCM_CHANNEL_RS;
+ open_v6->dev_channel_mapping_eid2[6] = PCM_CHANNEL_LB;
+ open_v6->dev_channel_mapping_eid2[7] = PCM_CHANNEL_RB;
+ } else {
+ pr_err("%s: invalid num_chan %d\n", __func__,
+ channel_mode);
+ rc = -EINVAL;
+ }
+
+ return rc;
+}
+
int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
int perf_mode, uint16_t bit_width, int app_type, int acdb_id)
{
struct adm_cmd_device_open_v5 open;
+ struct adm_cmd_device_open_v6 open_v6;
int ret = 0;
int port_idx, copp_idx, flags;
int tmp_port = q6audio_get_port_id(port_id);
@@ -2492,10 +2551,9 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
open.flags = flags;
open.mode_of_operation = path;
open.endpoint_id_1 = tmp_port;
+ open.endpoint_id_2 = 0xFFFF;
- if (this_adm.ec_ref_rx == -1) {
- open.endpoint_id_2 = 0xFFFF;
- } else if (this_adm.ec_ref_rx && (path != 1)) {
+ if (this_adm.ec_ref_rx && (path != 1)) {
open.endpoint_id_2 = this_adm.ec_ref_rx;
this_adm.ec_ref_rx = -1;
}
@@ -2519,7 +2577,47 @@ int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
- ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ if ((this_adm.num_ec_ref_rx_chans != 0) && (path != 1) &&
+ (open.endpoint_id_2 != 0xFFFF)) {
+ memcpy(&open_v6, &open,
+ sizeof(struct adm_cmd_device_open_v5));
+ open_v6.hdr.opcode = ADM_CMD_DEVICE_OPEN_V6;
+ open_v6.hdr.pkt_size = sizeof(open_v6);
+ open_v6.dev_num_channel_eid2 =
+ this_adm.num_ec_ref_rx_chans;
+ this_adm.num_ec_ref_rx_chans = 0;
+
+ if (this_adm.ec_ref_rx_bit_width != 0) {
+ open_v6.bit_width_eid2 =
+ this_adm.ec_ref_rx_bit_width;
+ this_adm.ec_ref_rx_bit_width = 0;
+ } else {
+ open_v6.bit_width_eid2 = bit_width;
+ }
+
+ if (this_adm.ec_ref_rx_sampling_rate != 0) {
+ open_v6.sample_rate_eid2 =
+ this_adm.ec_ref_rx_sampling_rate;
+ this_adm.ec_ref_rx_sampling_rate = 0;
+ } else {
+ open_v6.sample_rate_eid2 = rate;
+ }
+
+ pr_debug("%s: eid2_channels=%d eid2_bit_width=%d eid2_rate=%d\n",
+ __func__, open_v6.dev_num_channel_eid2,
+ open_v6.bit_width_eid2,
+ open_v6.sample_rate_eid2);
+
+ ret = adm_arrange_mch_ep2_map(&open_v6,
+ open_v6.dev_num_channel_eid2);
+
+ if (ret)
+ return ret;
+
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open_v6);
+ } else {
+ ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+ }
if (ret < 0) {
pr_err("%s: port_id: 0x%x for[0x%x] failed %d\n",
__func__, tmp_port, port_id, ret);
@@ -2812,7 +2910,28 @@ fail_cmd:
void adm_ec_ref_rx_id(int port_id)
{
this_adm.ec_ref_rx = port_id;
- pr_debug("%s: ec_ref_rx:%d", __func__, this_adm.ec_ref_rx);
+ pr_debug("%s: ec_ref_rx:%d\n", __func__, this_adm.ec_ref_rx);
+}
+
+void adm_num_ec_ref_rx_chans(int num_chans)
+{
+ this_adm.num_ec_ref_rx_chans = num_chans;
+ pr_debug("%s: num_ec_ref_rx_chans:%d\n",
+ __func__, this_adm.num_ec_ref_rx_chans);
+}
+
+void adm_ec_ref_rx_bit_width(int bit_width)
+{
+ this_adm.ec_ref_rx_bit_width = bit_width;
+ pr_debug("%s: ec_ref_rx_bit_width:%d\n",
+ __func__, this_adm.ec_ref_rx_bit_width);
+}
+
+void adm_ec_ref_rx_sampling_rate(int sampling_rate)
+{
+ this_adm.ec_ref_rx_sampling_rate = sampling_rate;
+ pr_debug("%s: ec_ref_rx_sampling_rate:%d\n",
+ __func__, this_adm.ec_ref_rx_sampling_rate);
}
int adm_close(int port_id, int perf_mode, int copp_idx)
@@ -3559,6 +3678,84 @@ fail_cmd:
return rc;
}
+int adm_set_mic_gain(int port_id, int copp_idx, int volume)
+{
+ struct adm_set_mic_gain_params mic_gain_params;
+ int rc = 0;
+ int sz, port_idx;
+
+ pr_debug("%s:\n", __func__);
+ port_id = afe_convert_virtual_to_portid(port_id);
+ port_idx = adm_validate_and_get_port_index(port_id);
+ if (port_idx < 0) {
+ pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+ return -EINVAL;
+ }
+
+ sz = sizeof(struct adm_set_mic_gain_params);
+
+ mic_gain_params.params.hdr.hdr_field =
+ APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+ APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+ mic_gain_params.params.hdr.pkt_size = sz;
+ mic_gain_params.params.hdr.src_svc = APR_SVC_ADM;
+ mic_gain_params.params.hdr.src_domain = APR_DOMAIN_APPS;
+ mic_gain_params.params.hdr.src_port = port_id;
+ mic_gain_params.params.hdr.dest_svc = APR_SVC_ADM;
+ mic_gain_params.params.hdr.dest_domain = APR_DOMAIN_ADSP;
+ mic_gain_params.params.hdr.dest_port =
+ atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+ mic_gain_params.params.hdr.token = port_idx << 16 | copp_idx;
+ mic_gain_params.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+ mic_gain_params.params.payload_addr_lsw = 0;
+ mic_gain_params.params.payload_addr_msw = 0;
+ mic_gain_params.params.mem_map_handle = 0;
+ mic_gain_params.params.payload_size =
+ sizeof(struct adm_param_data_v5) +
+ sizeof(struct admx_mic_gain);
+ mic_gain_params.data.module_id = ADM_MODULE_IDX_MIC_GAIN_CTRL;
+ mic_gain_params.data.param_id = ADM_PARAM_IDX_MIC_GAIN;
+ mic_gain_params.data.param_size =
+ sizeof(struct admx_mic_gain);
+ mic_gain_params.data.reserved = 0;
+ mic_gain_params.mic_gain_data.tx_mic_gain = volume;
+ mic_gain_params.mic_gain_data.reserved = 0;
+ pr_debug("%s: Mic Gain set to %d at port_id 0x%x\n",
+ __func__, volume, port_id);
+
+ atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+ rc = apr_send_pkt(this_adm.apr, (uint32_t *)&mic_gain_params);
+ if (rc < 0) {
+ pr_err("%s: Set params failed port = %#x\n",
+ __func__, port_id);
+ rc = -EINVAL;
+ goto fail_cmd;
+ }
+ /* Wait for the callback */
+ rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+ atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+ msecs_to_jiffies(TIMEOUT_MS));
+ if (!rc) {
+ pr_err("%s: Mic Gain Set params timed out port = %#x\n",
+ __func__, port_id);
+ rc = -EINVAL;
+ goto fail_cmd;
+ } else if (atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]) > 0) {
+ pr_err("%s: DSP returned error[%s]\n",
+ __func__, adsp_err_get_err_str(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx])));
+ rc = adsp_err_get_lnx_err_code(
+ atomic_read(&this_adm.copp.stat
+ [port_idx][copp_idx]));
+ goto fail_cmd;
+ }
+ rc = 0;
+fail_cmd:
+ return rc;
+}
+
int adm_param_enable(int port_id, int copp_idx, int module_id, int enable)
{
struct audproc_enable_param_t adm_mod_enable;
@@ -4433,6 +4630,9 @@ static int __init adm_init(void)
int i = 0, j;
this_adm.apr = NULL;
this_adm.ec_ref_rx = -1;
+ this_adm.num_ec_ref_rx_chans = 0;
+ this_adm.ec_ref_rx_bit_width = 0;
+ this_adm.ec_ref_rx_sampling_rate = 0;
atomic_set(&this_adm.matrix_map_stat, 0);
init_waitqueue_head(&this_adm.matrix_map_wait);
atomic_set(&this_adm.adm_stat, 0);
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index aa108e5a6adb..3db9c92fd7ac 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1775,7 +1775,8 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
- (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_SUSPEND))
continue;
dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 5bfb695547f8..92ae88bdff7e 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -2406,7 +2406,6 @@ int snd_usbmidi_create(struct snd_card *card,
else
err = snd_usbmidi_create_endpoints(umidi, endpoints);
if (err < 0) {
- snd_usbmidi_free(umidi);
return err;
}
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 8631910cbeb8..8d541a19e8b4 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -811,12 +811,12 @@ static struct usb_feature_control_info audio_feature_info[] = {
{ "Tone Control - Treble", USB_MIXER_S8 },
{ "Graphic Equalizer", USB_MIXER_S8 }, /* FIXME: not implemeted yet */
{ "Auto Gain Control", USB_MIXER_BOOLEAN },
- { "Delay Control", USB_MIXER_U16 },
+ { "Delay Control", USB_MIXER_U16 }, /* FIXME: U32 in UAC2 */
{ "Bass Boost", USB_MIXER_BOOLEAN },
{ "Loudness", USB_MIXER_BOOLEAN },
/* UAC2 specific */
- { "Input Gain Control", USB_MIXER_U16 },
- { "Input Gain Pad Control", USB_MIXER_BOOLEAN },
+ { "Input Gain Control", USB_MIXER_S16 },
+ { "Input Gain Pad Control", USB_MIXER_S16 },
{ "Phase Inverter Control", USB_MIXER_BOOLEAN },
};
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 2c10c9ee36a2..7da345b0cdaf 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -168,6 +168,12 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
}
alts = &iface->altsetting[fp->altset_idx];
altsd = get_iface_desc(alts);
+ if (altsd->bNumEndpoints < 1) {
+ kfree(fp);
+ kfree(rate_table);
+ return -EINVAL;
+ }
+
fp->protocol = altsd->bInterfaceProtocol;
if (fp->datainterval == 0)
@@ -1108,8 +1114,11 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
switch (chip->usb_id) {
case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */
case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */
+ case USB_ID(0x045E, 0x076E): /* MS Lifecam HD-5001 */
+ case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
+ case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
return true;
}
@@ -1190,8 +1199,12 @@ void snd_usb_set_interface_quirk(struct usb_device *dev)
* "Playback Design" products need a 50ms delay after setting the
* USB interface.
*/
- if (le16_to_cpu(dev->descriptor.idVendor) == 0x23ba)
+ switch (le16_to_cpu(dev->descriptor.idVendor)) {
+ case 0x23ba: /* Playback Design */
+ case 0x0644: /* TEAC Corp. */
mdelay(50);
+ break;
+ }
}
void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
@@ -1206,6 +1219,14 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
(requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
mdelay(20);
+ /*
+ * "TEAC Corp." products need a 20ms delay after each
+ * class compliant request
+ */
+ if ((le16_to_cpu(dev->descriptor.idVendor) == 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
*/
@@ -1261,6 +1282,8 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */
case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */
+ case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */
+ case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
if (fp->altsetting == 3)
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
break;